Every error from the Claude API arrives as JSON with a top-level "type": "error", an error object containing a type and human-readable message, and a request_id. Every response — success or failure — also carries a request-id header; log it, because it is what Anthropic support will ask for. Here is the full taxonomy.
The taxonomy
| HTTP | Error type | Retryable? | What to do |
|---|---|---|---|
| 400 | invalid_request_error | No | Fix the request — the payload is malformed or uses an unsupported feature (also the catch-all for other unlisted 4xx) |
| 401 | authentication_error | No | Check the API key / credentials |
| 402 | billing_error | No | Fix billing state — expired card, exhausted credit |
| 403 | permission_error | No | The key lacks access to this resource — check workspace and key scopes |
| 404 | not_found_error | No | Wrong resource ID or a retired model — verify the model ID |
| 413 | request_too_large | No | Shrink the payload (Messages and Token Counting cap at 32 MB; Batch 256 MB; Files 500 MB) |
| 429 | rate_limit_error | Yes | Honor retry-after, back off, and read the rate-limit headers |
| 500 | api_error | Yes | Retry with exponential backoff |
| 504 | timeout_error | Yes | Retry; for long generations switch to streaming or the Batch API |
| 529 | overloaded_error | Yes | API-wide high traffic — back off, consider a less-loaded model, spread requests over time |
The rule compresses well: 429, all 5xx, and connection errors are retryable; every other 4xx means fix the request, not resend it. The official SDKs already implement this — they retry retryable errors twice by default with exponential backoff and honor the retry-after header — so before writing your own retry loop, check you aren't stacking one on top of the SDK's.
Handling errors in Python
The Python SDK raises typed exceptions; catch the most specific ones first, and use the .type property on APIStatusError subclasses when two error types share a status-code family (for example distinguishing billing_error from permission_error).
import anthropic
from anthropic import AnthropicAWS # SigV4; AWS_REGION + workspace ID env vars
client = AnthropicAWS()
try:
msg = client.messages.create(model="claude-opus-4-8",
max_tokens=1024, messages=history)
except anthropic.RateLimitError as e: # 429 — SDK already retried
schedule_retry(e.response.headers.get("retry-after"))
except anthropic.BadRequestError as e: # 400 — do NOT resend as-is
log_and_alert(e.type, e.message)
except anthropic.APIStatusError as e: # everything else, typed
route_on(e.status_code, e.type)
The 400s that don't look like your fault (but are fixable)
A cluster of invalid_request_error causes on the newest models catches migrating teams: assistant-message prefilling returns 400 on Claude Fable 5, Opus 4.8/4.7/4.6, and Sonnet 4.6 (use structured outputs instead); non-default temperature, top_p, or top_k returns 400 on Fable 5, Opus 4.8/4.7, and Sonnet 5; and modifying, reordering, or filtering thinking blocks before resending them returns 400 — pass them back exactly as received. One notable configuration trap: Claude Fable 5 requires 30-day data retention, so an organization set to Zero Data Retention gets a 400 on every Fable 5 request regardless of payload. If a whole model is failing, check org retention settings before debugging request bodies.
Streaming, timeouts, and 3P differences
Two operational wrinkles. First, on streaming requests an error can occur after the 200 status has been sent; it arrives as an in-stream error event (a mid-stream overloaded_error is equivalent to HTTP 529), so stream consumers need an error branch too. Second, for requests that would run longer than about 10 minutes, use streaming or the Batch API — the SDKs validate this for non-streaming calls.
This taxonomy is the Claude API's own, shared by Claude Platform on AWS. Amazon Bedrock, Google Vertex AI, and Microsoft Foundry wrap Claude in their own service front doors, so throttling and auth errors there surface in each cloud's native error format — same retry logic, different envelope; check your provider's documentation for the mapping.
Where to go next
Rate-limit errors deserve their own playbook — see rate limit headers. For the non-error signals in successful responses, read stop reasons, and for Bedrock-specific throttling, Bedrock throttling handling.