Gestión de errores

Jerarquía de excepciones, atributos de error, errores en respuestas HTTP 200, validación del cliente y reintentos en el Python SDK de Listings API.

Todas las excepciones se pueden importar desde el módulo listingsapi de nivel superior.

Jerarquía de excepciones

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

Captura ListingsAPIError para gestionar cualquier excepción que pueda lanzar el SDK, APIError para cualquier error que haya comunicado la API o una subclase específica para gestionar un caso concreto.

Atributos de las excepciones

APIError y sus subclases incluyen:

AtributoTipoDescripción
status_codeint | NoneCódigo de estado HTTP (puede ser 200 para errores en el contenido de la respuesta)
codestr | NonePrimer código de error de la plataforma, e.g. SY10005
errorslist[dict]Entradas de error normalizadas, cada una {"code", "message", "context"}
response_bodystr | NoneTexto sin procesar de la respuesta de la API
retry_afterfloat | NoneSolo para RateLimitError; extraído de la cabecera Retry-After

APIConnectionError hereda directamente de ListingsAPIError y no tiene ninguno de estos atributos; el mensaje de la excepción describe el fallo de red.

Captura de excepciones

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)

Errores en respuestas HTTP 200

La plataforma a veces comunica fallos dentro del cuerpo de una respuesta 200. El SDK inspecciona cada respuesta y lanza las excepciones correspondientes, por lo que nunca tienes que comprobar los indicadores success ni recorrer los arrays errors por tu cuenta:

  • Los cuerpos errors[] de nivel superior se asignan a excepciones según el código: SY90005 y SY90001 provocan AuthenticationError, SY90003 provoca PermissionDeniedError, RATE_LIMITED provoca RateLimitError y cualquier otro código provoca APIError.
  • Las respuestas a operaciones de escritura (creación, actualización, archivado, publicación de contenido) que contienen success: false o una lista errors[] provocan ValidationError.

En ambos casos, status_code es el estado HTTP real (a menudo 200), code es el primer código de la plataforma y errors contiene todas las entradas normalizadas:

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"])

Validación en el cliente

locations.add() y las funciones auxiliares para publicaciones (posts.create_announcement(), posts.create_event(), posts.create_offer(), posts.bulk_publish()) validan localmente los datos que se van a enviar y lanzan ValidationError antes de cualquier llamada de red, por lo que, si los datos no son válidos, la operación falla de inmediato con un mensaje claro, sin enviar una solicitud ni esperar una respuesta.

Para locations.add():

  • name debe tener entre 2 y 150 caracteres
  • description debe tener al menos 200 caracteres
  • sub_category_id es obligatorio
  • country_iso es obligatorio
  • city es obligatorio salvo que hide_address=True (empresas con área de servicio)

Para las funciones auxiliares para publicaciones:

  • name es obligatorio y location_ids no debe estar vacío
  • sites debe ser un subconjunto no vacío de GOOGLE, FACEBOOK
  • message es obligatorio; cuando es un diccionario por sitio, cada sitio de destino necesita texto
  • cta_type y cta_url deben proporcionarse juntos, y cta_type debe ser uno de los siguientes: BOOK, ORDER, SHOP, LEARN_MORE, SIGN_UP, GET_OFFER
  • title es obligatorio para eventos y ofertas

En estos errores generados localmente, status_code, code y response_body tienen el valor None y errors está vacío; str(e) enumera todos los problemas encontrados:

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

Reintentos automáticos

El SDK reintenta automáticamente las solicitudes ante 429, 500, 502, 503 y 504 para GET, HEAD y OPTIONS (por defecto, 2 reintentos, un factor de espera exponencial de 0.5 segundos y respetando Retry-After). Las operaciones de escritura no se reintentan: una solicitud POST o DELETE se envía una sola vez y provoca una excepción al primer fallo. Configura este comportamiento mediante el argumento max_retries del constructor; consulta Configuración.

La API v4 no admite Idempotency-Key ni detecta operaciones de escritura repetidas, por lo que una operación de escritura que agote el tiempo de espera o devuelva 5xx puede haberse aplicado ya. Reintentarla sin comprobarlo crearía una segunda ubicación, publicación o respuesta a una reseña. Vuelve a consultar el registro mediante una clave que controles antes de repetir una operación de escritura.

Espera entre reintentos ante RateLimitError

Cuando un error 429 persiste tras los reintentos integrados, RateLimitError.retry_after contiene el valor de Retry-After del servidor en segundos (o None si la cabecera no estaba presente). Un ejemplo sencillo para tareas por lotes:

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)

Importación de excepciones

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

Consulta también