Заголовок с API-ключом: Authorization Bearer или x-api-key — что куда

OpenAI-клиенты шлют ключ в Authorization, Anthropic-клиенты в x-api-key. Разбираем, что принимает шлюз, и как по ответу понять, где именно ключ потерялся.

5 мин · · ошибки · API-ключи

Первая же ошибка 401 при подключении нового клиента часто связана не с самим ключом, а с тем, как он передан. У разных API свои соглашения: в одном случае ключ едет в заголовке Authorization, в другом в x-api-key. Ниже разбираем, какой OpenAI API key header нужен, что такое x-api-key у Claude и что из этого принимает шлюз. В конце есть порядок диагностики, если запрос всё равно получает 401.

Два соглашения

OpenAI: Authorization Bearer

Официальный OpenAI API и всё, что подражает его формату, ждёт ключ в стандартном HTTP-заголовке:

Authorization: Bearer <ключ>

Слово Bearer, пробел, затем сам ключ. OpenAI SDK делает это сам: вы передаёте api_key, он подставляет заголовок. Отдельного заголовка x-api-key у OpenAI нет, поэтому запросы вида «openai x api key» обычно возникают, когда человек путает два формата.

Anthropic: x-api-key и anthropic-version

Anthropic API использует свой заголовок:

x-api-key: <ключ> anthropic-version: 2023-06-01

Второй заголовок задаёт версию API. Официальный Anthropic SDK отправляет оба сам. Так что «anthropic x api key» — это штатный способ передачи ключа в Anthropic-формате, а не что-то дополнительное.

Имена HTTP-заголовков не зависят от регистра, поэтому X-API-Key, X-Api-Key и x-api-key — один и тот же заголовок.

Что принимает шлюз Zukko

В Zukko один ключ вида fetch_... работает во всех форматах, и принимаются оба варианта:

Authorization: Bearer fetch_YOUR_KEY x-api-key: fetch_YOUR_KEY

Согласно документации по аутентификации, Authorization: Bearer используют OpenAI SDK и большинство IDE, а x-api-key отправляют Anthropic SDK и другие Anthropic-клиенты (в том числе Roo Code, хотя разработка этого расширения прекращена 15 мая 2026 года). Менять их настройки не нужно. Формат запроса и заголовок ключа между собой не связаны: любую модель можно вызвать в любом формате, а ключ принимается любым из двух способов.

Что не поддерживается: передача ключа в строке запроса (?api_key=...) — такой запрос получает 401. Другие имена заголовков (например, api-key) в документации не описаны, на них рассчитывать не стоит.

Примеры для обоих форматов

OpenAI-формат (/v1/chat/completions), заголовок Bearer:

bash
curl https://api.zukko.pro/v1/chat/completions \
  -H "Authorization: Bearer $ZUKKO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-6.1-sol","messages":[{"role":"user","content":"Привет"}]}'

Anthropic-формат (/v1/messages), заголовок x-api-key:

bash
curl https://api.zukko.pro/v1/messages \
  -H "x-api-key: $ZUKKO_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{"model":"claude-sonnet-5-5","max_tokens":256,"messages":[{"role":"user","content":"Привет"}]}'

Для Anthropic-формата указывайте anthropic-version: 2023-06-01: SDK ставят его сами, а в curl добавляйте вручную, как в примере выше и в официальных примерах Anthropic.

Теперь то же через SDK. В Python у каждого SDK свой base URL: у OpenAI SDK с /v1, ключ уходит в Authorization: Bearer; у Anthropic SDK без /v1 (он добавит сам), ключ уходит в x-api-key.

bash
import os
from openai import OpenAI
import anthropic

oa = OpenAI(base_url="https://api.zukko.pro/v1", api_key=os.environ["ZUKKO_API_KEY"])
print(oa.chat.completions.create(
    model="gpt-6.1-sol",
    messages=[{"role": "user", "content": "Привет"}],
    max_tokens=50,
).choices[0].message.content)

an = anthropic.Anthropic(base_url="https://api.zukko.pro", api_key=os.environ["ZUKKO_API_KEY"])
print(an.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=50,
    messages=[{"role": "user", "content": "Привет"}],
).content[0].text)

Обратите внимание на адреса: клиенты, которые сами дописывают /v1, получают адрес без него, остальные с ним. Если перепутать, ошибка будет не про заголовок, а про путь (чаще всего 404). Подробнее — в статье Base URL с /v1 или без.

Где что используют популярные клиенты

КлиентЧто отправляет
OpenAI SDK, LangChain (OpenAI), Cursor, Cline в режиме OpenAIAuthorization: Bearer
Anthropic SDKx-api-key и anthropic-version
Roo Code в режиме Anthropic (расширение закрыто 15 мая 2026, но установленные копии ещё встречаются)x-api-key
Claude Code с переменной ANTHROPIC_AUTH_TOKENAuthorization: Bearer

Последняя строка важна: у Claude Code токен из ANTHROPIC_AUTH_TOKEN уходит как Bearer, и именно эту переменную мы рекомендуем для Zukko.

Диагностика: ключ передан не туда

Ответ 401 от шлюза выглядит так:

bash
{
  "type": "error",
  "error": {
    "type": "authentication_error",
    "message": "Invalid API key. Use Authorization: Bearer fetch_*. ..."
  }
}

Пройдите по списку причин сверху вниз:

  1. Ключ не дошёл до заголовка. Переменная окружения пустая (echo ${#ZUKKO_API_KEY} покажет длину; 0 означает пустую переменную). В curl кавычки нужны, иначе оболочка может съесть часть строки.
  2. Пробел или перенос строки в конце. Частая беда при копировании из мессенджера. Заголовок с таким ключом не совпадёт.
  3. Нет слова Bearer или двойной префикс. Authorization: fetch_... без Bearer и Authorization: Bearer Bearer fetch_... — обе ошибки дают 401.
  4. Ключ в query string. Строка ?api_key=... не поддерживается.
  5. Ключ отозван или удалён. Проверьте список ключей в кабинете. Полный ключ показывается один раз при создании, поэтому потерянный ключ придётся создать заново.
  6. Ключ другой системы. Ключ OpenAI или Anthropic не подойдёт: у шлюза свои ключи fetch_....

Хитрость с /v1/models

Эндпоинт GET /v1/models по документации открыт и ключ в нём игнорируется. Поэтому проверка «список моделей приходит, значит, ключ верный» неверна. Чтобы проверить ключ, обращайтесь к эндпоинту, где он нужен, например GET /v1/balance:

bash
curl https://api.zukko.pro/v1/balance -H "Authorization: Bearer $ZUKKO_API_KEY"

Ответ 200 говорит, что ключ распознан. Если 401 здесь, проблема в самом ключе или заголовке.

Не 401, а другая ошибка

Заголовок может быть в порядке, а ошибка в другом:

  • 402: не хватает баланса;
  • 403 с кодом email_unverified: не подтверждён email;
  • 403 с кодом model_not_allowed_for_key: модель не входит в список разрешённых для этого ключа;
  • 404: неверный base URL или неизвестное имя модели (model_not_found);
  • 429: исчерпан дневной или месячный лимит ключа.

Общую таблицу ошибок мы разбирали в статье 401, 402, model not found.

Безопасность заголовка

  • Ключ нельзя отправлять по HTTP без шифрования: используйте только https://api.zukko.pro.
  • Не вставляйте ключ в код, который выполняется в браузере посетителя: заголовок виден в инструментах разработчика. Держите ключ на сервере.
  • Заведите отдельный ключ на приложение и задайте ему дневной лимит. Об этом — в документации по лимитам ключа.

Если нужен ключ, создайте его в кабинете Zukko и проверьте запросами выше.

Частые вопросы

Нужен ли x-api-key для OpenAI-совместимых запросов?

Нет. Для /v1/chat/completions стандартный способ — Authorization: Bearer. Шлюз принимает и x-api-key, но OpenAI SDK его не отправляет и менять это не нужно.

Можно ли отправить Bearer в Anthropic-формате?

Да. Шлюз принимает Bearer и для /v1/messages, поэтому в Claude Code работает ANTHROPIC_AUTH_TOKEN. Официальный Anthropic SDK при этом по умолчанию использует x-api-key, и это тоже работает.

Нужен ли заголовок anthropic-version?

Для Anthropic-формата да. SDK добавляют его сами, а в curl указывайте anthropic-version: 2023-06-01 вручную.

Почему список моделей отвечает, а чат выдаёт 401?

Потому что GET /v1/models не требует ключа. Проверяйте ключ запросом GET /v1/balance или реальным запросом к модели.

zukko

Платите в 25×
меньше

за Claude Code & ChatGPT

Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.