Claude API Authentication Error Troubleshooting Guide
If you're getting a 401 Unauthorized or authentication_error response from the Claude API, the cause is almost always one of five things: a missing or malformed header, an invalid or revoked key, a key/organization mismatch, a missing API version header, or an environment variable that isn't actually loaded at runtime. This guide walks through each one so you can isolate the problem in minutes instead of guessing.
The fastest way to debug this is to strip your request down to a single curl call with no SDK, no wrapper, and no framework in between. SDKs and middleware can silently swallow or rewrite headers, which makes it look like the API itself is broken when it isn't.
Step 1: Reproduce the error with raw curl
Before touching your application code, confirm the error exists at the HTTP level:
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-3-5-sonnet-20241022",
"max_tokens": 100,
"messages": [{"role": "user", "content": "test"}]
}'
If this works but your application doesn't, the bug is in your code, not your credentials. If it fails too, move to the checks below.
Step 2: Check the header name and casing
Anthropic's own API expects x-api-key, not Authorization: Bearer. This is the single most common mistake developers make when they're used to OpenAI-style auth, since OpenAI uses Authorization: Bearer sk-.... Sending a valid key with the wrong header name produces an authentication error that looks identical to an invalid-key error, which wastes a lot of debugging time.
A quick checklist:
- Header name is exactly
x-api-key(lowercase is fine, HTTP headers are case-insensitive, but the name must be correct) - No extra whitespace or newline characters pasted in from a
.envfile - The key isn't wrapped in quotes that get included literally in the string
- You're not accidentally sending both
x-api-keyand a conflictingAuthorizationheader
Step 3: Confirm the key itself is valid
Keys get revoked, rotated, or scoped to a different workspace than you expect. Check for:
- Revoked or rotated keys — if someone on your team regenerated the key in the console, old copies stop working immediately with no grace period.
- Wrong environment — a key copied from a personal account won't work against a team/org project, and vice versa.
- Billing or account status — a suspended account can return authentication-style errors rather than a clear billing message, depending on the failure mode.
- Copy-paste truncation — keys are long; it's easy to miss the last few characters when copying from a terminal output or chat message.
If you're not sure whether the key or the request format is the problem, test with a key you just generated fresh, in a brand-new terminal session, with no shell history or exported variables that could be stale.
Step 4: Verify the API version header
Anthropic requires an anthropic-version header on every request. If it's missing or set to a value the API doesn't recognize, you can get an error that's easy to mistake for an authentication failure, especially if your error handling just logs the status code without the body. Always log the full response body during debugging:
curl -v 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-3-5-sonnet-20241022","max_tokens":50,"messages":[{"role":"user","content":"hi"}]}' \
2>&1 | tail -30
The response JSON almost always includes a type and message field that tells you exactly what's wrong — read it instead of only checking the status code.
Step 5: Check environment variable loading, not just the .env file
A huge share of "authentication errors" aren't about the key at all — they're about the variable being empty at runtime. Common causes:
.envfile exists but isn't loaded because the process was started before the env loader ran- The variable is defined in
.env.localbut your deploy target only reads.env.production - A serverless function's environment variables weren't redeployed after being added in the dashboard
- The key is set in your shell but not exported (
KEY=xyzinstead ofexport KEY=xyz)
Add a temporary log line that prints the length of the key (never the key itself) right before the request is made:
console.log("Key length:", process.env.ANTHROPIC_API_KEY?.length ?? 0);
If it prints 0 or undefined, you've found your bug and it has nothing to do with Anthropic's servers.
Step 6: Rule out rate limits and quota errors masquerading as auth errors
Some client libraries catch all 4xx errors and label them generically as "authentication errors" in their own exception classes, even when the underlying HTTP status is a 429 or 403 for a quota or permissions issue rather than a 401. Check the raw status code and response body, not just your SDK's exception message.
A simpler path: managed authentication
If you're building a product on top of Claude and want to stop debugging raw header formats every time something changes, SubToAPI sits in front of your Claude access and issues clean sub_live_... application keys that work with a standard Authorization: Bearer header — the format most developers already expect from other providers. You get one dashboard for keys, usage, and team seats instead of juggling raw provider credentials across environments. Setup takes a few minutes: see the quickstart or the full messages API reference. If you want streaming or tool use working out of the box, those are documented separately in streaming and tools.
Quick reference: error-to-cause mapping
| Symptom | Likely cause | |---|---| | 401 with "invalid x-api-key" | Wrong header name or expired key | | 401 only in production, works locally | Env var not deployed/loaded | | 400 mentioning "anthropic-version" | Missing or malformed version header | | Works in curl, fails in app | Bug in SDK config or middleware | | Intermittent auth failures | Key rotation race condition across instances |
questions
Why does my Claude API key work in curl but not in my app? Your application is likely sending the wrong header name, an empty environment variable, or a stale key cached from a previous deploy. Isolate the request with curl first, then compare headers byte-for-byte against your app's outgoing request.
Is Authorization: Bearer or x-api-key correct for Claude? Anthropic's native API requires x-api-key. If you're using a gateway like SubToAPI, it accepts the more familiar Authorization: Bearer sub_live_... format instead — check your provider's docs before assuming one or the other.
How do I tell an authentication error apart from a rate limit error? Always inspect the raw HTTP status code and response body rather than trusting your SDK's exception type. A 401 is authentication, a 429 is rate limiting, and a 403 usually means permissions or billing — the message field in the JSON response states this explicitly.