Зачем заводить отдельный API-ключ на каждый проект

Один ключ на все проекты — это пять минут экономии сейчас и путаница при первом же инциденте. Что именно даёт разделение ключей по проектам.

5 мин · · Обновлено · API-ключи

Проще всего выпустить один ключ fetch_... и вставить его во всё — в личный пет-проект, в рабочий сервис, в скрипт для тестов. Экономит пять минут при настройке и создаёт ровно те проблемы, которые обычно всплывают в худший момент.

Лимит одного проекта не должен быть лимитом всех

На странице ключа в /app/api-keys можно задать дневной и месячный лимит расхода в долларах и список разрешённых моделей — но это настройка конкретного ключа, а не аккаунта целиком. Если ключ один на пять проектов, лимит приходится ставить с запасом на все пять сразу, то есть по факту он не защищает ни один из них: баг в одном сервисе может съесть весь дневной лимит, и остальные четыре просто перестанут получать ответы с 429 key_daily_limit_reached, хотя сами ни в чём не виноваты.

С отдельным ключом на проект лимит считается для этого проекта:

bash
# ключ для internal-скрипта аналитики — низкий потолок
curl https://api.zukko.pro/v1/chat/completions \
  -H "Authorization: Bearer fetch_ключ_analytics" \
  -H "Content-Type: application/json" \
  -d '{"model":"glm-5.3-flash","messages":[{"role":"user","content":"..."}]}'

Если этот скрипт зациклится, он упрётся в свой лимит и остановится — прод с отдельным ключом продолжит работать как ни в чём не бывало.

Список разрешённых моделей ограничивает не только бюджет, но и площадь ошибки

Ключ можно ограничить конкретными моделями. Для проекта, которому нужен только gpt-5.6-luna для генерации коротких ответов, нет смысла разрешать ему claude-opus-5-5 — случайная опечатка в конфиге или чужой код, который получил доступ к этому ключу, физически не сможет прогнать через него дорогую модель. Попытка вернёт 403 model_not_allowed_for_key.

Это работает и в обратную сторону: если один проект специально держат на дорогой модели ради качества, а другой — на дешёвой ради экономии, разделение ключей делает эту границу технической, а не договорной. Не нужно надеяться, что никто не забудет, какую модель использовать в каком месте кода — ключ сам не даст вызвать не ту.

Раздельный учёт расхода по ключам

GET /v1/usage отдаёт историю списаний с курсорной пагинацией — но общую на весь аккаунт: все ключи вместе, фильтра по ключу нет, и баланс тоже один на аккаунт. Поэтому делить расход по проектам приходится на своей стороне: логируйте X-Zukko-Cost-Micros по ключу (для нестриминговых запросов), а если нужно строгое разделение, в том числе для потоковых запросов, — заведите отдельный аккаунт:

bash
curl https://api.zukko.pro/v1/usage \
  -H "Authorization: Bearer fetch_ваш_ключ"

Заголовок X-Zukko-Cost-Micros приходит в каждом обычном (не потоковом) ответе — если ключ один на все проекты, эти цифры смешаны и бесполезны для юнит-экономики отдельного продукта. Если ключей несколько и вы пишете стоимость в лог вместе с ключом, расход каждого проекта считается сам, без ручного разбора логов по признаку "какой это был вызов". X-Fetch-Balance-Cents при этом показывает баланс всего аккаунта (в микродолларах, несмотря на название), он общий для всех ключей.

Отзыв одного ключа не трогает остальные

Ключ показывается один раз при создании и может быть скомпрометирован — засветиться в логах, в публичном репозитории, в конфиге, который кто-то не должен был видеть. Если ключ один на всё, отозвать его — значит остановить все проекты разом, пока не выпустишь новый и не обновишь его везде. Если ключи разделены, реакция на утечку — это работа с одним проектом, а не аварийное отключение всего, что использует API.

Idempotency-Key и кэш работают в рамках запроса, а не ключа — но масштаб инцидента всё равно разный

Заголовок Idempotency-Key защищает от повторного списания за один и тот же запрос, а кэш по заголовку X-Zukko-Cache: true (или temperature: 0 — у моделей, которые его принимают, не у Claude) работает на уровне пользователя и запроса. Ни то, ни другое не заменяет разделение ключей — они решают задачу "не заплатить дважды за одно и то же", а не задачу "не дать одному проекту утащить бюджет другого".

Когда одного ключа действительно достаточно

Если это личный проект без внешних участников и без риска, что код с ключом попадёт куда-то ещё — разделение ключей просто лишняя настройка. Порог, после которого разделение того стоит: как только к коду получает доступ кто-то ещё (коллега, фрилансер, CI с внешними раннерами) или проект переходит из "поиграться" в "на нём что-то реально работает".

Как это выглядит на практике

Заводишь ключ в кабинете под явным назначением, сразу выставляешь лимит и список моделей, кладёшь в переменные окружения именно этого проекта:

bash
export FETCH_API_KEY_PROJECT_A=fetch_...
export FETCH_API_KEY_PROJECT_B=fetch_...

Дальше каждый проект обращается к своей переменной, и вопрос "чей это был расход" перестаёт требовать расследования.

Снятие модели с обслуживания — тоже проще пережить по отдельности

Когда модель снимают с обслуживания, ответ прямо называет замену: {"error":{"message":"Модель 'X' снята с обслуживания. Используйте 'Y'","code":"model_retired"}}. Если модель просто выключена в каталоге, ответ другой — 503 с кодом model_disabled, и замену приходится выбирать самому по GET https://api.zukko.pro/v1/models. Если ключ один на несколько проектов и все они завязаны на одну и ту же модель, миграцию приходится делать сразу и одновременно для всех — откатить только часть трафика не получится, ключ либо разрешает старое имя, либо новое.

С отдельными ключами на проект список разрешённых моделей у каждого свой, и переход на замену можно сделать поэтапно: сначала обновить allowed-models на ключе одного проекта, проверить, что всё работает с новым идентификатором, и только потом катить то же самое на остальные. Один и тот же организационный факт — модель заменили — превращается либо в аварийный релиз сразу везде, либо в спокойный поэтапный роллаут, и разница как раз в том, разделены ключи или нет.

zukko

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

за Claude Code & ChatGPT

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