Claude Max 플랜으로 API 호출하면 429가 뜨는 이유 — 인증 체계 5단계 완전 정리
📚 1인 인프라 구축기 시리즈 (7편)
Claude Max 플랜의 OAuth 토큰(sk-ant-oat)으로 Messages API를 직접 호출하면 429 Rate Limit이 뜹니다. Claude Code의 인증 우선순위 5단계, sk-ant-oat vs sk-ant-api 차이, 그리고 스크립트에서 Max 플랜을 활용하는 우회법을 실제 트러블슈팅 사례와 함께 정리합니다.
💡 Tip. 바쁜 현대인들을 위한 본문 요약
Claude Max 플랜($100/월)을 구독 중인데, Messages API를 직접 호출하면 429 Rate Limit 에러가 뜬다. 이유는 Max 플랜의 OAuth 토큰(
sk-ant-oat)은 CLI 전용이고, 직접 API 호출에는 Console API 키(sk-ant-api)가 별도로 필요하기 때문이다. 이 글에서는 Claude Code의 인증 우선순위 5단계, 두 토큰의 차이, 그리고 Max 플랜만으로 스크립트를 돌리는 우회법을 정리한다.
문제 상황: Max 플랜인데 API가 거부한다
블로그 자동화 파이프라인에서 키워드 필터링용으로 Anthropic Messages API를 직접 호출했다. fetch로 api.anthropic.com/v1/messages에 요청을 보내는 간단한 코드다.
const res = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": process.env.ANTHROPIC_API_KEY, // ← 여기가 문제
"anthropic-version": "2023-06-01",
},
body: JSON.stringify({
model: "claude-sonnet-4-6",
max_tokens: 4096,
messages: [{ role: "user", content: prompt }],
}),
});
Max 플랜을 구독 중이니까 당연히 될 줄 알았다. 결과는:
{
"type": "error",
"error": {
"type": "rate_limit_error",
"message": "Error"
}
}
HTTP 429. Rate limit에 걸렸다.
첫 요청부터 429라는 게 이상했다. Rate limit이면 여러 번 호출한 뒤에 걸려야 하는데, 단 1회 호출에 즉시 거부당했다. 이건 rate limit이 아니라 인증 자체가 거부된 것이다.
원인 분석: 토큰이 두 종류다

macOS Keychain에 저장된 토큰을 확인해봤다.
echo "$ANTHROPIC_API_KEY" | head -c 15
# sk-ant-oat01-...
sk-ant-oat. 이게 범인이었다.
Anthropic의 API 토큰은 프리픽스로 용도가 구분된다.
| 프리픽스 | 정식 명칭 | 발급처 | 용도 |
|---|---|---|---|
sk-ant-api | Console API Key | console.anthropic.com | REST API 직접 호출 (x-api-key 헤더) |
sk-ant-oat | Subscription OAuth Token | Claude.ai 로그인 (Pro/Max) | Claude Code CLI 전용 (내부 인증) |
Max 플랜의 OAuth 토큰은 CLI를 통해서만 유효하다. claude -p 명령을 실행하면 CLI 내부에서 이 토큰으로 Anthropic 서버와 통신하지만, 이 토큰을 꺼내서 x-api-key 헤더에 직접 넣으면 서버가 거부한다.
Claude Code 인증 우선순위 5단계

Claude Code 공식 문서에 명시된 인증 우선순위(Authentication Precedence)는 다음과 같다.
| 우선순위 | 인증 방식 | 환경변수/설정 | API 직접 호출 |
|---|---|---|---|
| 1 | Cloud Provider | CLAUDE_CODE_USE_BEDROCK 등 | ✅ (해당 클라우드 API) |
| 2 | Auth Token | ANTHROPIC_AUTH_TOKEN | ⚠️ (프록시/게이트웨이) |
| 3 | API Key | ANTHROPIC_API_KEY (sk-ant-api) | ✅ 직접 호출 가능 |
| 4 | apiKeyHelper | 스크립트 출력값 | ✅ (스크립트가 API 키 반환 시) |
| 5 | Subscription OAuth | /login으로 발급 (sk-ant-oat) | ❌ CLI 전용 |
핵심은 3번과 5번의 차이다.
- 3번
ANTHROPIC_API_KEY: Console에서 발급한 정식 API 키.x-api-key헤더로api.anthropic.com에 직접 요청 가능. 사용한 만큼 과금 (종량제). - 5번 Subscription OAuth: Max/Pro 구독 시
/login으로 자동 발급되는 토큰. CLI 내부에서만 유효. 직접 API 호출 불가.
그리고 공식 문서에는 이런 경고가 있다:
If you have an active Claude subscription but also have
ANTHROPIC_API_KEYset in your environment, the API key takes precedence once approved. This can cause authentication failures if the key belongs to a disabled or expired organization.
환경변수에 API 키가 있으면 구독 토큰보다 우선한다. 즉, 두 인증이 공존하면 예상과 다르게 동작할 수 있다.
해결: 세 가지 선택지

선택지 A: Console API 키 발급 (종량제)
가장 정석적인 방법. console.anthropic.com에서 API 키를 발급받으면 직접 호출이 가능하다.
# Console에서 발급한 키
export ANTHROPIC_API_KEY="sk-ant-api03-..."
# 직접 호출 가능
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{ "model": "claude-sonnet-4-6", "max_tokens": 1024, "messages": [{"role":"user","content":"Hello"}] }'
비용: Sonnet 4.6 기준 input $3/MTok, output $15/MTok. 키워드 필터 1회 호출이면 ~$0.01 수준.
단점: Max 플랜과 별도 과금. 월 $100 내고 있는데 또 돈이 나간다.
선택지 B: claude -p subprocess 래퍼 (무료)
Max 플랜의 사용량 안에서 처리하려면, CLI를 subprocess로 호출해야 한다.
import { spawn } from "node:child_process";
function callClaude(prompt: string, systemPrompt: string): Promise<string> {
return new Promise((resolve, reject) => {
// ⚠️ 환경변수 정리 — 중첩 세션 방지
const env = { ...process.env };
delete env.ANTHROPIC_API_KEY; // API 키가 있으면 구독 대신 이걸 씀
delete env.CLAUDE_CODE_USE_BEDROCK; // 클라우드 프로바이더 우선순위 차단
const child = spawn("claude", [
"-p",
"--model", "claude-sonnet-4-6",
"--system-prompt", systemPrompt,
], { env, stdio: ["pipe", "pipe", "pipe"] });
const chunks: Buffer[] = [];
child.stdout.on("data", (c) => chunks.push(c));
child.stdin.write(prompt);
child.stdin.end();
child.on("close", (code) => {
if (code !== 0) reject(new Error(`exit ${code}`));
else resolve(Buffer.concat(chunks).toString("utf-8"));
});
});
}
주의사항이 3가지 있다.
| 주의 | 내용 |
|---|---|
| 환경변수 정리 | ANTHROPIC_API_KEY가 환경에 있으면 CLI가 구독 대신 해당 키를 사용한다. subprocess에서는 명시적으로 삭제해야 한다 |
| stdin pipe hang | 프롬프트가 길면(8KB+) stdin pipe가 불안정해질 수 있다. 타임아웃 필수 |
| 중첩 세션 차단 | OpenClaw 등 에이전트 프레임워크 내부에서 claude -p를 호출하면 CLAUDE_CODE_* 환경변수가 중첩 세션을 유발한다. 전부 삭제해야 한다 |
선택지 C: claude auth login --console (전환)
CLI의 인증 자체를 Console 기반으로 전환하는 방법.
claude auth login --console
이렇게 하면 CLI가 구독 OAuth 대신 Console API 키로 인증한다. 이후 claude -p로 호출해도 API 종량 과금이 된다. Max 플랜의 무제한 사용량을 포기하는 셈이므로 신중하게 선택해야 한다.
비교 정리

| Console API (A) | subprocess (B) | Console 전환 (C) | |
|---|---|---|---|
| 직접 fetch 호출 | ✅ | ❌ | ✅ |
| Max 플랜 사용량 | ❌ 별도 과금 | ✅ 구독 내 | ❌ 별도 과금 |
| 안정성 | ✅ 높음 | ⚠️ stdin hang 위험 | ✅ 높음 |
| 비용 | 종량제 | 무료 | 종량제 |
| 구현 난이도 | 낮음 | 중간 | 낮음 |
실전에서 내가 선택한 방법
나는 B + 타임아웃 폴백 조합을 선택했다.
claude -p (120초 제한) → 성공: 정상 처리
→ 실패: 전체 키워드 통과 (폴백)
이유는 단순하다.
- 이미 Max 플랜을 쓰고 있다. 별도 API 비용을 내고 싶지 않다.
- 필터 실패 시 치명적이지 않다. AI 필터가 실패하면 키워드가 전부 통과되지만, 뒤에 중복 제거 단계가 있어서 품질이 크게 떨어지지 않는다.
- 타임아웃을 120초로 설정했다.
claude -p가 hang되면 120초 후 SIGTERM → 3초 후 SIGKILL로 확실하게 종료한다.
// SIGTERM 후 3초 뒤 SIGKILL — hang된 프로세스 확실히 제거
const timer = setTimeout(() => {
child.kill("SIGTERM");
setTimeout(() => child.kill("SIGKILL"), 3000);
reject(new Error("타임아웃"));
}, 120_000);
이 구조로 크론 잡이 블로킹되지 않으면서, Max 플랜의 사용량 안에서 무료로 LLM을 활용할 수 있게 됐다.
핵심 정리

| 토큰 프리픽스 | 발급처 | 직접 API 호출 | CLI 사용 | 과금 |
|---|---|---|---|---|
sk-ant-api | Console | ✅ | ✅ (우선순위 3) | 종량제 |
sk-ant-oat | Claude.ai 로그인 | ❌ (429) | ✅ (우선순위 5) | 구독 내 |
기억할 것 하나: Max 플랜 ≠ API 무제한. Max 플랜은 CLI와 웹에서만 유효하고, REST API 직접 호출은 별도 Console API 키가 필요하다.
📚 1인 인프라 구축기 시리즈 (7편)
- 1. Oracle ARM + Docker로 WordPress 4사이트 운영하기
- 2. Cloudflare Full Strict SSL + Nginx 리버스 프록시 삽질 총정리
- 3. CouchDB + Obsidian LiveSync로 메모 동기화 구축하기
- 4. Umami 셀프호스팅 — Docker 설치부터 AdBlock 우회까지
- 5. GitHub Actions + Cloudflare Pages 자동 배포 — Astro 블로그 CI/CD
- 6. WordPress → Astro 마이그레이션 — 블로그 전환 실전 삽질기
- 7. Claude Max 플랜으로 API 호출하면 429가 뜨는 이유 — 인증 체계 5단계 완전 정리