본문으로 건너뛰기

호출 제한 429 rate limit — 기다릴지 멈출지 가르는 순서

개발 가이드약 8분 읽기비교표복사 양식점검 카드
핵심 요약8개 절 · 약 8분

같은 429라도 쉬면 풀리는 것과 다시 보내도 안 풀리는 것이 있어요. 오류 이름으로 가르는 표, 남은 양을 알려 주는 헤더, 한 겹만 다시 보내는 Python 코드를 담았어요.

읽는 분
배치·자동화·서비스를 돌리다 429를 만난 개발팀
끝나면
429가 나면 오류 이름으로 기다릴지 멈출지 가르고, 속도 429만 횟수를 정해 한 겹으로 다시 보내요.
이 글의 목차 (8개 절)
01

429가 나면 먼저 가를 것 — 기다릴 429와 멈출 429

429는 「너무 빨리 보냈다」와 「한도·잔액에 닿았다」가 같은 번호로 와요. 오류 코드에 spend_limit · usage_limit · credit_balance · key_limit 같은 이름이 있으면 다시 보내도 안 풀리니, 재시도를 멈추고 한도나 결제부터 봐요. 그런 이름 없이 너무 빨리 보냈다는 뜻이면 Retry-After 시간 이상 쉰 뒤, 횟수를 정해 한 겹으로만 다시 보내요.

429가 났을 때 다음 행동
429가 났을 때 다음 행동
분당 요청·토큰 속도응답에서 보이는 것Retry-After 헤더 · 「Rate limit reached for requests」 · rate_limit_exceeded(Gemini)기다리면 풀리나풀려요다음 행동Retry-After 시간 이상 쉬고, 횟수를 정해 다시 보내요
호출량을 갑자기 늘림응답에서 보이는 것slow_down · 사용량이 급히 늘어 걸린 429기다리면 풀리나잠깐 쉬면 풀려요다음 행동늘리는 속도를 늦춰요
하루 요청·토큰 한도응답에서 보이는 것quota_exceeded(Gemini 하루 한도) · OpenAI 하루 한도(RPD·TPD)기다리면 풀리나다시 열리는 시각까지 안 풀려요다음 행동아래 표의 다시 열리는 시각까지 멈추거나 한도를 올려요
지출·사용 상한응답에서 보이는 것organization_spend_limit_exceeded · project_spend_limit_exceeded · organization_usage_limit_exceeded · enforced_spend_limit_reached기다리면 풀리나안 풀려요다음 행동콘솔에서 상한을 올리거나 다시 열리는 날까지 멈춰요
선불 잔액응답에서 보이는 것credit_balance_exhausted기다리면 풀리나안 풀려요다음 행동충전해요
원포트 키·그룹 몫응답에서 보이는 것key_limit_exceeded · group_limit_exceeded기다리면 풀리나안 풀려요다음 행동키 한도를 올리거나 다시 열리는 때까지 멈춰요 — 맨 아래 표

분당 몇 건까지인지는 등급·모델마다 달라 여기 적지 않았어요. 회사마다 보는 곳은 아래 표에 있어요.

02

무엇으로 세고 언제 다시 열리나

세 회사 모두 키 하나가 아니라 조직이나 프로젝트 단위로 세요. 다시 열리는 방식은 회사마다 달라요.

회사별 속도 한도 — 2026년 10월 17일 각 회사 문서 기준
회사별 속도 한도 — 2026년 10월 17일 각 회사 문서 기준
OpenAI세는 단위분당·하루 요청(RPM·RPD) · 분당·하루 토큰(TPM·TPD) · 분당 이미지(IPM)묶이는 단위조직·프로젝트(사용자 단위 아님)다시 열리는 방식응답 헤더 x-ratelimit-reset-* 가 남은 시간을 알려 줘요한도 보는 곳platform.openai.com 「Settings › Organization › Limits」
Anthropic세는 단위분당 요청(RPM) · 분당 입력 토큰(ITPM) · 분당 출력 토큰(OTPM)묶이는 단위조직(워크스페이스마다 더 낮게 걸 수 있음)다시 열리는 방식정해진 시각 없이 계속 다시 차요. 60 RPM 이 초당 1건으로 집행될 수 있어요. 등급 지출 상한만 다음 달 1일 00:00 UTC한도 보는 곳platform.claude.com/usage/limits
Google세는 단위분당 요청(RPM) · 분당 입력 토큰(TPM) · 하루 요청(RPD)묶이는 단위프로젝트(키 단위 아님)다시 열리는 방식하루 요청(RPD)은 태평양 시간 자정에 다시 열려요한도 보는 곳aistudio.google.com/rate-limit
03

응답 헤더에서 남은 양 읽기

Retry-After 는 최소 대기 시간이에요. 이보다 먼저 보내면 또 막혀요. 여러 작업이 한꺼번에 다시 몰리지 않게 작은 무작위 대기를 더하되, 빼지는 마세요.

Anthropic 의 등급 지출 상한에 닿은 429 에는 retry-after 가 아예 없어요. 오류 몸통의 details.error_code 가 enforced_spend_limit_reached 면 다시 보내지 말고 상한부터 봐요.

429 응답에서 볼 헤더
429 응답에서 볼 헤더
OpenAI대기 시간Retry-After(초)남은 요청x-ratelimit-remaining-requests남은 토큰x-ratelimit-remaining-tokens다시 차는 때x-ratelimit-reset-requests · x-ratelimit-reset-tokens(남은 시간, 예: 6m0s)
Anthropic대기 시간retry-after(초)남은 요청anthropic-ratelimit-requests-remaining남은 토큰anthropic-ratelimit-input-tokens-remaining · anthropic-ratelimit-output-tokens-remaining다시 차는 때anthropic-ratelimit-*-reset(RFC 3339 시각)

Gemini API 한도 문서에는 응답 헤더 이름이 없어요.

04

다시 보내는 코드는 한 겹만

공식 SDK 는 429 를 알아서 다시 보내요. OpenAI·Anthropic SDK 는 2번, Google 공식 Python SDK 는 최대 4번이에요. 여기에 직접 짠 재시도 루프를 얹으면 횟수가 곱해지고, OpenAI 는 실패한 요청도 분당 한도에 세요.

그래서 SDK 재시도를 끄고(max_retries=0) 아래 함수 한 겹만 둬요. 멈출 이름이면 바로 멈추고, 속도 429 만 Retry-After 와 두 배씩 늘리는 대기 중 긴 쪽만큼 쉬고 다시 보내요.

Anthropic SDK 를 쓰면 anthropic.Anthropic(max_retries=0) · anthropic.RateLimitError 로 바꾸고, 부르는 줄은 client.messages.create(model=MODEL, max_tokens=..., messages=messages) 로 써요. Anthropic 은 max_tokens 를 꼭 넣어야 해요.

서버가 60초보다 오래 기다리라고 하면 그 자리에서 다시 보내지 않고 오류를 올려요. 그 작업은 큐에 미뤘다가 나중에 다시 넣어요.

복사용 문안

Python — 멈출 429는 바로 멈추고, 속도 429만 다시 보내기

import os
import random
import time

from openai import OpenAI, RateLimitError

client = OpenAI(
    max_retries=0,  # SDK 기본 재시도(2번)를 꺼서 아래 루프와 곱해지지 않게
    # base_url="https://oneport.kr/v1",  # 원포트 키로 부를 때만. OpenAI 를 바로 부르면 이 줄은 지워요
)
MODEL = os.environ["MODEL"]  # 쓰는 모델 이름은 환경변수로

# 다시 보내도 안 풀리는 429 — 지출·사용 상한, 잔액, 키·그룹 몫
STOP_CODES = {
    "credit_balance_exhausted",
    "organization_spend_limit_exceeded",
    "project_spend_limit_exceeded",
    "organization_usage_limit_exceeded",
    "insufficient_quota",  # 결제 쪽 오류의 type 으로 남아 있을 수 있는 이름
    "enforced_spend_limit_reached",  # Anthropic 등급 지출 상한(details.error_code)
    "key_limit_exceeded",  # 원포트 키 몫
    "group_limit_exceeded",  # 원포트 그룹 몫
}


def error_names(err):
    names = {getattr(err, "code", None), getattr(err, "type", None)}
    body = err.body if isinstance(err.body, dict) else {}
    if isinstance(body.get("error"), dict):
        body = body["error"]
    names.update([body.get("type"), body.get("code")])
    details = body.get("details")
    if isinstance(details, dict):
        names.add(details.get("error_code"))
    return {name for name in names if name}


def retry_after_seconds(err):
    value = err.response.headers.get("retry-after", "")
    try:
        return max(0.0, float(value))
    except ValueError:
        return 0.0  # 없거나 날짜 형식이면 숫자로 읽지 않아요


MAX_SERVER_WAIT = 60  # 이보다 긴 Retry-After 는 기다리지 않고 미뤄요


def create_with_retry(messages, max_attempts=4):  # 첫 호출 포함 4번 — 다시 보내며 쉬는 시간은 모두 합쳐 약 3분 안
    for attempt in range(max_attempts):
        try:
            return client.chat.completions.create(model=MODEL, messages=messages)
        except RateLimitError as err:
            if error_names(err) & STOP_CODES:
                raise  # 한도·잔액 — 기다려도 안 풀려요
            if attempt == max_attempts - 1:
                raise
            server_wait = retry_after_seconds(err)
            if server_wait > MAX_SERVER_WAIT:
                raise  # 너무 오래 기다리라는 뜻 — 작업을 미뤄요
            backoff = min(32, 0.5 * 2 ** attempt)
            wait = max(server_wait, backoff)
            time.sleep(wait + random.uniform(0, 1))  # 지터는 더하기만 — Retry-After 보다 짧아지지 않게
05

키를 늘려도 속도 한도는 그대로

키를 여러 개로 나눴는데 똑같이 429 가 나요

원인·확인
같은 조직·프로젝트의 키는 속도 한도 한 통을 나눠 써요. 키를 늘려도 분당 요청·토큰은 늘지 않아요.
해결 방법
키 나누기는 사람·작업마다 금액을 가르고 쓴 곳을 추적할 때 써요. 속도는 동시 호출 수로 맞춰요.

짧은 질문인데 토큰 한도에 걸려요

원인·확인
OpenAI 문서는 max_tokens 와 요청 글자 수로 어림한 토큰 중 큰 값으로 센다고 적어요. 상한을 크게 잡아 두면 실제 답이 짧아도 그만큼 세요. Anthropic 은 max_tokens 를 분당 출력 토큰(OTPM)에 세지 않아요.
해결 방법
OpenAI 에서는 출력 상한을 답 길이에 가깝게 줄여요. Chat Completions 에서는 deprecated 된 max_tokens 대신 max_completion_tokens 를 써요(o 시리즈 모델은 max_tokens 를 안 받아요). 이 값엔 추론 토큰도 들어가서, 너무 빠듯하면 답이 끊겨요.

호출량을 확 늘린 직후 429 가 나요

원인·확인
OpenAI 는 slow_down, Anthropic 은 acceleration limit 이에요. 분당 한도 안이어도 늘어나는 속도가 빠르면 막혀요.
해결 방법
OpenAI 는 분당 입력 100만 토큰을 넘는 규모면 15분마다 50% 넘게 올리지 말라고 적어요.
06

배치·자동화 작업이 막히지 않게

  1. 동시에 도는 호출 수에 상한을 두고, 429 없이 돌면 조금씩 올려요.
  2. 같은 긴 지시문을 매번 보내면 프롬프트 캐시를 써요. Anthropic 은 대부분 모델에서 캐시에서 읽은 토큰을 분당 입력 한도(ITPM)에 세지 않아요.
  3. 바로 답이 필요 없는 대량 작업은 각 회사의 배치 API 로 보내요. OpenAI Batch API · Anthropic Message Batches · Gemini Batch API 모두 일반 호출과 따로 한도를 둬요. 원포트엔 배치 주소가 없어서 각 회사 키로 직접 불러야 해요.
07

원포트 키에서는 이렇게 막혀요

원포트는 분당 요청 수 제한을 두지 않아요. 키마다 이번 달·오늘 금액 몫으로 막고, 스페이스에서는 그룹 몫으로도 막아요. 한 사람이 몫에 닿아도 나머지 키는 그대로 써요.

다만 모델 회사가 429를 주면 원포트를 거쳐도 날 수 있어요 — 같은 모델을 다른 경로로 다시 걸어 보고, 전부 막히면 429를 그대로 돌려드려요. 이때 Retry-After 헤더는 붙지 않아서, 위 함수는 0.5초부터 두 배씩 늘려 기다려요. 다른 경로로 몇 번 걸었는지는 응답 헤더 x-gateway-fallback-depth 에 적혀요.

1 크레딧 = 부가세 포함 2원어치예요.

모델마다 1M 토큰당 크레딧을 얼마나 사용하는지는 API 페이지의 「모델별 차감 단가」 표에 있어요.

원포트 키로 부를 때 막히는 응답
원포트 키로 부를 때 막히는 응답
429 key_limit_exceeded(이번 달)뜻이번 달 남은 몫이 이 요청이 잡아 둘 최대 금액보다 적어요. 그래서 /v1/key 의 남은 양이 0보다 커도 나요다시 보내면같은 요청은 한국 시간 다음 달 1일까지 안 풀려요할 일max_tokens 를 줄이거나, 「AI API」 화면 「키 관리」에서 「한도 수정」
429 key_limit_exceeded(오늘)뜻이 키의 오늘 몫을 다 썼어요다시 보내면한국 시간 내일 0시까지 안 풀려요할 일「한도 수정」에서 하루 한도를 올려요
429 group_limit_exceeded뜻그룹 남은 몫으로는 이 요청을 받을 수 없거나 그룹이 닫혔어요다시 보내면매달 채우는 그룹은 다음 달 1일까지 안 풀려요할 일스페이스 관리자에게 말해요
429 rate_limit_error뜻모델 회사가 429를 줬고 다른 경로도 막혔어요. 세부 오류 코드는 실리지 않아요다시 보내면대개 쉬었다 보내면 풀려요할 일위 함수로 다시 보내고, 계속되면 문의해요
402 insufficient_credit뜻잔액이 모자라거나, 이 요청이 잡아 둘 최대 금액(max_tokens 로 정해져요)보다 적어요. 보내기 전에 막혀서 요금이 붙지 않아요다시 보내면같은 요청은 안 풀려요할 일max_tokens 를 줄이거나 원포트 AI API 크레딧을 충전해요
CLI · 설정

이 키의 남은 몫 보기 — 그룹 몫은 나오지 않아요

curl https://oneport.kr/v1/key \
  -H "Authorization: Bearer sk-so-..."
# "month"·"day" 마다 한도 · 쓴 양 · 남은 양 · 다시 열리는 시각(resets_at)
08

더 자주 묻는 것

insufficient_quota 는 이제 안 쓰는 이름인가요? — OpenAI 오류 코드 문서의 429 줄에는 이 이름이 코드로 없어요. 결제 쪽 원인은 error.code(credit_balance_exhausted 같은 것)에 오고, 넓은 분류인 error.type 은 아직 insufficient_quota 일 수 있다고 적어요. 그래서 위 함수는 코드와 type 을 같이 봐요.

429 말고 503·529 는 어떻게 다루나요? — OpenAI 의 503 server_is_overloaded 와 Anthropic 의 529 overloaded_error 는 모델 쪽이 붐빈다는 뜻이에요. 내 키나 한도 문제가 아니라서 한도를 올려도 달라지지 않아요. 위 함수는 429 만 잡으니, 이 둘은 따로 잡아 대기를 늘려 가며 다시 보내요.

자주 묻는 질문

Retry-After 가 날짜로 오면요?

위 함수는 초 단위 숫자만 읽고, 날짜 형식이면 0으로 보고 두 배씩 늘리는 대기를 써요. 날짜까지 따르려면 email.utils.parsedate_to_datetime 으로 읽어 남은 초를 구해요.

출처와 확인일

함께 보면 좋은 글과 도움말

← 블로그 전체 목록 보기