Error handling

Exception hierarchy, error attributes, HTTP 200 payload errors, client-side validation, and retry guidance for the Listings API Python SDK.

All exceptions are importable from the top-level listingsapi module.

Exception hierarchy

Code
ListingsAPIError├── APIError                    API-reported failure: non-2xx, or an error payload in HTTP 200│   ├── AuthenticationError     401, or payload codes SY90005 / SY90001│   ├── PermissionDeniedError   403, or payload code SY90003│   ├── NotFoundError           404│   ├── ValidationError         400 / 422, mutation failures, client-side validation│   ├── RateLimitError          429, or payload code RATE_LIMITED (carries .retry_after)│   └── InternalServerError     5xx (safe to retry)└── APIConnectionError          network failure: DNS, connection refused, timeout

Catch ListingsAPIError to handle everything the SDK can raise, APIError for anything the API reported, or a specific subclass for targeted handling.

Exception attributes

APIError and its subclasses carry:

AttributeTypeDescription
status_codeint | NoneHTTP status code (can be 200 for payload-level errors)
codestr | NoneFirst platform error code, e.g. SY10005
errorslist[dict]Normalized error entries, each {"code", "message", "context"}
response_bodystr | NoneRaw response text from the API
retry_afterfloat | NoneRateLimitError only; parsed from the Retry-After header

APIConnectionError inherits directly from ListingsAPIError and has none of these attributes; the exception message describes the network failure.

Catching exceptions

Python
import listingsapi client = listingsapi.ListingsAPI() try:  location = client.locations.retrieve(16808)except listingsapi.AuthenticationError as e:  print("Invalid API key:", e.code)except listingsapi.PermissionDeniedError as e:  print("Not allowed:", e.status_code)except listingsapi.NotFoundError as e:  print("No such location:", e.status_code)except listingsapi.ValidationError as e:  for err in e.errors:      print(err["code"], err["message"], err["context"])except listingsapi.RateLimitError as e:  print("Rate limited, retry in", e.retry_after, "seconds")except listingsapi.InternalServerError as e:  print("Server error (safe to retry):", e.status_code)except listingsapi.APIConnectionError as e:  print("Network failure:", e)except listingsapi.APIError as e:  print("Unexpected API error:", e.status_code, e.code, e.response_body)

Errors inside HTTP 200 responses

The platform sometimes reports failures inside a 200 response body. The SDK inspects every response and raises, so you never check success flags or scan errors arrays yourself:

  • Top-level errors[] bodies are mapped by code: SY90005 and SY90001 raise AuthenticationError, SY90003 raises PermissionDeniedError, RATE_LIMITED raises RateLimitError, and any other code raises APIError.
  • Mutation envelopes (create, update, archive, post publishing) that come back with success: false or a per-mutation errors[] list raise ValidationError.

In both cases status_code is the actual HTTP status (often 200), code is the first platform code, and errors holds every normalized entry:

Python
import listingsapi client = listingsapi.ListingsAPI() try:  result = client.locations.update({"id": 16808, "phone": "not-a-phone"})except listingsapi.ValidationError as e:  print(e.status_code)   # 200: the API accepted the request but rejected the mutation  print(e.code)          # first platform code, e.g. "SY10005"  for err in e.errors:      print(err["code"], err["message"], err["context"])

Client-side validation

locations.add() and the post helpers (posts.create_announcement(), posts.create_event(), posts.create_offer(), posts.bulk_publish()) validate their payload locally and raise ValidationError before any network call, so a bad payload fails fast with a clear message instead of a round trip.

For locations.add():

  • name must be 2 to 150 characters
  • description must be at least 200 characters
  • sub_category_id is required
  • country_iso is required
  • city is required unless hide_address=True (service-area businesses)

For the post helpers:

  • name is required and location_ids must not be empty
  • sites must be a non-empty subset of GOOGLE, FACEBOOK
  • message is required; when it is a per-site dict, every target site needs text
  • cta_type and cta_url must be provided together, and cta_type must be one of BOOK, ORDER, SHOP, LEARN_MORE, SIGN_UP, GET_OFFER
  • title is required for events and offers

On these locally raised errors status_code, code, and response_body are None and errors is empty; str(e) lists every problem found:

Python
import listingsapi client = listingsapi.ListingsAPI() try:  client.locations.add(      name="Acme Dental",      description="Too short.",      sub_category_id=1432,      country_iso="US",      city="New York",  )except listingsapi.ValidationError as e:  print(e)  # Invalid location: description must be at least 200 characters (got 10)  print(e.status_code)  # None: raised before any network call

Automatic retries

The SDK retries 429, 500, 502, 503, and 504 automatically for GET and POST methods (default 2 retries, 0.5-second exponential backoff factor, Retry-After honoured). Configure via the max_retries constructor argument; see Configuration.

Backing off on RateLimitError

When a 429 survives the built-in retries, RateLimitError.retry_after carries the server's Retry-After value in seconds (or None if the header was absent). A simple recipe for batch jobs:

Python
import randomimport time import listingsapi client = listingsapi.ListingsAPI(max_retries=0)  # manual retry control def with_backoff(call, max_attempts=5):  for attempt in range(max_attempts):      try:          return call()      except listingsapi.RateLimitError as e:          if attempt == max_attempts - 1:              raise          if e.retry_after is not None:              delay = e.retry_after          else:              delay = min(2 ** attempt, 30) + random.random()          time.sleep(delay) page = with_backoff(lambda: client.locations.list(first=50))for location in page:  print(location.name)

Importing exceptions

Python
# All exceptions from the top-level moduleimport listingsapi listingsapi.ListingsAPIErrorlistingsapi.APIErrorlistingsapi.AuthenticationErrorlistingsapi.PermissionDeniedErrorlistingsapi.NotFoundErrorlistingsapi.ValidationErrorlistingsapi.RateLimitErrorlistingsapi.InternalServerErrorlistingsapi.APIConnectionError # Or import individuallyfrom listingsapi import RateLimitError, ValidationError

See also