Claude API(앤스로픽이 제공하는 Claude 호출용 인터페이스) 요청이 실패했을 때, 공식 Python·TypeScript SDK는 오류 종류에 따라 서로 다른 이름의 예외를 던지고, 일부는 자동으로 재시도까지 해 줍니다. 이 글에서는 어떤 오류가 어떤 예외로 나타나는지, 어떤 오류만 자동 재시도되는지, 재시도 횟수와 타임아웃(요청 응답을 기다리는 최대 시간)을 어떻게 바꾸는지 예제 코드와 함께 살펴봅니다. 읽고 코드에 바로 적용하는 데 약 10분이면 충분합니다.
🟢 현행 모델 라인업 일치 · 모델 안내 · Fable 구독 안내
Fable 5·5.1 구독 안내(2026년 9월 7일 갱신): 2026년 9월 1일 출시된 Claude Fable 5.1이 현행 Fable 모델이고, Fable 5는 레거시로 전환됐습니다. 구독 조건은 두 모델이 같습니다 — Max·Team Premium 플랜은 주간 사용 한도의 50% 범위에서 포함되고, Pro·Team Standard 플랜은 사용량 크레딧(입력 100만 토큰당
이 글로 할 수 있게 되는 것
이 글을 따라오면 어떤 Claude API 오류가 재시도 대상인지 정확히 구분하고, 상황에 맞게 재시도 횟수(max_retries)와 타임아웃(timeout)을 조정할 수 있게 됩니다. 이미 client.messages.create()로 기본 요청을 성공시켜 본 상태를 전제로 합니다.
시작 전 준비물
- Python 3.9 이상 또는 Node.js 20 LTS 이상 — 각 SDK의 최소 요구 버전입니다.
- SDK 설치 — Python은
pip install anthropic, TypeScript는npm install @anthropic-ai/sdk. - 발급된 Claude API 키(환경변수
ANTHROPIC_API_KEY에 설정)와, 이미 한 번은 성공한messages.create()호출.
용어 빠른 풀이
- APIError — SDK가 실패한 요청을 표현할 때 쓰는 예외의 최상위 클래스입니다.
- APIStatusError — API가 400·404·500 같은 명확한 HTTP 상태 코드와 함께 오류를 응답했을 때의 예외로, 아래에 상태 코드별 하위 클래스가 있습니다.
- APIConnectionError — 네트워크 문제 등으로 서버에 연결 자체가 안 됐을 때의 예외입니다(상태 코드가 없는 경우).
- 지수 백오프(exponential backoff) — 재시도할 때마다 대기 시간을 점점 늘려가며 다시 시도하는 방식입니다.
- max_retries — 자동 재시도의 최대 횟수를 정하는 옵션입니다.
- timeout — 응답을 기다리는 최대 시간을 정하는 옵션입니다.
- with_options() / 두 번째 인자 — 클라이언트 전체가 아니라 특정 요청 하나에만 다른 설정을 적용하는 방법입니다. Python은
with_options(), TypeScript는 요청 메서드의 두 번째 인자를 씁니다.
APIError 타입별로 어떻게 다른가요?
Claude API가 오류를 반환하면 Python·TypeScript SDK는 HTTP 상태 코드에 따라 서로 다른 이름의 타입 예외를 던지며, 두 언어 모두 예외 클래스 이름이 동일합니다.
| 상태 코드 | 예외 클래스 | 의미 |
|---|---|---|
| 400 | BadRequestError | 요청의 형식이나 내용에 문제가 있음 |
| 401 | AuthenticationError | API 키가 잘못됐거나 만료됨 |
| 403 | PermissionDeniedError | 해당 리소스에 대한 권한이 없음 |
| 404 | NotFoundError | 요청한 리소스를 찾을 수 없음 |
| 409 | ConflictError | 리소스의 현재 상태와 요청이 충돌함 |
| 422 | UnprocessableEntityError | 처리할 수 없는 요청 내용 |
| 429 | RateLimitError | 계정의 요청 한도를 초과함 |
| 500 이상 | InternalServerError | Anthropic 서버 내부 오류 |
| 해당 없음 | APIConnectionError | 서버에 연결 자체가 되지 않음(네트워크 문제 등) |
Python에서는 이렇게 구체적인 예외부터 먼저 잡습니다.
import anthropic
try:
message = client.messages.create(
max_tokens=1024,
messages=[{"role": "user", "content": "안녕, Claude"}],
model="claude-sonnet-5",
)
except anthropic.APIConnectionError as e:
print("서버에 연결할 수 없습니다")
print(e.__cause__)
except anthropic.RateLimitError as e:
print("429 상태 코드 — 잠시 후 다시 시도해야 합니다")
except anthropic.APIStatusError as e:
print("그 외 상태 코드 오류")
print(e.status_code)
TypeScript에서는 err instanceof로 같은 방식의 분기를 만듭니다.
const message = await client.messages
.create({
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-sonnet-5"
})
.catch((err) => {
if (err instanceof Anthropic.APIError) {
console.log(err.status); // 예: 429
console.log(err.name); // 예: RateLimitError
} else {
throw err;
}
});
주의: APIError(최상위 클래스)를 가장 먼저 잡으면 그 아래의 구체적인 분기(예: RateLimitError만 다르게 처리하기)가 절대 실행되지 않습니다. 항상 더 구체적인 예외를 먼저 잡아야 합니다.
어떤 에러가 자동으로 재시도되나요?
연결 오류, 408 요청 시간 초과, 409 충돌, 429 요청 한도 초과, 500 이상 서버 오류는 SDK가 지수 백오프 방식으로 자동 재시도하며, 기본값은 최대 2회입니다. 서버가 retry-after 응답 헤더를 보내면 그 값을 존중해 대기 시간을 조정합니다. 이 목록에 없는 오류(예: 400·401·403·404·422)는 재시도 없이 즉시 예외가 발생합니다.
재시도 횟수는 클라이언트를 만들 때 기본값을 바꾸거나, 요청 하나에만 다르게 지정할 수 있습니다.
# Python — 클라이언트 전체 기본값 변경
client = Anthropic(
max_retries=0, # 기본값은 2
)
# 요청 하나에만 다르게 지정
client.with_options(max_retries=5).messages.create(
max_tokens=1024,
messages=[{"role": "user", "content": "안녕, Claude"}],
model="claude-sonnet-5",
)
// TypeScript — 클라이언트 전체 기본값 변경
const client = new Anthropic({
maxRetries: 0 // 기본값은 2
});
// 요청 하나에만 다르게 지정
await client.messages.create(
{
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-sonnet-5"
},
{ maxRetries: 5 }
);
기본 타임아웃은 얼마이고 어떻게 바꾸나요?
Python과 TypeScript SDK 모두 기본 타임아웃은 10분입니다. 타임아웃이 발생하면 Python은 APITimeoutError, TypeScript는 APIConnectionTimeoutError를 던지며, 타임아웃으로 실패한 요청도 앞서 설명한 기본 재시도 정책의 대상이 됩니다(즉 최대 2회까지 자동으로 다시 시도됩니다).
# Python — 전체 기본값 변경
import httpx
from anthropic import Anthropic
client = Anthropic(
timeout=20.0, # 20초 (기본값은 10분)
)
# 더 세밀한 제어 (연결·읽기·쓰기 시간을 각각 지정)
client = Anthropic(
timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
)
# 요청 하나에만 다르게 지정
client.with_options(timeout=5.0).messages.create(
max_tokens=1024,
messages=[{"role": "user", "content": "안녕, Claude"}],
model="claude-sonnet-5",
)
// TypeScript — 전체 기본값 변경
const client = new Anthropic({
timeout: 20 * 1000 // 20초 (기본값은 10분)
});
// 요청 하나에만 다르게 지정
await client.messages.create(
{
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-sonnet-5"
},
{ timeout: 5 * 1000 }
);
TypeScript SDK는 스트리밍(응답을 조각조각 실시간으로 받는 방식)을 쓰지 않으면서 max_tokens를 크게 잡은 경우, 아래 공식으로 타임아웃을 최대 60분까지 자동으로 늘립니다.
const minimum = 10 * 60;
const calculated = (60 * 60 * maxTokens) / 128_000;
return calculated < minimum ? minimum * 1000 : calculated * 1000;
주의: 스트리밍을 쓰지 않는 요청이 약 10분을 넘길 것으로 예상되면, Python은 ValueError를 미리 던지고 TypeScript도 동일한 취지의 오류를 던져 요청을 막습니다. stream=True(TypeScript는 stream: true)로 스트리밍을 켜거나, timeout 값을 직접 늘려서 이 사전 차단을 해제할 수 있습니다.
자주 막히는 지점 & 해결
1. "연결할 수 없다"는 오류가 반복될 때
원인: APIConnectionError는 상태 코드 없이 네트워크·방화벽·프록시 문제로 서버에 아예 닿지 못했을 때 발생합니다. 조치: SDK가 자동으로 재시도하지만, 계속 반복된다면 base_url 설정과 사내 프록시·방화벽 정책부터 확인하세요.
2. 큰 max_tokens에서 요청이 시작도 하기 전에 실패할 때
원인: 스트리밍 없이 max_tokens를 크게 설정해 예상 소요 시간이 약 10분을 넘기면 SDK가 사전에 요청을 막습니다. 조치: client.messages.stream()으로 스트리밍을 쓰거나, timeout 옵션을 명시적으로 늘리세요.
3. 429(RateLimitError)만 따로 처리하고 싶은데 항상 같은 분기로 빠질 때
원인: APIError나 APIStatusError 같은 상위 클래스를 먼저 잡으면 그 아래의 구체적인 예외 분기가 실행되지 않습니다. 조치: 가장 구체적인 예외(예: RateLimitError)를 먼저 잡고, 상위 클래스는 마지막에 두세요.
응용/다음 단계
여기까지 적용했다면, 다음으로는 요청이 실패했을 때 응답 헤더의 request-id 값을 함께 로그로 남겨 두는 것을 권합니다. 각 응답 객체의 _request_id 속성으로 바로 읽을 수 있고, Anthropic 지원팀에 문의할 때 이 값이 있으면 훨씬 빠르게 원인을 확인받을 수 있습니다. 응답 시간이 긴 요청을 자주 다룬다면, 스트리밍 Messages API로 전환해 타임아웃 자체를 피하는 방법도 함께 검토해 보세요.
자주 묻는 질문
Q. 재시도는 기본적으로 몇 번 실행되나요?
기본값은 2회입니다. 연결 오류·408·409·429·500 이상 오류에 한해 지수 백오프로 대기 시간을 늘려가며 재시도하며, max_retries 옵션으로 늘리거나 0으로 꺼서 즉시 예외를 받을 수 있습니다.
Q. 타임아웃 기본값을 넘기면 어떤 예외가 발생하나요?
Python은 APITimeoutError, TypeScript는 APIConnectionTimeoutError가 발생합니다. 두 경우 모두 기본 재시도 정책의 대상이라 자동으로 다시 시도됩니다.
Q. RateLimitError(429)를 만나면 직접 재시도 로직을 짜야 하나요?
따로 짤 필요는 없습니다. SDK가 이미 자동으로 재시도하기 때문입니다. 다만 반복적으로 발생한다면 요청 빈도를 낮추거나 사용량 한도를 확인하는 것이 근본적인 해결책입니다.
Q. 특정 요청 하나에만 다른 타임아웃·재시도 값을 주고 싶다면?
클라이언트 전체 기본값은 그대로 두고 개별 요청만 다르게 설정할 수 있습니다. Python은 with_options(), TypeScript는 요청 메서드의 두 번째 인자로 재정의합니다.