같은 429라도 쉬면 풀리는 것과 다시 보내도 안 풀리는 것이 있어요. 오류 이름으로 가르는 표, 남은 양을 알려 주는 헤더, 한 겹만 다시 보내는 Python 코드를 담았어요.
- 읽는 분
- 배치·자동화·서비스를 돌리다 429를 만난 개발팀
- 끝나면
- 429가 나면 오류 이름으로 기다릴지 멈출지 가르고, 속도 429만 횟수를 정해 한 겹으로 다시 보내요.
이 글의 목차 (8개 절)
429가 나면 먼저 가를 것 — 기다릴 429와 멈출 429
429는 「너무 빨리 보냈다」와 「한도·잔액에 닿았다」가 같은 번호로 와요. 오류 코드에 spend_limit · usage_limit · credit_balance · key_limit 같은 이름이 있으면 다시 보내도 안 풀리니, 재시도를 멈추고 한도나 결제부터 봐요. 그런 이름 없이 너무 빨리 보냈다는 뜻이면 Retry-After 시간 이상 쉰 뒤, 횟수를 정해 한 겹으로만 다시 보내요.
| 갈래 | 응답에서 보이는 것 | 기다리면 풀리나 | 다음 행동 |
|---|---|---|---|
| 분당 요청·토큰 속도 | 응답에서 보이는 것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 | 기다리면 풀리나안 풀려요 | 다음 행동키 한도를 올리거나 다시 열리는 때까지 멈춰요 — 맨 아래 표 |
분당 몇 건까지인지는 등급·모델마다 달라 여기 적지 않았어요. 회사마다 보는 곳은 아래 표에 있어요.
무엇으로 세고 언제 다시 열리나
세 회사 모두 키 하나가 아니라 조직이나 프로젝트 단위로 세요. 다시 열리는 방식은 회사마다 달라요.
| 회사 | 세는 단위 | 묶이는 단위 | 다시 열리는 방식 | 한도 보는 곳 |
|---|---|---|---|---|
| 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 |
| 세는 단위분당 요청(RPM) · 분당 입력 토큰(TPM) · 하루 요청(RPD) | 묶이는 단위프로젝트(키 단위 아님) | 다시 열리는 방식하루 요청(RPD)은 태평양 시간 자정에 다시 열려요 | 한도 보는 곳aistudio.google.com/rate-limit |
응답 헤더에서 남은 양 읽기
Retry-After 는 최소 대기 시간이에요. 이보다 먼저 보내면 또 막혀요. 여러 작업이 한꺼번에 다시 몰리지 않게 작은 무작위 대기를 더하되, 빼지는 마세요.
Anthropic 의 등급 지출 상한에 닿은 429 에는 retry-after 가 아예 없어요. 오류 몸통의 details.error_code 가 enforced_spend_limit_reached 면 다시 보내지 말고 상한부터 봐요.
| 회사 | 대기 시간 | 남은 요청 | 남은 토큰 | 다시 차는 때 |
|---|---|---|---|---|
| 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 한도 문서에는 응답 헤더 이름이 없어요.
다시 보내는 코드는 한 겹만
공식 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 보다 짧아지지 않게키를 늘려도 속도 한도는 그대로
키를 여러 개로 나눴는데 똑같이 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% 넘게 올리지 말라고 적어요.
배치·자동화 작업이 막히지 않게
- 동시에 도는 호출 수에 상한을 두고, 429 없이 돌면 조금씩 올려요.
- 같은 긴 지시문을 매번 보내면 프롬프트 캐시를 써요. Anthropic 은 대부분 모델에서 캐시에서 읽은 토큰을 분당 입력 한도(ITPM)에 세지 않아요.
- 바로 답이 필요 없는 대량 작업은 각 회사의 배치 API 로 보내요. OpenAI Batch API · Anthropic Message Batches · Gemini Batch API 모두 일반 호출과 따로 한도를 둬요. 원포트엔 배치 주소가 없어서 각 회사 키로 직접 불러야 해요.
원포트 키에서는 이렇게 막혀요
원포트는 분당 요청 수 제한을 두지 않아요. 키마다 이번 달·오늘 금액 몫으로 막고, 스페이스에서는 그룹 몫으로도 막아요. 한 사람이 몫에 닿아도 나머지 키는 그대로 써요.
다만 모델 회사가 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 크레딧을 충전해요 |
이 키의 남은 몫 보기 — 그룹 몫은 나오지 않아요
curl https://oneport.kr/v1/key \
-H "Authorization: Bearer sk-so-..."
# "month"·"day" 마다 한도 · 쓴 양 · 남은 양 · 다시 열리는 시각(resets_at)더 자주 묻는 것
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 으로 읽어 남은 초를 구해요.
출처와 확인일
- OpenAI 요청 한도 가이드 · 2026년 10월 17일 확인
- Gemini API 요청 한도 · 2026년 10월 17일 확인
함께 보면 좋은 글과 도움말
- 개발 가이드클로드 코드·Cursor 팀 사용량 관리 — 개발자마다 키 나누고 상한 정하기개발팀이 Claude Code·Cursor 를 쓸 때 한 지갑에서 개발자마다 API 키를 나누고, 키마다 한 달·하루에 쓸 수 있는 양을 정해 두는 길이에요.
- 개발 가이드API 키 공유와 유출 — 팀원·외주에 나누고 새면 끄는 순서API 키 하나를 팀원·외주와 돌려 쓰면 누가 썼는지 갈리지 않고, 새면 모두가 같이 멈춰요. 사람마다 키를 나누는 기준, 새었을 때 끄고 바꾸는 순서, 외주 계약서에 붙일 키 사용 조건을 담았어요.
- 개발 가이드n8n·Dify AI 연결하기 — OpenAI 호환 주소·키·모델 설정n8n 과 Dify 의 OpenAI 호환 설정에 원포트 AI 주소와 키, 모델 이름을 넣는 칸을 정리하고, 실패할 때 다시 시도하는 법은 도구 쪽에서 정하도록 안내해요.
- 도움말 매뉴얼OpenAI API 결제 오류·영수증·한도OpenAI API 크레딧 결제가 거절되거나 잔액이 있는데 막힐 때 확인할 것, 영수증 찾는 곳, 원화로 사는 다른 길이에요.
- 도움말 매뉴얼Claude(클로드) API 결제가 안 될 때Claude 콘솔에서 크레딧 결제가 막히거나 카드가 거절될 때 확인할 것, 영수증 찾는 곳, 원화로 사는 다른 길이에요.