Published on

Anthropic Claude 429 과금·레이트리밋 해결법

Authors

서버에서 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를 프로덕션에서 훨씬 안정적으로 굴릴 수 있습니다.