A 401 from Claude is always one of five things: the wrong header, the wrong key type for the endpoint, a malformed env var, a revoked key, or the wrong base URL for that key. Run the diagnostic below and you'll find yours in under a minute.
The error
{
"type": "error",
"error": {
"type": "authentication_error",
"message": "invalid x-api-key"
}
}Causes and fixes at a glance
| Cause | Fix |
|---|---|
| Wrong header for the endpoint | Anthropic's native API wants x-api-key + anthropic-version; OpenAI-compatible endpoints want Authorization: Bearer. |
| Key/endpoint mismatch | sk-ant-… keys work only against api.anthropic.com; gateway keys (e.g. sk-kunavo-…) work only against their own gateway URL. |
| Whitespace or quotes smuggled into the env var | Re-export without quotes/newlines; print len(key) to catch trailing \n from copy-paste. |
| Key revoked or workspace disabled | Mint a fresh key in the console and rotate it in your secret manager. |
Reproduce with raw curl (removes your SDK from the equation)
If curl works but your app doesn't, the bug is in your env plumbing, not the key:
# Native Anthropic wire (works on api.anthropic.com and Kunavo /v1/messages)
curl -s https://api.kunavo.com/v1/messages \
-H "x-api-key: $KUNAVO_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-4-6","max_tokens":16,"messages":[{"role":"user","content":"ping"}]}'
# OpenAI-compatible wire (Bearer header instead)
curl -s https://api.kunavo.com/v1/chat/completions \
-H "Authorization: Bearer $KUNAVO_API_KEY" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-4-6","max_tokens":16,"messages":[{"role":"user","content":"ping"}]}'
# Check the key isn't carrying whitespace
python3 -c "import os; k=os.environ['KUNAVO_API_KEY']; print(repr(k[:12]), len(k))"Match the key prefix to the base URL
sk-ant-… → api.anthropic.com. sk-kunavo-… → api.kunavo.com/v1. Sending a gateway key to Anthropic (or vice versa) always 401s — the error message never says "wrong host," so this one hides in plain sight.
Rotate if the key ever touched a repo or log
If the key is correct and still rejected, assume revocation (automated scanners revoke leaked keys fast). Mint a new one and store it in a secret manager rather than .env files that get committed.
If you're calling through Kunavo
Kunavo keys (sk-kunavo-…) authenticate with either wire format — Authorization: Bearer on the OpenAI-compatible endpoints or x-api-key on the native Anthropic Messages endpoint — so whichever SDK you use, only the base URL changes. Keys are created and revoked instantly in the dashboard.
FAQ
Why does my key work in curl but not in my app?
Almost always env plumbing: a trailing newline from copy-paste, quotes included in the value, the variable not exported to the process, or a different env loaded in production. Print the repr and length of the key inside the failing process.
Can I use my Anthropic Console key on an OpenAI-compatible gateway?
No. Each service authenticates only its own keys: sk-ant keys belong to api.anthropic.com, gateway keys belong to the gateway. Get a key from whichever base URL you're calling.
More error semantics live in the error reference; getting a key takes a minute via sign up and the authentication docs.