A 401 here is about your credential or your base URL, never your model — a model problem returns 404 with a message naming the model. That single distinction resolves most of these in one curl.
The error
API Error: 401 {"type":"error","error":{"type":"authentication_error","message":"Missing or invalid API key"}}Causes and fixes at a glance
| Cause | Fix |
|---|---|
| ANTHROPIC_API_KEY set where ANTHROPIC_AUTH_TOKEN was meant | Use AUTH_TOKEN for a third-party base URL; API_KEY takes a one-time approval prompt. |
| Base URL carrying a /v1 path | Set the origin only — Claude Code appends /v1/messages itself. |
| Key revoked, or the account suspended | Both answer 401, never 403. Mint a fresh key and check the account. |
| A model the Messages endpoint does not serve | That returns 404 naming the model, not 401 — so it is a different fix. |
Print the three variables and check the base URL has no path
The most common cause is visible here. The base URL must be an origin — no /v1, no trailing path — because the client appends the endpoint itself. A base URL ending in /v1 produces a request to /v1/v1/messages.
env | grep -E '^ANTHROPIC_(BASE_URL|AUTH_TOKEN|API_KEY|MODEL)='
# Right: https://api.kunavo.com
# Wrong: https://api.kunavo.com/v1Call the endpoint directly, both ways
Kunavo's /v1/messages accepts the credential as either x-api-key or Authorization: Bearer, which is why Claude Code needs no plugin or proxy in front of it. If curl succeeds and the CLI does not, the problem is in your shell environment rather than on the server.
curl -s https://api.kunavo.com/v1/messages -H "x-api-key: $ANTHROPIC_AUTH_TOKEN" -H "anthropic-version: 2023-06-01" -H "content-type: application/json" -d '{"model":"claude-sonnet-5","max_tokens":8,
"messages":[{"role":"user","content":"hi"}]}'
# Same call, other header style — both are accepted:
# -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN"Unset the variable you are not using
If both ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN are set, the wrong one can win. Unset ANTHROPIC_API_KEY, start a new shell, and try again — a stale export in a shell profile outlives every other fix you try.
If the status is 404, stop debugging the key
A 404 whose message names the model means the credential was accepted and the model string was not. Fix ANTHROPIC_MODEL instead; nothing about the key is wrong.
If you’re calling through Kunavo
A 401 from Kunavo narrows to exactly three causes: the key does not carry the sk-kn- prefix, the key has been revoked, or the account is suspended. None of them answers 403, so the status code alone tells you which family you are in — and a valid key aimed at a model the Messages endpoint does not serve returns 404 with the model named in the message, not 401. That is the whole diagnostic tree.
FAQ
Why does my key work in curl but not in Claude Code?
Almost always a second variable set in a shell profile, or a base URL with a path on it. The endpoint accepts both header styles, so the difference is not the header.
ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY?
AUTH_TOKEN for a third-party base URL — it is used immediately. API_KEY triggers a one-time approval prompt first, which is often mistaken for a failure.
Does a Claude subscription cover a custom base URL?
No. A subscription authenticates to the vendor's own endpoint; pointing the CLI elsewhere means that endpoint's credential, billed by that endpoint.
Related guides
- Claude API 401 authentication_error / invalid x-api-key — every cause
- Install Claude Code — the command for every OS, first login, and the errors people hit
More error semantics live in the error reference; getting a key takes a minute via sign up and the authentication docs.