401 authentication_error and invalid x-api-key: why your key is rejected
A 401 means the server could not match the key you sent to an existing account. The Anthropic API answers with authentication_error and the text invalid x-api-key; go2llm answers with the code no-key and the message missing or unknown api key. It is always a problem with the header or with the key itself — never with the model, the balance or your region, because the request never reaches inference.
Written 2026-09-12.
What the error looks like
The official Anthropic API returns this on a bad key, with status 401:
{
"type": "error",
"error": {
"type": "authentication_error",
"message": "invalid x-api-key"
}
}
go2llm returns its own shape for the same request — status 401, code no-key:
{
"error": {
"type": "no-key",
"code": "no-key",
"message": "missing or unknown api key",
"request_id": "gw_9c2b41a7e0f34d18b6c5a903d7e21f4b"
}
}
Claude Code shows the same situation differently: instead of JSON you get Please run /login. That is the same 401 — the CLI intercepts the status and offers to re-authenticate rather than showing you the server's answer.
You can reproduce it in one command:
curl -i https://go2llm.tech/v1/messages \
-H "x-api-key: gk_sk_definitely_not_a_real_key" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-5","max_tokens":16,"messages":[{"role":"user","content":"ping"}]}'
Seven causes of a 401
1. Claude Code is using ANTHROPIC_API_KEY instead of ANTHROPIC_AUTH_TOKEN
This is cause number one and it is documented almost nowhere. If the machine has ever been logged into Claude Code with a personal claude.ai or Anthropic Console account, ANTHROPIC_API_KEY can lose to the stored login, and the request goes past go2llm to api.anthropic.com, where a gk_sk_… key is unknown — 401. ANTHROPIC_AUTH_TOKEN does not behave that way: it always talks to whatever ANTHROPIC_BASE_URL points at.
2. The key is missing or in the wrong header
The Anthropic Messages API reads the key from x-api-key; OpenAI Chat Completions reads it from Authorization: Bearer. go2llm accepts both, each on its own endpoint: x-api-key on POST /v1/messages, Authorization: Bearer on POST /v1/chat/completions and POST /v1/images/generations. An Authorization: gk_sk_… header without the word Bearer is not recognised and yields a 401 with code no-key.
3. Stray characters got into the key
Copying out of a dashboard through a terminal routinely adds a newline, a space or a quote. A go2llm key starts with the prefix gk_sk_ and contains no spaces. Check with echo -n "$GO2LLM_API_KEY" | wc -c — the length should not include a trailing newline.
4. The key is revoked, expired, or not allowed to use that model
go2llm distinguishes four refusal reasons and returns different codes. no-key is a 401, but inactive (revoked), expired (past its expiry) and model (model not on the key's allow-list) come back with status 403, not 401. If you got a 403, read the 403 page, not this one.
5. The key is being sent to the wrong base URL
A go2llm key only works on go2llm.tech. The base URL for the OpenAI SDK is https://go2llm.tech/v1; for the Anthropic SDK and Claude Code it is https://go2llm.tech without /v1, because the Anthropic SDK appends /v1 itself. Passing https://go2llm.tech/v1 to the Anthropic SDK produces a request to /v1/v1/messages and a routing error.
6. The environment variable never reached the process
An export in one terminal tab is invisible in another, and systemd units, Docker containers and CI runners do not inherit your shell profile. Check from inside the process itself: print(bool(os.environ.get("GO2LLM_API_KEY"))).
7. It is a key from a different service
An Anthropic key starts with sk-ant-, an OpenAI key with sk-proj- or sk-, a go2llm key with gk_sk_. go2llm does not accept vendor keys, and vendors do not accept go2llm keys.
Fixing it, step by step
Step 1. Prove the key works at all, with no SDK and no environment variables:
curl -s -o /dev/null -w "%{http_code}\n" https://go2llm.tech/v1/messages \
-H "x-api-key: PASTE_YOUR_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-5","max_tokens":16,"messages":[{"role":"user","content":"ping"}]}'
A 200 means the key is fine and the problem is in your client. A 401 means the key is wrong.
Step 2. For Claude Code, use ANTHROPIC_AUTH_TOKEN specifically:
export ANTHROPIC_BASE_URL=https://go2llm.tech
export ANTHROPIC_AUTH_TOKEN=YOUR_API_KEY
export ANTHROPIC_MODEL=claude-sonnet-5
claude
If ANTHROPIC_API_KEY was set earlier, clear it: unset ANTHROPIC_API_KEY.
Step 3. For an SDK, check the base-URL-and-header pair together:
import os
from anthropic import Anthropic
client = Anthropic(
base_url="https://go2llm.tech", # no /v1
api_key=os.environ["GO2LLM_API_KEY"], # gk_sk_... key
)
msg = client.messages.create(
model="claude-opus-5",
max_tokens=64,
messages=[{"role": "user", "content": "Hello"}],
)
print(msg.usage) # go2llm also puts cost_usd here
Step 4. Check the key's status in your dashboard: not revoked, not expired, the model you want on its allow-list, and your current IP passing the IP allow-list.
Avoiding 401s later
Treat 401 as fatal and do not retry it: a repeat with the same key returns the same 401 and only burns your 20 RPM limit. A correct handler branches on the code:
import httpx
r = httpx.post(
"https://go2llm.tech/v1/chat/completions",
headers={"Authorization": f"Bearer {api_key}"},
json={"model": "gpt-5.6-sol", "max_tokens": 64,
"messages": [{"role": "user", "content": "OK"}]},
timeout=120.0,
)
if r.status_code == 401:
err = r.json().get("error", {})
code = err.get("code") or err.get("type")
raise SystemExit(
f"Key rejected ({code}): {err.get('message')}. "
f"request_id={err.get('request_id')}. Retrying will not help."
)
Keep the key in a secrets manager or a .env outside git, not in code. Issue a separate go2llm key per environment, each with its own dollar quota and IP allow-list — then a leaked CI key does not touch production. The key created automatically at registration carries no quota of its own: set a limit by hand in the dashboard.
Related errors
- The full error code reference — every code with its fix.
- API documentation — authentication, endpoints, parameters.
- Pricing — per-model rates and how billing settles.
FAQ
Why does Claude Code say "Please run /login" instead of a key error? Claude Code intercepts HTTP 401 and reads it as an expired session. If you are going through go2llm you do not need /login — you need ANTHROPIC_AUTH_TOKEN plus ANTHROPIC_BASE_URL=https://go2llm.tech, and ANTHROPIC_API_KEY cleared with unset.
Can I use an sk-ant-… Anthropic key on go2llm? No. go2llm accepts only its own keys with the gk_sk_ prefix and returns 401 no-key for anything else. The reverse holds too: a gk_sk_… key on api.anthropic.com gives authentication_error: invalid x-api-key.
Am I charged for a request that returned 401? No. go2llm reserves cost only after the key authorizes, so a 401 never touches your balance. The cost of a successful request arrives in usage.cost_usd.
Get a key and make your first request in 3 minutes → go2llm.tech. Registration credits $1 to your balance; the minimum top-up is $1. Prices for every model are on the pricing page.