오류 처리

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

SDK가 발생시킬 수 있는 모든 예외를 처리하려면 ListingsAPIError을, API가 보고한 모든 오류를 처리하려면 APIError을, 특정 예외만 처리하려면 해당 하위 클래스를 잡습니다.

예외 속성

APIError과 그 하위 클래스에는 다음 속성이 있습니다.

속성타입설명
status_codeint | NoneHTTP 상태 코드(페이로드 수준 오류에서는 200일 수 있습니다)
codestr | None첫 번째 플랫폼 오류 코드, e.g. SY10005
errorslist[dict]정규화된 오류 항목으로, 각 항목은 {"code", "message", "context"}입니다
response_bodystr | NoneAPI의 원본 응답 텍스트
retry_afterfloat | NoneRateLimitError에만 있으며, 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은 필수이며, 사이트별 dict인 경우 모든 대상 사이트에 텍스트가 있어야 합니다
  • 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

자동 재시도

SDK는 GET, HEAD, OPTIONS에 대해 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
  • 빠른 시작: 오류 처리를 포함한 첫 번째 스크립트