Anthropic API Key Not Working? Read the Status Code First
"My Anthropic API key is not working" is almost always one of five different problems wearing the same costume. Four of them have nothing wrong with the key at all. The status code tells you which one you have, and it takes about ten seconds to read — far less time than the average round of key regeneration, which usually fixes nothing because the key was fine.
This guide sorts the failures by what the API actually returned, then covers the credential-resolution traps that produce the most confusing case of all: the request that fails while a perfectly valid key sits in your environment.
First, read the status code — it is the whole diagnosis
Every failed call returns an HTTP status and a JSON error object with a type field. Those two together identify the problem. Before changing anything, print them.
|
Status |
Error type |
What it actually means |
|---|---|---|
|
401 |
|
The credential was missing, malformed, revoked, or sent in the wrong header. This is the only code that means "the key" |
|
403 |
|
The key is valid and authenticated. It just is not allowed to do this |
|
404 |
|
The key is fine. The model ID or the endpoint path is wrong |
|
400 |
|
The key is fine. The request body or your org configuration is the problem |
|
429 |
|
The key is fine and working. You are over a limit |
Only the first row is a key problem. If you regenerate your key in response to any of the other four, you will replace a working credential with another working credential and reproduce the same failure.
The request ID matters too. Every response carries a request-id header, and the SDKs expose it on the response object. Capture it before you start experimenting — it is what support can trace, and it distinguishes "the call reached Anthropic and was rejected" from "the call never arrived."
401: the cases that really are the credential
Within 401 there is still a fork, and the non-obvious branches are the ones that cost hours.
The header is wrong for the credential type. An API key goes in x-api-key. An OAuth access token goes in Authorization as a bearer token, together with the beta header anthropic-beta: oauth-2025-04-20. These are not interchangeable. Converting a working request from an API key to an OAuth token is a header change, not a value swap — and pasting a bearer token into x-api-key returns exactly the same 401 as a garbage key, with no hint about which mistake you made.
Two credentials are set at once. If both ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN are present in the environment, the SDK sends both headers and the API rejects the request. This one is genuinely cruel: each variable is individually correct, nothing is expired, and the fix is to unset one. It shows up most often after someone adds OAuth to a machine that already had a key exported in a shell profile.
The key was revoked, rotated, or belongs to a deleted workspace. Nothing about the string looks different when this happens. Check the Console rather than inspecting the value.
Whitespace and truncation. A key read from a file with a trailing newline, or copied out of a wrapped terminal display, is a different string. Compare the length of what your process actually loaded against the length in the Console, rather than eyeballing the prefix.
403: authenticated, but not permitted
A 403 means the credential was accepted and identified. What follows is an authorization decision, and there are three usual causes: the key does not have access to the model you named, an organization-level restriction applies, or you are calling a beta feature your org has not been granted.
The distinguishing test is simple. Retry the identical request against a model you know your org uses in production. If that succeeds, the credential is fine and the problem is scoped to the model or the feature — go to the Console and check key permissions and workspace assignment, not the key value.
404: the model ID, not the key
A 404 with not_found_error on the messages endpoint almost always means the model string is wrong. The failure mode that catches everyone is punctuation: model IDs use hyphens throughout, so a dot where a hyphen belongs produces a 404 that reads, to a tired person at 2am, exactly like an auth failure.
Two related traps. First, do not append a date suffix to a current model ID you half-remember from older documentation — the current IDs are complete as written. Second, a retired model returns a 404 too, so code that ran fine last quarter can start "failing authentication" without anything about your credentials having changed. When in doubt, list the models your key can actually see through the models endpoint and copy an ID from the response.
400: the body, or your organization's configuration
A 400 is a request problem, but two flavours of it get misread as credential trouble because they fail on every single call rather than on some of them.
The first is a parameter that a newer model no longer accepts. Sampling parameters and fixed thinking budgets were removed on the current generation; sending them returns a 400 regardless of how good your key is. If a working integration started failing right after a model upgrade, this — not the key — is the first thing to check.
The second is a data-retention constraint. Certain models require the organization or workspace to have data retention enabled at the required level; an org configured below that gets a 400 on every request to those models, with a perfectly valid payload and a perfectly valid key. The message names retention explicitly, which is the tell — read the error text rather than assuming a 400 means malformed JSON.
429: working correctly, and telling you so
A 429 is proof your key authenticated. It is a capacity signal, not a credential one — and it is the single most commonly misfiled "key not working" report, because a new organization starts on the lowest rate-limit tier and can hit ceilings immediately with a small workload.
Read retry-after and the x-ratelimit-* headers to see which limit you crossed, and note that the SDKs already retry 429 and 5xx with backoff by default. The full diagnosis — which of the separately-measured limits you actually hit, and what fixes each one — is its own subject, covered in Anthropic API rate limits.
The trap that produces "the key is right there and it still fails"
This is the case that generates the most wasted hours, and it is not about the key being wrong. It is about which credential your process actually picked up.
The SDKs and the CLI resolve credentials in a fixed order, first match wins: ANTHROPIC_API_KEY, then ANTHROPIC_AUTH_TOKEN, then the profile selected by ANTHROPIC_PROFILE or the active OAuth profile from a prior login, then Workload Identity Federation environment variables, then the default profile on disk.
Three consequences follow, and each one explains a real bug:
- An unset
ANTHROPIC_API_KEYdoes not mean you have no credentials. A zero-argument client constructor works fine after a CLI login, with no environment variable anywhere. Code that panics on a missing env var and refuses to construct the client is rejecting a machine that was correctly configured. - An exported key shadows everything below it. A stale key left in a shell profile, a Dockerfile, or a CI secret quietly wins over the profile you just logged into. You log in successfully, you verify the login, and the request still uses the old key — because the environment variable outranks the profile.
- An empty value still counts as set. For federated credentials in particular,
ANTHROPIC_API_KEYorANTHROPIC_AUTH_TOKENoutranks federation even when the value is an empty string, and a setANTHROPIC_PROFILEnaming a profile that does not exist is an error rather than a fall-through to the next source.
The one-command answer is ant auth status, which reports which source and profile are actually active. Run it before touching anything. If it names a source you did not expect, you have found the bug without changing a single credential.
When you are calling through a gateway or proxy
If your requests go through an OpenAI-compatible gateway, a proxy, or a corporate egress layer rather than directly to Anthropic, add one step before any of the above: confirm which service produced the error.
A gateway authenticates you with its credential, then authenticates itself to the upstream with a different one. A 401 can therefore originate at either hop, and the two have opposite fixes. Distinguish them by looking at the response body shape — an Anthropic error object has a type field with the values in the table above; a gateway's own rejection usually looks different, and frequently is not JSON at all. An HTML error page is never from the Messages API; it is from something in front of it. The same applies to timeouts and to a 200 that carries an error inside a non-standard envelope.
The base URL is the other half of this. A client pointed at a gateway sends credentials intended for that gateway; the same code pointed at the default endpoint sends them to Anthropic, where they are meaningless and return 401. If you switch environments by changing a base URL variable, the credential has to move with it. See Claude API proxy for how that layer is structured and Anthropic Claude API access for the routes and where access itself gets blocked.
A five-minute diagnostic order
- Print the HTTP status, the error
type, and therequest-id. Stop guessing; you now know the category. - If 401: check which header carries the credential, and check whether two credential environment variables are set at once.
- Confirm which credential the process actually loaded, not which one you intended it to load.
- If 403 or 404: hold the credential fixed and vary the model ID. The result tells you whether the problem is permission or spelling.
- If 400: read the message text. It names the offending parameter or the org setting.
- Only after all of the above, consider rotating the key — and when you do, do it with an overlap window rather than a hard cutover, as covered in API key rotation best practices.
Frequently asked questions
Why does my Anthropic API key return 401 when it works in the Console?
The Console does not use your API key — it authenticates your browser session. A key that has never been exercised over HTTP can be revoked, mistyped, or sent in the wrong header without the Console ever showing a difference. Check the header name first, then whether a second credential variable is also set.
Is a 403 an invalid API key?
No. A 403 means the credential authenticated successfully and was then denied a specific action — usually access to a model, a workspace, or a beta feature. The key itself is valid. Regenerating it will not change the outcome.
Why did my requests start failing after I ran a CLI login?
Most likely they did not start failing because of the login. Check whether an older key is still exported in your environment, because an environment variable outranks the profile you just created. Verify which source is active before assuming the login is broken.
Does a 404 mean my key lacks access to the model?
No — that would be a 403. A 404 means the model ID or endpoint path does not exist as written. Check for a dot where a hyphen belongs, and for a date suffix appended to a model ID that does not take one.
Can a billing or credit problem look like a bad key?
It can look like one, but it does not present as 401. Insufficient credit or a billing block surfaces as a request-level error with a message that names the condition, so read the error text rather than assuming authentication. If you are working out how the balance side works, see buying Claude API credits.
How do I test a key without writing any code?
Send a minimal request to the messages endpoint with three headers — the key in x-api-key, anthropic-version set to the documented version string, and content-type as JSON — and a tiny body naming a model and a one-line message. It either returns content or returns an error object that puts you in the table above. Do not include anything else in the test; extra parameters can produce a 400 that hides the answer you were looking for.
The short version
Read the status code before you touch the key. 401 is the only one that means the credential, and even inside 401 the frequent causes are a wrong header or two credential variables set at once rather than a bad key. 403 means authenticated but not allowed, 404 means the model ID is misspelled, 400 means the body or an org setting, and 429 means the key is working perfectly. When a valid key appears to be ignored, the answer is almost always credential resolution order — something higher in the chain is winning, and one status command will tell you what.