Gestion des erreurs
Hiérarchie des exceptions, attributs d’erreur, erreurs du contenu HTTP 200, validation côté client et conseils de réessai du Python SDK Listings API.
Toutes les exceptions peuvent être importées depuis le module de premier niveau listingsapi.
Hiérarchie des exceptions
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, timeoutInterceptez ListingsAPIError pour gérer toutes les exceptions que le SDK peut lever, APIError pour gérer toutes celles signalées par l'API, ou une sous-classe spécifique pour un traitement ciblé.
Attributs des exceptions
APIError et ses sous-classes possèdent les attributs suivants :
APIConnectionError hérite directement de ListingsAPIError et ne possède aucun de ces attributs ; le message de l'exception décrit la défaillance réseau.
Interception des exceptions
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)Erreurs dans les réponses HTTP 200
La plateforme signale parfois des échecs dans le corps d'une réponse 200. Le SDK inspecte chaque réponse et lève les exceptions correspondantes : vous n'avez donc jamais à vérifier vous-même les indicateurs success ni à parcourir les tableaux errors :
- Les corps
errors[]de premier niveau sont associés à une exception selon leur code :SY90005etSY90001lèventAuthenticationError,SY90003lèvePermissionDeniedError,RATE_LIMITEDlèveRateLimitErroret tout autre code lèveAPIError. - Les réponses aux opérations d'écriture (création, mise à jour, archivage, publication de posts) qui contiennent
success: falseou une listeerrors[]lèventValidationError.
Dans les deux cas, status_code correspond au statut HTTP réel (souvent 200), code est le premier code de la plateforme et errors contient toutes les entrées normalisées :
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"])Validation côté client
locations.add() et les fonctions utilitaires pour les posts (posts.create_announcement(), posts.create_event(), posts.create_offer(), posts.bulk_publish()) valident leurs données localement et lèvent ValidationError avant tout appel réseau, de sorte que des données incorrectes provoquent un échec immédiat avec un message clair, sans aller-retour réseau.
Pour locations.add() :
namedoit comporter entre 2 et 150 caractèresdescriptiondoit comporter au moins 200 caractèressub_category_idest obligatoirecountry_isoest obligatoirecityest obligatoire, sauf sihide_address=True(entreprises desservant une zone géographique)
Pour les fonctions utilitaires pour les posts :
nameest obligatoire etlocation_idsne doit pas être videsitesdoit être un sous-ensemble non vide deGOOGLE,FACEBOOKmessageest obligatoire ; lorsqu'il s'agit d'un dict par site, chaque site cible doit avoir du textecta_typeetcta_urldoivent être fournis ensemble, etcta_typedoit avoir l'une des valeurs suivantes :BOOK,ORDER,SHOP,LEARN_MORE,SIGN_UP,GET_OFFERtitleest obligatoire pour les événements et les offres
Pour ces erreurs levées localement, status_code, code et response_body valent None et errors est vide ; str(e) répertorie tous les problèmes détectés :
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 callNouvelles tentatives automatiques
Le SDK effectue automatiquement de nouvelles tentatives en cas de 429, 500, 502, 503 et 504 pour GET, HEAD et OPTIONS (2 nouvelles tentatives par défaut, facteur de temporisation exponentielle de 0,5 seconde, respect de Retry-After). Les opérations d'écriture ne sont pas retentées : une requête POST ou DELETE est envoyée une seule fois et lève une exception dès le premier échec. Configurez ce comportement à l'aide de l'argument max_retries du constructeur ; voir Configuration.
L'API v4 ne prend pas en charge Idempotency-Key et ne détecte pas les écritures répétées ; une écriture dont le délai d'attente expire ou qui renvoie 5xx peut donc avoir déjà été enregistrée. La retenter sans vérification créerait un deuxième établissement, post ou réponse à un avis. Relisez l'enregistrement à l'aide d'une clé que vous contrôlez avant de répéter une écriture.
Temporisation en cas de RateLimitError
Lorsqu'une exception 429 persiste après les nouvelles tentatives intégrées, RateLimitError.retry_after contient la valeur Retry-After du serveur en secondes (ou None si l'en-tête était absent). Voici un exemple simple pour les traitements par lots :
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)Importation des exceptions
# All exceptions from the top-level moduleimport listingsapi listingsapi.ListingsAPIErrorlistingsapi.APIErrorlistingsapi.AuthenticationErrorlistingsapi.PermissionDeniedErrorlistingsapi.NotFoundErrorlistingsapi.ValidationErrorlistingsapi.RateLimitErrorlistingsapi.InternalServerErrorlistingsapi.APIConnectionError # Or import individuallyfrom listingsapi import RateLimitError, ValidationErrorVoir aussi
- Configuration :
max_retriesettimeout - Démarrage rapide : votre premier script avec gestion des erreurs