错误处理

Listings API Python SDK 的异常层次结构、错误属性、HTTP 200 响应载荷中的错误、客户端验证及重试指南。

所有异常均可从顶层 listingsapi 模块导入。

异常层次结构

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

捕获 ListingsAPIError 可处理 SDK 可能抛出的所有异常,捕获 APIError 可处理 API 报告的所有异常,也可捕获特定子类以进行针对性处理。

异常属性

APIError 及其子类具有以下属性:

属性类型说明
status_codeint | NoneHTTP 状态码(对于载荷级错误,可以为 200)
codestr | None第一个平台错误码,e.g。SY10005
errorslist[dict]规范化后的错误条目,每个条目为 {"code", "message", "context"}
response_bodystr | NoneAPI 返回的原始响应文本
retry_afterfloat | None仅适用于 RateLimitError;从 Retry-After 响应头解析得到

APIConnectionError 直接继承自 ListingsAPIError,不具有上述任何属性;异常消息描述了网络故障。

捕获异常

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)

HTTP 200 响应中的错误

平台有时会在 200 响应体中报告失败。SDK 会检查每个响应并抛出异常,因此您无需自行检查 success 标志或遍历 errors 数组:

  • 顶层 errors[] 响应体按错误码映射到异常:SY90005 和 SY90001 会引发 AuthenticationError,SY90003 会引发 PermissionDeniedError,RATE_LIMITED 会引发 RateLimitError,其他任何错误码都会引发 APIError。
  • 写入响应(创建、更新、归档、帖子发布)如果包含 success: false 或 errors[] 列表,就会引发 ValidationError。

在这两种情况下,status_code 都是实际的 HTTP 状态码(通常为 200),code 是第一个平台错误码,errors 包含所有规范化后的条目:

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

客户端验证

locations.add() 和帖子辅助函数(posts.create_announcement()、posts.create_event()、posts.create_offer()、posts.bulk_publish())会在本地验证载荷,并在发起任何网络调用之前抛出 ValidationError,因此无效载荷会立即导致失败并给出明确的消息,无需进行一次网络往返。

对于 locations.add():

  • name 的长度必须为 2 至 150 个字符
  • description 的长度必须至少为 200 个字符
  • sub_category_id 为必填项
  • country_iso 为必填项
  • 除非 hide_address=True(服务区域商家),否则 city 为必填项

对于帖子辅助函数:

  • name 为必填项,且 location_ids 不得为空
  • sites 必须是由 GOOGLE、FACEBOOK 组成的集合的非空子集
  • message 为必填项;如果它是按站点组织的字典,则每个目标站点都需要提供文本
  • cta_type 和 cta_url 必须同时提供,且 cta_type 必须是 BOOK、ORDER、SHOP、LEARN_MORE、SIGN_UP、GET_OFFER 之一
  • 对于活动和优惠,title 为必填项

对于这些在本地抛出的错误,status_code、code 和 response_body 均为 None,且 errors 为空;str(e) 列出了发现的所有问题:

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

自动重试

对于 GET、HEAD 和 OPTIONS,SDK 会在遇到 429、500、502、503 和 504 时自动重试(默认重试 2 次,指数退避因子为 0.5 秒,并遵循 Retry-After)。写入操作不会重试:POST 或 DELETE 只发送一次,并在首次失败时抛出异常。通过构造函数参数 max_retries 进行配置;请参阅配置。

v4 API 不支持 Idempotency-Key,也不会检测重复写入,因此超时或返回 5xx 的写入操作可能已经提交。盲目重试会创建重复的地点、帖子或评价回复。在重复写入之前,请使用您掌握的键读取记录。

遇到 RateLimitError 时进行退避

如果内置重试结束后仍然出现 429,RateLimitError.retry_after 会包含服务器的 Retry-After 值,单位为秒(如果响应头不存在,则为 None)。以下是适用于批处理任务的简单示例:

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)

导入异常

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

另请参阅

  • 配置:max_retries 和 timeout
  • 快速入门:您的第一个包含错误处理的脚本