Tratamento de erros

Hierarquia de exceções, atributos de erro, erros no payload HTTP 200, validação no cliente e orientações de retentativa no Python SDK da Listings API.

Todas as exceções podem ser importadas do módulo de nível superior listingsapi.

Hierarquia de exceções

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

Capture ListingsAPIError para tratar todas as exceções que o SDK pode lançar, APIError para tratar qualquer erro informado pela API ou uma subclasse específica para um tratamento direcionado.

Atributos das exceções

APIError e suas subclasses contêm:

AtributoTipoDescrição
status_codeint | NoneCódigo de status HTTP (pode ser 200 para erros no nível do payload)
codestr | NonePrimeiro código de erro da plataforma, e.g. SY10005
errorslist[dict]Entradas de erro normalizadas, cada uma {"code", "message", "context"}
response_bodystr | NoneTexto bruto da resposta da API
retry_afterfloat | NoneSomente RateLimitError; extraído do cabeçalho Retry-After

APIConnectionError herda diretamente de ListingsAPIError e não tem nenhum desses atributos; a mensagem da exceção descreve a falha de rede.

Captura de exceções

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)

Erros em respostas HTTP 200

A plataforma às vezes informa falhas no corpo de uma resposta 200. O SDK inspeciona cada resposta e lança exceções, para que você nunca precise verificar os indicadores success nem percorrer os arrays errors por conta própria:

  • Corpos de resposta errors[] de nível superior são mapeados por código: SY90005 e SY90001 lançam AuthenticationError, SY90003 lança PermissionDeniedError, RATE_LIMITED lança RateLimitError e qualquer outro código lança APIError.
  • Respostas de operações de escrita (criação, atualização, arquivamento, publicação de posts) que retornam com success: false ou uma lista errors[] fazem com que seja lançada a exceção ValidationError.

Em ambos os casos, status_code é o status HTTP real (geralmente 200), code é o primeiro código da plataforma e errors contém todas as 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"])

Validação no cliente

locations.add() e as funções auxiliares de publicação de posts (posts.create_announcement(), posts.create_event(), posts.create_offer(), posts.bulk_publish()) validam seus payloads localmente e lançam ValidationError antes de qualquer chamada de rede, de modo que um payload inválido cause uma falha imediata com uma mensagem clara, sem precisar enviar uma requisição e aguardar a resposta.

Para locations.add():

  • name deve ter de 2 a 150 caracteres
  • description deve ter pelo menos 200 caracteres
  • sub_category_id é obrigatório
  • country_iso é obrigatório
  • city é obrigatório, a menos que hide_address=True (empresas que atendem em uma área de cobertura)

Para as funções auxiliares de publicação de posts:

  • name é obrigatório e location_ids não pode estar vazio
  • sites deve ser um subconjunto não vazio de GOOGLE, FACEBOOK
  • message é obrigatório; quando for um dict por site, cada site de destino precisa ter texto
  • cta_type e cta_url devem ser fornecidos juntos, e cta_type deve ser um dos seguintes valores: BOOK, ORDER, SHOP, LEARN_MORE, SIGN_UP, GET_OFFER
  • title é obrigatório para eventos e ofertas

Nesses erros lançados localmente, status_code, code e response_body são None e errors está vazio; str(e) lista todos os 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

Novas tentativas automáticas

O SDK faz novas tentativas automaticamente em caso de 429, 500, 502, 503 e 504 para GET, HEAD e OPTIONS (por padrão, 2 novas tentativas, fator de espera exponencial de 0,5 segundo, respeitando Retry-After). Operações de escrita não são repetidas: uma requisição POST ou DELETE é enviada uma única vez e lança uma exceção na primeira falha. Configure pelo argumento max_retries do construtor; consulte Configuração.

A API v4 não oferece suporte a Idempotency-Key e não detecta operações de escrita repetidas, portanto, uma operação de escrita que exceda o tempo limite ou retorne 5xx pode já ter sido efetivada. Repeti-la sem verificar criaria um segundo local, post ou resposta a uma avaliação. Consulte novamente o registro por uma chave que você controla antes de repetir uma operação de escrita.

Espera progressiva em caso de RateLimitError

Quando 429 persiste após as novas tentativas integradas, RateLimitError.retry_after contém o valor de Retry-After do servidor em segundos (ou None se o cabeçalho estiver ausente). Um exemplo simples para tarefas em lote:

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)

Importação de exceções

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

Veja também