Claude API Error Codes: Fix 401, 429, 529 Fast
Every Claude API error code (4xx and 5xx) explained with causes and fixes. Plus how OpenAI-compat errors map. Real-talk debugging for vibe coders.
When Your Claude API Call Just Won't Work
You hit run, got a wall of red, now what.
Claude API errors are consistent and most have obvious fixes once you know what they mean. This post lists the codes you'll actually run into, the cause, and the fix.
If you're using aiapi.cheap, the same errors show up — we mirror Anthropic's response format faithfully. We also support OpenAI-format calls (one key for Claude, GPT, Gemini, Grok, DeepSeek), and at the bottom there's a quick map of how the two error styles relate.
For the official spec, Anthropic's error reference is the source of truth.
Error Code Cheat Sheet
| Code | Name | What it means | First thing to check |
| --- | --- | --- | --- |
| 400 | Bad Request | Your request body is broken | Required fields, JSON shape |
| 401 | Unauthorized | API key missing or wrong | Header name, key value, env var |
| 403 | Forbidden | Key valid but not allowed for this | Plan tier, model permission |
| 404 | Not Found | Wrong URL or unknown model name | Model ID typo, endpoint path |
| 413 | Payload Too Large | Request body is too big | Token count, attachment size |
| 429 | Too Many Requests | You're hitting limits | Backoff, slow down, upgrade plan |
| 500 | Server Error | Something broke upstream | Retry with backoff |
| 529 | Overloaded | API is swamped right now | Retry with backoff, wait longer |
400 — Bad Request
Cause: Your request body is malformed.
Common reasons:
model or messages fieldmax_tokens missing, zero, or negativemessages array is emptyuser or assistantFix: Log the full error body. Anthropic returns error.message describing what's wrong. 90% of 400 errors are obvious once you read that field.
try:
response = client.messages.create(...)
except anthropic.BadRequestError as e:
print(e.body) # exact reason is here
raiseValidate locally before sending — at minimum, check that model, max_tokens, and messages are all set.
401 — Unauthorized
Cause: API key is missing, wrong, or revoked.
Common reasons:
x-api-key (Anthropic) or Authorization: Bearer (OpenAI) headerFix: Print the key prefix (never the full value) to confirm what your code sends:
import os
key = os.environ.get("ANTHROPIC_API_KEY", "")
print(f"prefix: {key[:8]}... len: {len(key)}")If the prefix isn't sk-aic- (aiapi.cheap) or sk-ant- (direct Anthropic), wrong env var. Regenerate if the key is genuinely invalid.
403 — Forbidden
Cause: Key valid, but you're not allowed to do the thing.
Usually a model not enabled on your tier, an account flag, or a feature not on your plan.
Fix: Read the error message — it tells you what's restricted. Switch model or upgrade plan. On aiapi.cheap, all 5 vendors work on both plans, so 403 is rare and usually a model name typo.
404 — Not Found
Cause: URL or model doesn't exist. Usually a typo in the model ID or a deprecated model name.
Fix: Check the model overview or the docs for current model names.
413 — Payload Too Large
Cause: Request bigger than the API will accept (long context, huge images, giant tool defs).
Fix: Trim the prompt, split documents, downsize images, or switch to a model with a bigger context window.
429 — Too Many Requests
Cause: You're hitting fair-use limits. Most common runtime error. Not a bug — a guardrail.
Usually a burst of parallel requests, multiple services sharing one key, or a script firing as fast as it can.
Fix: Exponential backoff. Don't retry instantly. See the rate limits post for the full retry pattern with Retry-After handling. If 429s persist, batch smarter or upgrade plan tier — Pro has more headroom than Basic.
500 — Internal Server Error
Cause: Something broke upstream. Not your fault. Transient infrastructure hiccup or model timeout.
Fix: Retry with exponential backoff (1s, 2s, 4s). If it persists across many retries with different inputs, the issue may be vendor-side — check status pages. Don't retry tight in a loop or you'll turn a glitch into a thousand-error incident.
529 — Overloaded
Cause: Model capacity saturated. Anthropic-specific.
Fix: Same as 500 but be more patient. Wait longer between retries. If it persists, switch model — Sonnet often has more headroom than Opus during busy periods. On aiapi.cheap, this is a good reason to have a fallback ladder: try Claude, fall back to GPT or Gemini.
Retry Pattern That Works
A single backoff loop that handles 429, 500, and 529 cleanly:
import time
import random
import anthropic
client = anthropic.Anthropic(
api_key="sk-aic-your-key-here",
base_url="https://aiapi.cheap/api/proxy",
)
RETRYABLE = (
anthropic.RateLimitError,
anthropic.InternalServerError,
anthropic.APIStatusError, # catches 529 etc.
)
def safe_call(messages, model="claude-sonnet-4-6", max_attempts=5):
for attempt in range(max_attempts):
try:
return client.messages.create(
model=model,
max_tokens=1024,
messages=messages,
)
except RETRYABLE as e:
if attempt == max_attempts - 1:
raise
# Honor Retry-After if the API gave us one
wait = getattr(e, "retry_after", None)
if wait is None:
wait = (2 ** attempt) + random.uniform(0, 1)
print(f"Retry {attempt + 1}/{max_attempts} after {wait:.1f}s")
time.sleep(wait)Three things this does right:
1. Distinguishes retryable from non-retryable. A 401 won't get better with retries — fail loudly.
2. Honors the server's hint when Retry-After is sent.
3. Adds jitter so a fleet of clients doesn't retry in lockstep.
OpenAI-Format Errors on aiapi.cheap
If you use the OpenAI SDK on our /v1/chat/completions endpoint (routes to all 5 vendors via the model field), errors come back OpenAI-style. The codes map cleanly:
| HTTP code | Anthropic-style | OpenAI-style |
| --- | --- | --- |
| 400 | BadRequestError | BadRequestError |
| 401 | AuthenticationError | AuthenticationError |
| 403 | PermissionDeniedError | PermissionDeniedError |
| 404 | NotFoundError | NotFoundError |
| 429 | RateLimitError | RateLimitError |
| 500 | InternalServerError | APIError |
| 529 | APIStatusError (529) | APIError |
Rule of thumb: status code is what matters. The class name varies, but e.status_code is the same on both sides. Build retry logic around the status code, not the exception class, and your code works across both formats.
Debugging Habits That Pay Off
anthropic and openai SDKs have retry hooks built in — configure themQuick Recap
Keep this open in a tab the next time something explodes.