Как поставить дневной и месячный лимит на API-ключ

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

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

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

Где настраивается

Лимит задаётся не глобально на аккаунт, а на странице конкретного ключа в кабинете: https://zukko.pro/app/api-keys. У каждого ключа можно независимо указать:

  • дневной лимит расхода в долларах;
  • месячный лимит расхода в долларах;
  • список моделей, разрешённых именно этому ключу (это отдельная, но соседняя настройка на той же странице).

Ключи создаются в этом же разделе, и напомню: значение ключа показывается один раз, в момент создания, — сохраните его сразу, лимиты потом можно поменять, а сам ключ восстановить нельзя.

Чем это отличается от других 429

Общего лимита частоты запросов на шлюзе нет. 429 от самого Zukko — это ровно два кода, и оба означают исчерпание бюджета конкретного ключа:

bash
{
  "error": {
    "message": "Daily spend limit reached for this API key",
    "code": "key_daily_limit_reached"
  }
}
bash
{
  "error": {
    "message": "Monthly spend limit reached for this API key",
    "code": "key_monthly_limit_reached"
  }
}

Любой другой 429 (без этих кодов) приходит от провайдера модели — это его ограничение частоты, оно не про деньги и обычно снимается через секунды-минуты (замедлились — и снова можно отправлять запросы). key_daily_limit_reached держится до 00:00 UTC, key_monthly_limit_reached — до начала следующего месяца. Если код в логах — один из этих двух, повторять запрос раньше времени бессмысленно, лимит сброса не ускорить.

Зачем это нужно на практике

Ключ для подрядчика или стажёра. Выдавая доступ человеку, с которым нет полного доверия к его коду, разумно сразу поставить дневной потолок в $5–10, а не потом разбираться, почему баланс аккаунта обнулился за ночь.

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

Разделение бюджета между проектами на одном аккаунте. Если под one аккаунтом крутится три отдельных сервиса, у каждого свой ключ с собственным месячным лимитом — это позволяет видеть перерасход конкретного сервиса раньше, чем закончится общий баланс.

Ключ для CI/тестов. Тестовые прогоны, которые дергают модель на каждый пуш, иногда неожиданно зацикливаются (например, из-за бага в retry-логике). Дневной лимит превращает потенциальный «слил весь баланс за ночь из-за бага» в «упёрлись в $10 и тесты начали падать с понятной ошибкой».

Как обработать это в коде

bash
import requests

def call_with_limit_check(payload, key):
    resp = requests.post(
        "https://api.zukko.pro/v1/chat/completions",
        headers={"Authorization": f"Bearer {key}"},
        json=payload,
    )
    if resp.status_code == 429:
        data = resp.json()
        code = data.get("error", {}).get("code")
        if code == "key_daily_limit_reached":
            raise RuntimeError("Дневной лимит ключа исчерпан, ждать до 00:00 UTC или сменить ключ")
        if code == "key_monthly_limit_reached":
            raise RuntimeError("Месячный лимит ключа исчерпан")
        raise RuntimeError("429 от провайдера модели, стоит подождать и повторить")
    resp.raise_for_status()
    return resp.json()

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

Резервный ключ вместо повышения лимита на лету

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

Лимит ключа и баланс аккаунта — разные вещи

Лимит на ключ не связан напрямую с фактическим балансом аккаунта: можно поставить ключу дневной лимит в $20, даже если на счету $500, — это просто потолок для конкретного ключа, а не отражение реальных денег. Общий остаток средств проверяется отдельно через GET /v1/balance (нужен ключ), и его тоже стоит мониторить: лимит ключа защищает от перерасхода одним ключом, а не от того, что баланс всего аккаунта в принципе закончится и все ключи разом получат 402.

Итог

Лимит на ключ — это не защита от исчерпания баланса аккаунта в принципе, а способ заранее ограничить, сколько именно может потратить один конкретный ключ, прежде чем понадобится ручное вмешательство. Настраивается на странице ключа в кабинете, срабатывает как 429 с кодом key_daily_limit_reached или key_monthly_limit_reached, и в коде эти коды стоит обрабатывать отдельно от 429 провайдера.

zukko

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

за Claude Code & ChatGPT

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