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
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, timeoutCaptura 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:
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
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:SY90005ySY90001provocanAuthenticationError,SY90003provocaPermissionDeniedError,RATE_LIMITEDprovocaRateLimitErrory cualquier otro código provocaAPIError. - Las respuestas a operaciones de escritura (creación, actualización, archivado, publicación de contenido) que contienen
success: falseo una listaerrors[]provocanValidationError.
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:
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():
namedebe tener entre 2 y 150 caracteresdescriptiondebe tener al menos 200 caracteressub_category_ides obligatoriocountry_isoes obligatoriocityes obligatorio salvo quehide_address=True(empresas con área de servicio)
Para las funciones auxiliares para publicaciones:
namees obligatorio ylocation_idsno debe estar vacíositesdebe ser un subconjunto no vacío deGOOGLE,FACEBOOKmessagees obligatorio; cuando es un diccionario por sitio, cada sitio de destino necesita textocta_typeycta_urldeben proporcionarse juntos, ycta_typedebe ser uno de los siguientes:BOOK,ORDER,SHOP,LEARN_MORE,SIGN_UP,GET_OFFERtitlees 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:
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 callReintentos 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:
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
# All exceptions from the top-level moduleimport listingsapi listingsapi.ListingsAPIErrorlistingsapi.APIErrorlistingsapi.AuthenticationErrorlistingsapi.PermissionDeniedErrorlistingsapi.NotFoundErrorlistingsapi.ValidationErrorlistingsapi.RateLimitErrorlistingsapi.InternalServerErrorlistingsapi.APIConnectionError # Or import individuallyfrom listingsapi import RateLimitError, ValidationErrorConsulta también
- Configuración:
max_retriesytimeout - Inicio rápido: tu primer script con gestión de errores