TL;DR — Claude Code sends credentials in two different HTTP headers depending on which environment variable you set: ANTHROPIC_API_KEY goes out as x-api-key (what Anthropic's own API expects), while ANTHROPIC_AUTH_TOKEN goes out as Authorization: Bearer (what most gateways and routers expect). Point ANTHROPIC_BASE_URL at a router but keep your credential in the wrong variable, and you get 401 authentication_error even though the key itself is valid. Rule of thumb: custom endpoint → ANTHROPIC_AUTH_TOKEN; api.anthropic.com directly → ANTHROPIC_API_KEY.
The error
You redirect Claude Code to a router, gateway, or proxy:
export ANTHROPIC_BASE_URL=https://www.coderouter.io/api/v1
export ANTHROPIC_API_KEY=<your router key> # ← the mistake
claude
And every request fails with:
API Error: 401
{"type":"error","error":{"type":"authentication_error","message":"invalid x-api-key"}}
The key is correct — you can curl the router with it directly. So why 401?
The cause: two env vars, two headers
Claude Code distinguishes first-party and custom-endpoint authentication:
| Env var | Header sent | Intended for |
|---|---|---|
| ANTHROPIC_API_KEY | x-api-key: <key> | Anthropic's own API (api.anthropic.com) |
| ANTHROPIC_AUTH_TOKEN | Authorization: Bearer <key> | Gateways, routers, proxies |
Most routers and gateways authenticate with a standard Authorization: Bearer header. When your key arrives in x-api-key instead, the router sees no credential where it looks for one and returns 401 — often echoing Anthropic-style error JSON, which makes it look like an upstream Anthropic rejection. (Both variables are documented in Claude Code's settings reference; LiteLLM's Claude Code guide shows the same ANTHROPIC_AUTH_TOKEN pattern for gateway setups.)
The fix
For any custom endpoint, use the token variable — and make sure the key variable isn't set at the same time:
export ANTHROPIC_BASE_URL=https://www.coderouter.io/api/v1
export ANTHROPIC_AUTH_TOKEN=<your coderouter key>
unset ANTHROPIC_API_KEY
claude
Or persistently, in ~/.claude/settings.json:
{
"env": {
"ANTHROPIC_BASE_URL": "https://www.coderouter.io/api/v1",
"ANTHROPIC_AUTH_TOKEN": "<your coderouter key>"
}
}
Then verify inside Claude Code with a trivial prompt, and confirm on the router dashboard that the request arrived and was routed.
Other 401/403 variants worth checking
If switching variables doesn't fix it:
- Placeholder key.
sk-...copied verbatim from an example config. Regenerate and paste the real key. - Stale login session. If you previously authenticated Claude Code with a subscription login, that credential can shadow env vars. Run
claude /logoutfirst (or checkapiKeyHelperin settings), then set the env vars. - Key scoped to the wrong product. Router keys, Anthropic Console keys, and subscription auth are different credentials. A valid Anthropic key is not valid on your router, and vice versa.
- Proxy in the middle. Corporate networks may require
HTTPS_PROXYin addition toANTHROPIC_BASE_URL— see Claude Code's corporate proxy docs. A TLS-intercepting proxy that strips headers produces the same 401 symptom.
Why route Claude Code at all?
Once the base URL points at a router, every Claude Code request becomes routable: planning-phase requests can go to a frontier model while bulk edit/implementation turns go to cheaper models, with per-request cost tracking — that's CodeRouter's phase-aware routing. The env-var setup above is the entire client-side integration; no plugin needed.
FAQ
Do I need both ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN?
No — set exactly one. Both set at once is the second most common broken state after "wrong one set", because which credential wins depends on the endpoint and tool version. Custom endpoint: ANTHROPIC_AUTH_TOKEN only.
My router logs show no request at all. Still an auth problem?
No — 401 with nothing in the router log means the request never reached the router: typo in ANTHROPIC_BASE_URL, missing /api/v1-style path prefix, or a shell where the export isn't active (new terminal, IDE-spawned process). Print the resolved env with claude /doctor or env | grep ANTHROPIC from the same shell.
Does this apply to the Claude Agent SDK too?
Yes — the SDK honors the same ANTHROPIC_BASE_URL / auth conventions as Claude Code, so the same header rule applies when pointing SDK agents at a gateway.
Sources: Claude Code settings reference, Claude Code corporate proxy docs, LiteLLM Claude Code quickstart.