Error codes
Listings API HTTP status codes, the SYxxxxx error codes for Listings and Social, and the three error response shapes the v4 API returns.
The Listings API uses conventional HTTP status codes to report the outcome of a
request. Error codes are SY-prefixed and travel inside the error message
string, formatted as SYxxxxx: <human-readable reason>.
HTTP status codes
HTTP status code summary
Three response shapes
Most failures carry an errors array, either at the top level or nested
inside the operation's result, as the sections below show. A few failures
return a single error object instead: rate limiting (429) and some
request-level failures (400/500 with a code such as
INTERNAL_SERVER_ERROR). Test for both: errors is an array, error is an
object.
1. Request-level: top-level errors[]
Authentication, authorization, and malformed-request failures short-circuit
before the operation runs. They return the matching 4xx status (above) with
the error at the top level of the response.
{ "data": { "createLocation": null }, "errors": [{ "message": "SY90005: Invalid Token" }]}2. Write validation: nested data.<operation>.errors[]
A create, update, or publish request that is otherwise well-formed returns
HTTP 200. If the input breaks a business rule, the failure is reported
nested under the
operation's own payload in data.<operation>.errors. On success this array is
present and empty ("errors": []).
{ "data": { "createLocation": { "errors": [ { "message": "SY10126: City is Mandatory" }, { "message": "SY10010: state_iso" } ], "location": null } }}3. Gateway-level: a single top-level error object
Rate limiting and unexpected gateway faults short-circuit before translation
and return one error object with a code, a message, and a
correlation_id. See rate_limited below for the 429 body.
Always read data.<operation>.errors on a write, even on a 200 with a
populated data.<operation> object: a write can partially fail and report
it here.
A 402 Payment Required response has no error wrapper: code, message,
doc_url and, when there is an invoice to pay, pay_url are at the top
level. See payment_required below.
Retrying a write is not free
The v4 API has no Idempotency-Key support: the header is ignored, and no
endpoint detects repeated writes. A create, update, or
publish call that times out or returns a 5xx may still have committed on the
server, so a blind retry can produce a duplicate location, post, or review
reply.
Retry reads freely with backoff. For a write, read back first: look the record
up by a key you control (storeId, a store code, the interaction's response
list) and only repeat the call if it is genuinely absent.
Common codes
Common SY error codes
Validation codes in the SY10xxx range correspond to a specific input field;
the message names the field or the rule that failed. Auth and authorization
codes in the SY90xxx range are request-level and arrive at the top level with
the matching status.
Connected-account codes
Codes in the SY810xx range come from the Connected Accounts endpoints that
import or link a publisher listing. They arrive nested in the response body
on an HTTP 200 (response shape 2 above), so read
data.<operation>.errors even when the status is a success.
SY810xx connected-account and import codes
Social codes
Codes in the SY950xx range come from the Social
endpoints: brands, connections and social posts. Like the connected-account
codes, they arrive nested in the response body on an HTTP 200, so read
data.<operation>.error (and data.<operation>.errors on the bulk routes) even
when the status is a success.
SY950xx Social codes
SY95042 and SY95044 are entitlement failures, not input errors: retrying
without changing the plan will fail identically. SY95045, SY95046,
SY95049 and SY95052 are input errors, fixable in the next request.
Listings entitlement codes
Listings plan codes
rate_limited
429 Too Many Requests: the account exceeded its plan's rate limit. The body
is the standard error response carrying a retry_after_seconds hint, a
correlation_id to quote in support requests, and a doc_url that links back
to this section:
{ "error": { "code": "RATE_LIMITED", "message": "Too many requests. Slow down.", "retry_after_seconds": 30, "correlation_id": "9f2c1e64-2b1a-4b0e-9f1a-2c7d5e8a1b3c", "doc_url": "https://listingsapi.com/docs/error-codes#rate_limited" }}Wait for retry_after_seconds (or the Retry-After header when present), then
retry with exponential backoff. See Rate limits for the
per-plan limits and a reference backoff implementation.
payment_required
402: The last payment for this account did not go through, so every
/api/v4 endpoint is paused. The body includes pay_url, the Stripe page
for the unpaid invoice, when one is available, and the X-Error-Code
response header carries the same code. You can still open the Billing page
in the dashboard to pay.
{ "code": "payment_required", "message": "This account has an unpaid invoice, so API access is paused. Pay the invoice to restore access.", "pay_url": "https://invoice.stripe.com/i/acct_123/test_abc", "doc_url": "https://listingsapi.com/docs/error-codes#payment_required"}Access comes back within a minute after the last open invoice is paid. If the invoice is still unpaid 3 days after the first failed payment, the Listings subscription is cancelled and the account's locations are archived. If the account also has an active Social subscription, it keeps Social access; otherwise the account is closed and its API keys stop working.
social_payment_required
402: The last payment for the Social subscription did not go through, so the
Social endpoints (/api/v4/social/*) are paused. Listings endpoints keep
working. The body has the same fields as payment_required, with
"code": "social_payment_required". If the invoice is still unpaid 3 days
after the first failed payment, the Social subscription is cancelled and the
account's brands are archived. An account with no Listings subscription is
closed, and its API keys stop working.
Dev-portal control-plane errors
Account and key-management endpoints in the developer portal (the dashboard's
own API routes) return a single, structured error response: distinct from the
REST errors[] shapes above. Each code carries a correlation_id and a
doc_url that links back to the matching section here.
{ "error": { "code": "AUTH_INVALID_KEY", "message": "Session is invalid or expired.", "correlation_id": "9f2c1e64-2b1a-4b0e-9f1a-2c7d5e8a1b3c", "doc_url": "https://listingsapi.com/docs/error-codes#auth_invalid_key" }}auth_missing_key
401: No credential was provided. Sign in again, or send your key as
Authorization: API <your-key>.
auth_invalid_key
401: The session or key is invalid or expired. Re-authenticate or re-issue a
key from the dashboard.
auth_insufficient_scope
403: The credential is valid but not permitted for this action. A read-scoped
key cannot call write endpoints; issue a write key if you need one.
not_found
404: The requested resource, or a resource addressed by ID, does not exist.
conflict
409: The request conflicts with the current state of the resource (for
example, revoking a key that is already revoked).
validation_failed
422: The request body failed validation. The response's field and
details name the offending input.
internal_error
500: An unexpected server-side error. Retry a read with exponential backoff;
for a write, read the record back first (see
Retrying a write is not free). Quote the
correlation_id if you contact support.
dependency_unavailable
503: A service the API depends on is temporarily unavailable. Retry with
backoff.
For 429, see rate_limited above.
Handling errors in code
Check the HTTP status first; then, for writes, scan the response body for a
nested errors array:
from listingsapi import APIError, ListingsAPI client = ListingsAPI(api_key=os.environ["LISTINGSAPI_KEY"]) try: location = client.locations.create(input=payload)except APIError as e: # Raised for a non-2xx status (AuthenticationError, ValidationError, and # so on) and for an error payload returned inside a 200 body. raise RuntimeError(f"Listings API error: {e}") from eAgainst the raw HTTP API the same two checks are explicit, the status code and then the response payload:
resp = requests.post( "https://listingsapi.com/api/v4/locations", headers={"Authorization": f"API {os.environ['LISTINGSAPI_KEY']}"}, json=payload, timeout=30,)resp.raise_for_status()body = resp.json() op = (body.get("data") or {}).get("createLocation") or {}if op.get("errors"): messages = [e["message"] for e in op["errors"]] raise RuntimeError(f"Listings API error: {messages}")The Python SDK does both for you: it maps each
HTTP status to a typed exception and also raises on error payloads returned
inside a 200 body.