요청 빈도 제한

Listings와 Social에 적용되는 요금제별 요청 제한, 429 응답 구조, 대기 시간을 늘리며 재시도하는 방법을 설명합니다.

Listings API는 계정별 요청 수를 측정하여 요금제의 요청 빈도 제한을 적용합니다. 제한 안에서 요청하면 연동이 지연 없이 응답하며, 제한을 초과하면 429 Too Many Requests와 함께 재시도에 필요한 정보를 모두 반환합니다.

요금제별 제한

유료 요금제 없이 API를 사용해 보고 싶으신가요? 무료 샌드박스 1일 이용권은 이용을 시작할 때 API 키와 함께 현재 적용되는 요청 제한을 반환합니다.

제한은 요금제에 따라 계정별로 적용됩니다. 한 계정에 속한 모든 API 키는 동일한 요청 허용량을 공유합니다. 요청 수는 분당 버스트 제한, 시간당 제한, 일일 상한이라는 3가지 시간 구간을 기준으로 측정됩니다. 엔드포인트에 관계없이 모든 요청은 동일한 허용량을 소모합니다. 무료 체험은 선택한 요금제로 진행되므로 첫 요청부터 해당 요금제의 제한이 적용됩니다.

요금제분당 제한시간당 제한일일 제한
Launch분당 요청 10회시간당 요청 600회일일 요청 7,200회
Growth분당 요청 50회시간당 요청 3,000회일일 요청 36,000회
Enterprise계약별로 설정계약별로 설정계약별로 설정

Social도 동일한 허용량을 사용합니다

Social 엔드포인트에도 다른 모든 엔드포인트와 동일한 계정별 요청 허용량이 적용됩니다. 500개 행을 포함한 POST /social/bulk-posts는 요청 500회가 아니라 1회로 계산되므로, 콘텐츠를 일괄로 불러올 때는 일괄 처리가 요청 허용량을 가장 적게 소모하는 방법입니다.

Listings와 Social은 별도의 구독이지만, 허용량이 별도로 제공되지는 않습니다. 두 구독을 모두 보유한 계정에는 두 요금제의 제한 중 더 높은 제한이 모든 엔드포인트에 걸쳐 한 번만 적용되며, 두 제한을 합산하거나 제품별로 허용량을 부여하지 않습니다. Listings Launch와 Social Growth를 사용하는 고객의 모든 트래픽에는 위의 Growth 수치가 적용됩니다.

429 응답

제한을 초과하면 API는 HTTP 429와 표준 오류 응답을 반환합니다. 재시도 시 중요한 필드는 3개입니다.

  • retry_after_seconds: 재시도하기 전에 기다려야 하는 시간입니다. Retry-After HTTP 헤더가 있으면 동일한 값을 담고 있습니다.
  • correlation_id: 지원팀에 문의할 때 전달할 요청 식별자입니다.
  • doc_url: 오류 코드 참조 문서의 rate_limited 항목으로 연결되는 링크입니다.
JSON
{  "error": {    "code": "RATE_LIMITED",    "message": "Too many requests. Slow down.",    "retry_after_seconds": 30,    "correlation_id": "9f2c1e64-2b1a-4b0e-9f1a-2c7d5e8a1b3c",    "doc_url": "https://listingsapi.com/docs/error-codes#rate_limited"  }}

백오프로 429 처리하기

retry_after_seconds가 있으면 해당 값을 따르고, 없으면 대기 시간을 지수적으로 늘립니다. 429가 연속해서 발생할 때마다 상한에 도달할 때까지 대기 시간을 2배로 늘리고, 여러 클라이언트가 동시에 재시도하지 않도록 약간의 무작위 지터를 추가합니다.

Python
import randomimport time import requests MAX_RETRIES = 5  def get_with_backoff(url: str, headers: dict, max_retries: int = MAX_RETRIES):    for attempt in range(max_retries):        resp = requests.get(url, headers=headers)        if resp.status_code != 429:            resp.raise_for_status()            return resp.json()         # Prefer the server's hint; fall back to exponential backoff + jitter.        body = resp.json().get("error", {})        retry_after = body.get("retry_after_seconds")        if retry_after is None:            retry_after = min(2 ** attempt, 30) + random.uniform(0, 1)        time.sleep(retry_after)     raise RuntimeError("Still rate limited after retrying")

제한 이내로 유지하기

  • 한 번에 한 페이지씩 요청합니다. 대기 없이 병렬로 실행되는 페이지네이션 반복문은 429의 가장 흔한 원인입니다. pageInfo.hasNextPage를 확인하고 현재 페이지의 응답이 반환된 후에만 다음 페이지를 가져옵니다.
  • 모든 페이지를 가져온 후 클라이언트 측에서 행을 버리는 대신, filter 쿼리 매개변수를 사용하여 서버 측에서 필터링합니다.
  • 자주 변경되지 않는 응답은 요청할 때마다 다시 가져오는 대신 캐시합니다.