401 authentication_error и invalid x-api-key: почему ключ не принимается
Ошибка 401 означает, что сервер не смог сопоставить присланный ключ с существующей учётной записью. Anthropic API отвечает authentication_error с текстом invalid x-api-key, go2llm — кодом no-key и сообщением missing or unknown api key. Это всегда проблема заголовка или самого ключа, а не модели, баланса или региона: до инференса запрос не доходит.
Страница составлена 2026-09-11, обновлена 2026-09-11.
Как выглядит ошибка
Официальный Anthropic API возвращает на неверный ключ такой ответ со статусом 401:
{
"type": "error",
"error": {
"type": "authentication_error",
"message": "invalid x-api-key"
}
}
go2llm на том же запросе возвращает собственный формат — статус 401 и код no-key:
{
"error": {
"type": "no-key",
"code": "no-key",
"message": "missing or unknown api key",
"request_id": "gw_9c2b41a7e0f34d18b6c5a903d7e21f4b"
}
}
Claude Code показывает ту же ситуацию иначе: вместо JSON пользователь видит предложение Please run /login. Это тот же 401 — CLI перехватывает статус и предлагает переавторизоваться вместо показа ответа сервера.
Воспроизводится ошибка одной командой:
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"}]}'
Причины 401
1. Claude Code использует ANTHROPIC_API_KEY вместо ANTHROPIC_AUTH_TOKEN
Это причина номер один и почти нигде не описанная. Если на машине уже выполнялся вход в Claude Code под личным аккаунтом claude.ai или Anthropic Console, переменная ANTHROPIC_API_KEY может уступить сохранённому логину, и запрос уйдёт мимо go2llm — на api.anthropic.com, где ключ gk_sk_… неизвестен, и вернётся 401. Переменная ANTHROPIC_AUTH_TOKEN такого поведения не имеет: она всегда обращается к адресу из ANTHROPIC_BASE_URL.
2. Ключ не передан или передан в неправильном заголовке
Anthropic Messages API читает ключ из заголовка x-api-key, OpenAI Chat Completions — из Authorization: Bearer. go2llm принимает оба, но каждый на своём endpoint: x-api-key на POST /v1/messages, Authorization: Bearer на POST /v1/chat/completions и POST /v1/images/generations. Заголовок Authorization: gk_sk_… без слова Bearer не распознаётся и даёт 401 с кодом no-key.
3. В ключ попали лишние символы
Копирование из кабинета через терминал регулярно добавляет перевод строки, пробел или кавычку. Ключ go2llm начинается с префикса gk_sk_ и не содержит пробелов. Проверка: echo -n "$GO2LLM_API_KEY" | wc -c должно вернуть длину без символа перевода строки.
4. Ключ отозван, истёк или не разрешает эту модель
go2llm различает четыре причины отказа и возвращает разные коды. no-key — это 401, а вот inactive (ключ отозван), expired (истёк срок) и model (модель не в списке разрешённых для ключа) возвращаются со статусом 403, а не 401. Если в ответе 403 — смотрите страницу про 403, а не эту.
5. Ключ отправляется на чужой base URL
Ключ go2llm работает только на домене go2llm.tech. Base URL для OpenAI SDK — https://go2llm.tech/v1, для Anthropic SDK и Claude Code — https://go2llm.tech без /v1, потому что Anthropic SDK добавляет /v1 сам. Указание https://go2llm.tech/v1 в Anthropic SDK даёт запрос на /v1/v1/messages и ошибку маршрутизации.
6. Переменная окружения не долетела до процесса
export в одной вкладке терминала не виден в другой, а systemd-юниты, Docker-контейнеры и CI-раннеры не наследуют пользовательский профиль. Проверка внутри самого процесса: print(bool(os.environ.get("GO2LLM_API_KEY"))).
7. Ключ от другого сервиса
Ключ Anthropic начинается с sk-ant-, ключ OpenAI — с sk-proj- или sk-, ключ go2llm — с gk_sk_. go2llm не принимает ключи вендоров, и наоборот.
Как починить по шагам
Шаг 1. Проверьте, что ключ вообще работает, запросом без SDK и без переменных окружения:
curl -s -o /dev/null -w "%{http_code}\n" https://go2llm.tech/v1/messages \
-H "x-api-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"}]}'
Ответ 200 означает, что ключ исправен и проблема в вашем клиенте. Ответ 401 — ключ не тот.
Шаг 2. Для Claude Code используйте именно ANTHROPIC_AUTH_TOKEN:
export ANTHROPIC_BASE_URL=https://go2llm.tech
export ANTHROPIC_AUTH_TOKEN=YOUR_API_KEY
export ANTHROPIC_MODEL=claude-sonnet-5
claude
Если ранее была выставлена ANTHROPIC_API_KEY, снимите её: unset ANTHROPIC_API_KEY.
Шаг 3. Для SDK проверьте пару «base URL + заголовок»:
import os
from anthropic import Anthropic
client = Anthropic(
base_url="https://go2llm.tech", # без /v1
api_key=os.environ["GO2LLM_API_KEY"], # ключ gk_sk_...
)
msg = client.messages.create(
model="claude-opus-5",
max_tokens=64,
messages=[{"role": "user", "content": "Привет"}],
)
print(msg.usage) # у go2llm здесь есть и cost_usd
Шаг 4. Проверьте статус ключа в кабинете: не отозван, не истёк, нужная модель в списке разрешённых, ваш текущий IP проходит IP allow-list.
Как избежать 401 в будущем
Обрабатывайте 401 как фатальную ошибку и не ретрайте: повтор с тем же ключом даст тот же 401 и только сожжёт лимит 20 RPM. Правильный обработчик различает коды:
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"Ключ не принят ({code}): {err.get('message')}. "
f"request_id={err.get('request_id')}. Ретрай не поможет."
)
Храните ключ в секрет-менеджере или .env вне git, а не в коде. Для каждого окружения заводите отдельный ключ go2llm со своей квотой в долларах и своим IP allow-list — тогда компрометация ключа из CI не затрагивает прод. Ключ, созданный автоматически при регистрации, идёт без квоты: лимит выставляется вручную в кабинете.
Связанные ошибки
- 403: ключ отозван, истёк, не та модель или не тот IP — если статус 403, а не 401.
- 402 insufficient_balance — ключ верный, но на балансе меньше стоимости запроса.
- 429 rate_limit — ключ верный, но превышен лимит 20 RPM.
- Справочник всех кодов ошибок и docs/errors.
FAQ
Почему Claude Code пишет «Please run /login» вместо ошибки ключа? Claude Code перехватывает HTTP 401 и трактует его как истёкшую сессию. Если вы работаете через go2llm, выполнять /login не нужно — нужно выставить ANTHROPIC_AUTH_TOKEN и ANTHROPIC_BASE_URL=https://go2llm.tech, а ANTHROPIC_API_KEY снять через unset.
Можно ли использовать ключ Anthropic вида sk-ant-… на go2llm? Нет. go2llm принимает только собственные ключи с префиксом gk_sk_ и вернёт 401 no-key на чужой ключ. Обратное тоже верно: ключ gk_sk_… на api.anthropic.com даст authentication_error: invalid x-api-key.
Списываются ли деньги за запрос, который вернул 401? Нет. go2llm резервирует стоимость только после успешной авторизации ключа, поэтому 401 не затрагивает баланс. Стоимость успешного запроса приходит в usage.cost_usd.
Сколько ключей можно завести? Точное ограничение по количеству ключей в аккаунте мы пока не документировали — напишите в @go2llm_support, ответим и дополним страницу. У каждого ключа настраиваются квота в долларах, список разрешённых моделей, IP allow-list, срок действия и лимит RPM (по умолчанию 20).
Получить ключ и сделать первый запрос за 3 минуты → go2llm.tech. При регистрации на баланс начисляется $1, минимальное пополнение — $1. Цены на все модели — на странице тарифов, инструкции по подключению — в документации.