Claude API Error Code Reference: Full Guide to Fixes
When your Claude API integration fails, the error code tells you almost everything you need to fix it. This reference covers every HTTP status code Claude's API returns, what triggers each one, and the exact steps to resolve it — whether you're calling Claude directly or through a proxy like SubToAPI.
The short answer: Claude API errors follow standard HTTP status code conventions. 400 means your request is malformed, 401 means your API key is invalid, 429 means you're rate limited, and 5xx means something failed on the server side. Below is the full breakdown with causes and fixes for each.
The Core Error Codes
400 — Invalid Request Error
This is the most common error and usually the easiest to fix. It means the request body doesn't match the expected schema.
Typical causes:
- Missing required fields like
model,messages, ormax_tokens - Malformed JSON (trailing commas, unescaped quotes)
- Invalid role values in the messages array (must alternate
user/assistant) - Token limit exceeded for the context window
- Invalid tool definitions when using function calling
Fix: Check the error.message field in the response body — it almost always names the exact field that's wrong.
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "messages: at least one message is required"
}
}
401 — Authentication Error
Your API key is missing, malformed, or revoked.
Common causes:
- Forgot the
Authorization: Bearerheader - Using an expired or deleted key
- Mixing up keys between environments (test key in production, or vice versa)
Fix: Regenerate the key from your dashboard and confirm the header format:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"claude-opus-4","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}'
If you're testing API keys for the first time, see /docs/quickstart for the full setup flow.
403 — Permission Error
The key is valid but doesn't have access to the requested resource — often a model your plan doesn't include, or a feature restricted to certain seat types. Check your plan tier and seat permissions before assuming it's a bug.
404 — Not Found
Usually means a typo in the endpoint path or a reference to a resource (like a file or batch job ID) that doesn't exist or was already deleted.
413 — Request Too Large
Your request body — including any attached files, images, or long conversation history — exceeds the maximum payload size. Trim conversation history or split large documents before sending.
429 — Rate Limit Error
You've exceeded your requests-per-minute, tokens-per-minute, or concurrent-request limit.
{
"type": "error",
"error": {
"type": "rate_limit_error",
"message": "Number of requests has exceeded your rate limit"
}
}
Fix:
- Implement exponential backoff with jitter on retries
- Check the
retry-afterresponse header and wait accordingly - Batch requests where possible instead of firing them in parallel
- If you're consistently hitting limits, consider whether you need a higher plan tier with more capacity
500 — Internal Server Error
Something failed on Anthropic's infrastructure, unrelated to your request. These are rare but do happen. The correct response is to retry with backoff — don't treat it as a bug in your code.
529 — Overloaded Error
The API is temporarily over capacity, typically during high-traffic periods. This is distinct from rate limiting — it's a global, not per-key, constraint.
{
"type": "error",
"error": {
"type": "overloaded_error",
"message": "Overloaded"
}
}
Fix: retry with exponential backoff. If this happens frequently in production, build retry logic into your request layer by default rather than treating it as exceptional.
A Simple Retry Pattern
Most transient errors (429, 500, 529) are solved the same way — retry with backoff:
async function callClaude(payload, retries = 3) {
for (let attempt = 0; attempt <= retries; attempt++) {
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
if (res.ok) return res.json();
if ([429, 500, 529].includes(res.status) && attempt < retries) {
const wait = Math.pow(2, attempt) * 1000 + Math.random() * 500;
await new Promise(r => setTimeout(r, wait));
continue;
}
throw new Error(`Claude API error ${res.status}: ${await res.text()}`);
}
}
This pattern handles the vast majority of production errors without manual intervention. Permanent errors (400, 401, 403, 404, 413) should fail immediately and surface to logs — retrying won't fix a malformed request.
Reading the Error Body
Every Claude API error returns a consistent JSON shape:
{
"type": "error",
"error": {
"type": "<error_type>",
"message": "<human readable description>"
}
}
Always log error.type and error.message together. The type field is stable across API versions and safe to match against in code; the message field is for humans and may change wording over time.
Where SubToAPI Fits
If you're accessing Claude through SubToAPI, the error codes and response shapes are identical to Anthropic's native API — SubToAPI is a pass-through layer, not a reinterpretation of it. The dashboard additionally logs every error by status code and application key, so you can see which key or endpoint is generating 429s or 401s without adding your own logging. Full request/response examples are in /docs/messages, and streaming-specific error handling (for dropped or interrupted streams) is covered in /docs/streaming.
questions
What does a 400 error mean in the Claude API? It means the request body is malformed — a missing required field, invalid JSON, or an incorrect value like a bad role in the messages array. Check error.message for the specific field at fault.
How should I handle 429 and 529 errors? Retry with exponential backoff and jitter. 429 means you've hit a rate limit on your key; 529 means the API is globally overloaded. Both are transient and usually resolve within seconds.
Are SubToAPI error codes the same as Anthropic's? Yes. SubToAPI passes through the same HTTP status codes and JSON error shapes as the native API, so existing error-handling code works unchanged. See /docs for full details.