오류 처리
Listings API Python SDK의 예외 계층 구조, 오류 속성, HTTP 200 응답 본문의 오류, 클라이언트 측 유효성 검사, 재시도 지침을 설명합니다.
모든 예외는 최상위 listingsapi 모듈에서 가져올 수 있습니다.
예외 계층 구조
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, timeoutSDK가 발생시킬 수 있는 모든 예외를 처리하려면 ListingsAPIError을, API가 보고한 모든 오류를 처리하려면 APIError을, 특정 예외만 처리하려면 해당 하위 클래스를 잡습니다.
예외 속성
APIError과 그 하위 클래스에는 다음 속성이 있습니다.
APIConnectionError은 ListingsAPIError을 직접 상속하며 이러한 속성이 없고, 예외 메시지에 네트워크 오류에 대한 설명이 포함됩니다.
예외 잡기
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에는 정규화된 모든 항목이 포함됩니다.
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)에는 발견된 모든 문제가 나열됩니다.
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). 배치 작업에 사용할 수 있는 간단한 예는 다음과 같습니다.
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)예외 가져오기
# 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