allowed_models: как разрешить ключу только нужные модели

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

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

По умолчанию ключ fetch_... может вызывать любую модель из каталога — от дешёвого glm-5.3-flash до claude-opus-5-5. Для многих сценариев это лишняя степень свободы: если ключ выдан узкому сервису, ему может быть нужна ровно одна модель, а не весь каталог.

Что это за настройка

На странице конкретного ключа в кабинете (https://zukko.pro/app/api-keys) можно задать список моделей, разрешённых именно этому ключу. Запрос с этим ключом к любой модели вне списка получит отказ ещё до обращения к провайдеру.

Настройка соседствует с дневным/месячным лимитом расхода на той же странице ключа — это две независимые, но дополняющие друг друга формы ограничения одного ключа: лимит контролирует «сколько», список моделей контролирует «чем».

Как выглядит отказ

bash
{
  "error": {
    "message": "Model 'claude-opus-5-5' is not allowed for this API key",
    "code": "model_not_allowed_for_key"
  }
}

Статус — 403. Это стоит отличать от 404 model_not_found: модель существует в каталоге, запрос синтаксически корректен, но конкретно этому ключу политика запрещает её вызывать.

bash
curl https://api.zukko.pro/v1/chat/completions \
  -H "Authorization: Bearer fetch_ограниченный_ключ" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "messages": [{"role": "user", "content": "..."}]
  }'
# -> 403 model_not_allowed_for_key, если claude-opus-5-5 не в списке разрешённых для этого ключа

Зачем это нужно

Защита от случайного вызова дорогой модели. claude-opus-5-5 стоит $0.30/$0.30 за 1M токенов, а glm-5.3-flash — $0.04/$0.04, то есть в 7,5 раза дешевле. Если сервис по задумке должен работать только с быстрой и дешёвой моделью, а в коде из-за бага или скопированного примера окажется другой model, allowlist остановит вызов до списания, а не после.

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

Раздельные ключи под разные компоненты одного продукта. У бэкенд-сервиса, которому нужен claude-sonnet-5 для основной логики, и у отдельного модуля классификации обращений, которому нужен только glm-5.3-flash, могут быть два ключа с непересекающимися списками разрешённых моделей — так утечка одного ключа не открывает доступ ко всему каталогу.

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

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

bash
import requests

ALLOWED_FALLBACK = "glm-5.3-flash"

def call_model(model_id, messages, key):
    resp = requests.post(
        "https://api.zukko.pro/v1/chat/completions",
        headers={"Authorization": f"Bearer {key}"},
        json={"model": model_id, "messages": messages},
    )
    if resp.status_code == 403:
        data = resp.json()
        if data.get("error", {}).get("code") == "model_not_allowed_for_key":
            # Не пытаемся угадать замену сами — здесь это не входит в ответ сервера,
            # в отличие от model_retired. Логируем и решаем осознанно.
            raise PermissionError(
                f"Модель '{model_id}' не разрешена для этого ключа. "
                f"Проверьте allowed_models в кабинете или используйте другой ключ."
            )
    resp.raise_for_status()
    return resp.json()

Важное отличие от ошибки model_retired: там сервер сам называет замену в тексте сообщения, потому что замена объективна — модель снята с обслуживания для всех. Здесь же ограничение специфично для конкретного ключа и отражает вашу же политику, поэтому сервер не подсказывает альтернативную модель — решение, что делать (расширить список или использовать другой ключ), остаётся за вами.

Частая ошибка при настройке

Если сервис использует несколько моделей поочерёдно — например, основной claude-sonnet-5 и запасной gpt-5.6-sol на случай 503 provider_unavailable у первого — обе модели должны быть в allowlist заранее. Иначе логика фолбэка при недоступности провайдера сама упрётся в 403, и вместо переключения на резервную модель сервис получит вторую ошибку поверх первой.

Пример: ключ только под дешёвые модели

Допустим, отдельный модуль вызывает исключительно glm-5.3-flash и gpt-5.6-luna — для классификации обращений и коротких пересказов. Разумно выпустить под него ключ, в allowlist которого нет ни одной другой модели. Тогда даже если в код этого модуля по ошибке попадёт вызов claude-opus-5-5 (например, при копировании чужого примера с другой моделью), запрос упадёт с 403 model_not_allowed_for_key вместо того, чтобы тихо списать деньги за вызов модели, которая этому модулю в принципе не нужна.

Итог

allowed_models — это позиционирование ключа под конкретную задачу, а не защита от лишних трат сама по себе (для этого есть отдельный лимит расхода). Настраивается на странице ключа в кабинете, срабатывает как 403 model_not_allowed_for_key, и, в отличие от model_retired, не предлагает замену — потому что ограничение ваше собственное, а не следствие изменений в каталоге.

zukko

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

за Claude Code & ChatGPT

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

allowed_models: список разрешённых моделей для ключа · Zukko