- Published on
Anthropic Claude 429 과금·레이트리밋 해결법
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버에서 Anthropic Claude를 붙여두고 트래픽이 조금만 늘어도 429가 터지면, 대부분은 “레이트리밋이겠지”라고만 생각하고 무작정 재시도(backoff)부터 넣습니다. 하지만 Claude의 429는 과금/크레딧/조직 설정 문제와 레이트리밋(요청·토큰·동시성) 초과가 같은 코드로 나타나거나, 메시지/헤더에만 힌트가 남는 경우가 있어 원인을 분리하지 않으면 해결이 어렵습니다.
이 글에서는 429를 (1) 과금 계열, (2) 레이트리밋 계열, (3) 구현/운영 계열로 나눠서 진단하고, 재발을 막는 아키텍처(큐·세마포어·토큰 버짓·캐시·서킷브레이커)까지 정리합니다.
> 참고로 429는 OpenAI에서도 “TPM만 넘어서가 아닌” 다양한 원인이 있습니다. 원인 분해 접근법은 이 글도 도움이 됩니다: OpenAI Responses API 429인데 TPM만 넘는 6가지 원인
1) Claude 429를 먼저 ‘유형’으로 분류하기
1-1. 과금/크레딧/플랜 문제로 인한 429
다음 경우는 트래픽이 적어도 429가 날 수 있습니다.
- 크레딧 소진 / 결제수단 문제 / 인보이스 미납
- 조직(Organization) 단위의 사용 제한: 예산 한도, 승인되지 않은 프로젝트/키
- 키가 비활성화/회수/권한 부족: 키가 살아있어도 특정 모델 접근이 막히면 유사한 증상
특징:
- 갑자기 “모든 요청이” 429로 바뀜
- 재시도해도 절대 회복되지 않음(일시적 스파이크가 아니라 상태 문제)
대응:
- Anthropic 콘솔에서 Billing / Usage / Limits를 확인
- CI/CD에서 키가 바뀌었는지, 런타임에 다른 키를 물고 있는지 점검
- 멀티 환경(스테이징/프로덕션)에서 같은 키를 공유하는지 확인
1-2. 레이트리밋(요청/토큰/동시성) 초과로 인한 429
Claude는 보통 다음 축에서 제한이 걸립니다.
- RPS/RPM(요청 수): 짧은 프롬프트라도 동시에 몰리면 초과
- TPM(토큰 처리량): 긴 컨텍스트/긴 출력이 겹치면 초과
- 동시성(Concurrent requests): 스트리밍을 오래 붙잡으면 동시성 점유 시간이 길어짐
특징:
- 특정 시간대에만 발생(피크 트래픽)
- 짧은 요청은 성공하고, 긴 요청에서만 실패
- 스트리밍/툴 호출/대형 컨텍스트에서 악화
1-3. 구현/운영 이슈가 429를 ‘증폭’시키는 경우
429 자체 원인은 레이트리밋이지만, 아래가 있으면 실패가 폭발합니다.
- 무제한 동시 재시도: 모든 워커가 동시에 backoff 후 재시도 → 다시 429(Thundering herd)
- 스트리밍 연결 관리 실패: 클라이언트가 끊겼는데 서버는 upstream을 계속 물고 있어 동시성 낭비
- 프록시 버퍼링/타임아웃: SSE가 중간에서 끊기며 재시도 루프 유발
SSE/프록시 이슈는 429뿐 아니라 499/502로도 같이 터지기 쉬워서, 스트리밍 운영 중이면 이 체크리스트도 같이 보는 걸 권합니다: LLM SSE 스트리밍 499 502 급증과 응답 끊김을 잡는 프록시 튜닝 체크리스트
2) 로그에서 429의 ‘진짜 원인’ 뽑아내기
2-1. 응답 바디/에러 메시지를 그대로 저장
대부분의 SDK는 예외 메시지에 중요한 힌트를 포함합니다. 최소한 아래를 구조화해 남기세요.
- HTTP status (=429)
- error type / message
- request id(있다면)
- 모델명, max_tokens, 입력 토큰 추정치
- 재시도 횟수, backoff 시간
Node.js 예시(에러 로깅 + 원인 태깅)
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
function classify429(err) {
const msg = (err?.message || "").toLowerCase();
// SDK/버전에 따라 필드명이 다를 수 있어 message 기반 휴리스틱도 병행
if (msg.includes("credit") || msg.includes("billing") || msg.includes("payment")) {
return "billing";
}
if (msg.includes("rate limit") || msg.includes("too many") || msg.includes("throttle")) {
return "rate_limit";
}
return "unknown";
}
export async function callClaude(prompt) {
try {
const res = await client.messages.create({
model: "claude-3-5-sonnet-latest",
max_tokens: 800,
messages: [{ role: "user", content: prompt }],
});
return res;
} catch (err) {
const status = err?.status || err?.response?.status;
if (status === 429) {
const type = classify429(err);
console.error("Claude 429", {
type,
status,
message: err?.message,
requestId: err?.request_id || err?.response?.headers?.get?.("request-id"),
});
}
throw err;
}
}
핵심은 “429면 재시도”가 아니라, 429를 원인별로 태깅해 대시보드에서 비율을 보는 겁니다. billing 계열이 섞여 있으면 재시도는 비용만 늘리고 장애를 길게 만듭니다.
2-2. 토큰 사용량을 요청 단위로 관측
TPM 초과는 “요청 수”가 아니라 “토큰 총량”이 원인입니다. 다음을 기록하세요.
- 입력 토큰(추정치라도)
- 출력 토큰 상한(
max_tokens) - 실제 출력 토큰(응답 메타에 있으면)
이 데이터가 있어야 “프롬프트 길이 2배 → 429 5배” 같은 상관관계를 잡습니다.
3) 해결책 A: 과금/플랜 계열 429를 없애는 체크리스트
3-1. 키/조직/환경 분리
- 프로덕션과 스테이징 키를 분리하고, 스테이징 트래픽이 프로덕션 한도를 갉아먹지 않게 합니다.
- 키 로테이션 시점에 구 키가 남아있는 서버가 없는지 확인합니다.
3-2. 소프트 리밋(예산) + 하드 리밋(차단) 설계
과금 문제는 “장애”이기도 하지만, 더 큰 문제는 비정상 트래픽이 비용을 폭발시키는 것입니다.
- 사용자/조직별 일일 토큰 예산
- 특정 기능(예: 긴 문서 요약, 대형 RAG)의 상한
- 예산 초과 시 graceful degradation(모델 다운그레이드, 요약 길이 축소, 캐시 응답)
4) 해결책 B: 레이트리밋 429를 구조적으로 줄이는 방법
4-1. 동시성 제한(세마포어)로 ‘한도 내에서만’ 보내기
가장 효과가 큽니다. 특히 스트리밍은 요청이 오래 살아 있어 동시성 점유가 길어집니다.
Node.js: p-limit로 동시성 캡
import pLimit from "p-limit";
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const limit = pLimit(Number(process.env.CLAUDE_CONCURRENCY ?? 5));
export function callClaudeLimited(payload) {
return limit(() => client.messages.create(payload));
}
- 동시성 캡은 “429를 피하는 것”뿐 아니라, 지연시간 분산과 장애 전파 차단에도 유리합니다.
- 동시성 값은 계정 한도와 평균 응답 시간을 기반으로 튜닝합니다(예: 평균 5초 응답, 5 동시성이면 대략 1초당 1요청 처리).
4-2. 재시도는 ‘지수 백오프 + 지터 + 상한’이 기본
429 재시도는 반드시 아래 3가지를 갖춰야 합니다.
- exponential backoff
- jitter(랜덤)
- max retries / max elapsed time
TypeScript: 429만 조건 재시도
function sleep(ms: number) {
return new Promise((r) => setTimeout(r, ms));
}
export async function retryOn429<T>(fn: () => Promise<T>) {
const maxRetries = 5;
let base = 300; // ms
for (let i = 0; i <= maxRetries; i++) {
try {
return await fn();
} catch (e: any) {
const status = e?.status ?? e?.response?.status;
if (status !== 429 || i === maxRetries) throw e;
const jitter = Math.floor(Math.random() * 200);
const delay = Math.min(5000, base * 2 ** i) + jitter;
await sleep(delay);
}
}
throw new Error("unreachable");
}
주의:
- billing 계열 429는 재시도해도 안 됩니다. 앞에서 분류/태깅이 필요한 이유입니다.
4-3. 토큰 버짓(입력/출력) 제어로 TPM을 낮추기
TPM 초과는 “긴 컨텍스트 + 큰 max_tokens + 동시 요청”의 곱으로 터집니다.
실전 팁:
max_tokens를 무조건 크게 잡지 말고, UX 요구사항에 맞춰 단계별로 분리- 1차: 짧은 답(예: 200~400 tokens)
- 2차: 사용자가 ‘자세히’ 눌렀을 때만 확장 생성
- 긴 문서를 통째로 넣지 말고, 요약/청크/RAG top-k 축소
- 시스템 프롬프트/정책 텍스트를 매번 길게 넣는다면 압축/공통화 고려
4-4. 캐시로 “동일 요청”을 줄이기
429는 결국 “요청량” 문제이므로, 캐시는 정공법입니다.
- 동일 입력(프롬프트 + 모델 + 파라미터) 해시 기반 캐시
- RAG라면 검색 결과까지 포함해 캐시 키를 설계
- TTL은 짧게(예: 1~10분) 잡아도 피크 방어에 효과
4-5. 큐(비동기)로 피크를 평탄화
실시간이 꼭 필요 없는 작업(리포트 생성, 대량 요약, 배치 태깅)은 큐로 넘기세요.
- API 서버는 요청을 받아 Job 생성 → 즉시 202 반환
- 워커가 레이트리밋에 맞춰 처리(동시성 캡 + 재시도)
이 구조는 429를 “사용자-facing 장애”에서 “백그라운드 지연”으로 바꿉니다.
5) 스트리밍(SSE)에서 429가 더 자주 보이는 이유와 대응
스트리밍은 요청이 길게 유지되므로:
- 동시성 점유 시간이 길어져 동시성 제한을 먼저 초과
- 프록시/로드밸런서가 연결을 끊으면 클라이언트가 재시도 → 요청 수 증가
대응 체크:
- 서버가 클라이언트 disconnect를 감지하면 upstream도 즉시 중단(abort)
- Nginx/Envoy에서 SSE 버퍼링 비활성화, idle timeout 조정
- HTTP/2 환경에서 TTFB/버퍼링 설정 점검
관련 운영 튜닝은 위에서 소개한 글(LLM SSE 스트리밍 499 502…)의 체크리스트를 그대로 적용해도 많은 문제가 정리됩니다.
6) 장애 대응 플레이북: 429가 터졌을 때 10분 안에 볼 것
6-1. “전 요청 429”인가, “일부만 429”인가
- 전 요청 429 + 지속: billing/키/조직 설정 가능성 ↑
- 피크 타임에만: rate limit 가능성 ↑
6-2. 긴 요청에서만 실패하는가
- 긴 컨텍스트/큰
max_tokens에서만 실패: TPM 병목 가능성 ↑
6-3. 재시도 폭주가 있는가
- 429 직후 RPS가 더 증가하면 재시도 설계 문제
- 재시도는 반드시 지터 + 상한
6-4. 임시 완화책(즉시 적용)
- 동시성 캡을 낮추기(환경변수로 조절 가능하게)
max_tokens를 낮추기- “자세히 보기” 같은 2단계 생성으로 전환
- 캐시 TTL을 늘려 동일 요청을 흡수
7) 운영 지표(Observability)로 429를 ‘예방’하기
다음 4가지를 대시보드로 만들면 429는 사후 대응이 아니라 사전 예방이 됩니다.
- 분당 요청 수(RPM) / 동시 요청 수
- 입력 토큰/출력 토큰 분포(p50/p95)
- 429 비율(원인 태깅: billing vs rate_limit vs unknown)
- 재시도 횟수/큐 적체(백로그)
특히 “토큰 분포”를 보면, 상위 5%의 초장문 요청이 전체 TPM을 잡아먹는 경우가 많습니다. 이때는 전체를 튜닝하기보다 문제 요청을 분리(청크/요약/비동기) 하는 게 효과적입니다.
8) 정리: 429는 ‘재시도’가 아니라 ‘설계’로 해결한다
- billing 계열 429: 재시도 금지. 콘솔/조직/키/예산을 먼저 확인
- rate limit 계열 429: 동시성 캡 + 지수 백오프(지터) + 토큰 버짓 제어가 핵심
- 스트리밍 운영: 연결/프록시 튜닝이 동시성·재시도 폭주를 막는다
- 관측 가능성: 429를 유형별로 태깅하고, 토큰 분포/동시성/재시도를 지표화
429는 “한도를 늘리면 끝”인 문제가 아니라, 트래픽이 성장할수록 반복되는 구조적 병목입니다. 위의 분류-관측-제어(동시성/토큰/큐/캐시)까지 갖추면, Claude를 프로덕션에서 훨씬 안정적으로 굴릴 수 있습니다.