halfoff.devДокументация
Получить ключ
Models API

Как работает шлюз

Один хост нативно обслуживает API Anthropic, OpenAI и Gemini. Конечные точки, базовые URL, допустимые id моделей, заголовки аутентификации и то, что Half Off Dev изменяет.

Смысл Half Off Dev в том, что вы сохраняете уже написанный код. Claude, GPT и Gemini обслуживаются каждый в собственном формате своего вендора, на одном хосте, за половину прайсовой цены. Ваш SDK отправляет тот же JSON, что и вендору; меняются только базовый URL и ключ.

Прослойки-переводчика посередине нет. Запрос в стиле Anthropic передаётся каналу, который говорит на Anthropic, запрос в стиле OpenAI — каналу, который говорит на OpenAI, и так далее. За исключением двух небольших правок, тело приходит так, как вы его написали, поэтому возможности вендоров вроде thinking, cache_control, tools или generationConfig работают ровно так, как описано в справочнике каждого вендора.

Конечные точки вкратце#

Передайте SDK базовый URL из этой таблицы и позвольте ему построить путь.

Формат протоколаПутиБазовый URL для SDKРуководство
Anthropic MessagesPOST /v1/messages, POST /v1/messages/count_tokenshttps://halfoff.devAnthropic Messages
OpenAI Chat CompletionsPOST /v1/chat/completionshttps://halfoff.dev/v1OpenAI
OpenAI Responses (только GPT)POST /v1/responseshttps://halfoff.dev/v1Responses API
GeminiPOST /v1beta/models/{model}:generateContent, :streamGenerateContent, :countTokenshttps://halfoff.devGemini
Каталог моделейGET /v1/models, GET /v1beta/modelsЦены

Всё, чего нет в этом списке, не поддерживается; см. что вызвать нельзя.

Передача ключа#

Каждая конечная точка ищет ключ в четырёх местах, так что то, что SDK делает по умолчанию, просто работает:

МестоТипичный отправитель
Authorization: Bearer ho-...OpenAI SDK, Claude Code через ANTHROPIC_AUTH_TOKEN, обычные HTTP-клиенты
x-api-key: ho-...Anthropic SDK
x-goog-api-key: ho-...Google Gen AI SDK, Gemini CLI
?key=ho-...Фрагменты REST для Gemini

Две конечные точки каталога публичны и ключа не требуют. Создание, ограничение и ротация ключей описаны в разделе Аутентификация.

Нативный формат для каждого семейства моделей#

Каждое семейство спроектировано вокруг одного API. Обращаясь к нему там, вы получаете все возможности вендора.

СемействоНативный форматТакже принимается сегодня
Claude (claude-*)Anthropic MessagesOpenAI Chat Completions, Gemini
GPT (gpt-*)OpenAI Chat Completions, OpenAI ResponsesAnthropic Messages, Gemini
Gemini (gemini-*)GeminiOpenAI Chat Completions, Anthropic Messages

Использование ненативного формата означает, что канал преобразует ваш запрос для модели, и параметры, специфичные для вендора, по пути могут быть отброшены или отклонены. cache_control и thinking у Anthropic, reasoning_effort у OpenAI и thinkingConfig у Gemini гарантированно работают только в родном формате модели.

Какие форматы принимает модель, зависит от обслуживающих её каналов, поэтому список может меняться. Маркетплейс моделей показывает актуальные форматы для каждой модели. Если ни один канал не принимает ваш формат, вы получите 404 с сообщением вроде Model 'gpt-6.1-sol' is not served in gemini format in any group.

Id моделей#

Канонические id — те, что в GET /v1/models, например claude-opus-5.5, gpt-6.1-sol или gemini-3-flash. Инструменты, которые пишут id по-своему, всё равно попадают на нужную модель, потому что шлюз нормализует полученное:

ОтправленоТрактуется как
Claude-Opus-5-5claude-opus-5.5
anthropic/claude-opus-5.5claude-opus-5.5
claude-haiku-4-5-20251001claude-haiku-4.5
gemini-3.1-pro-previewgemini-3.1-pro
models/gemini-3-flashgemini-3-flash

Нормализация приводит id к нижнему регистру, отбрасывает ведущие vendor/ или models/, считает . и - одним и тем же символом и убирает завершающую дату (-20251001), -preview или -latest. Биллинг, логи и списки разрешённых моделей ключа всегда записывают канонический id.

Id, который ни с чем не сопоставился, возвращает 404: Model '<id>' is not available. See GET /v1/models.

Путь одного запроса#

  1. Проверка ключа. Неактивный или просроченный ключ получает 401.
  2. Поиск модели. Id нормализуется, как описано выше, и сравнивается со списком разрешённых моделей ключа; модель вне списка получает 403.
  3. Проверка баланса. Баланс, равный нулю или ниже, либо ключ, исчерпавший лимит расходов, получает 402.
  4. Лимиты ключа. Действуют ограничения ключа на число запросов в минуту и на параллелизм (429). Подробности в разделе Лимиты запросов и переключение.
  5. Выбор канала. Уже идущий разговор возвращается на канал, где лежит его кеш промпта; новый попадает на самый здоровый канал своей группы каналов. См. Кеширование промптов.
  6. Вызов модели. Канал, отказавший до какого-либо вывода, заменяется другим, всего до трёх попыток. Потоки удерживаются до первого токена, так что замена незаметна. См. Потоковая передача.
  7. Учёт. Число токенов в ответе определяет списание по половине прайсовой цены, и оно уходит с баланса в течение нескольких секунд. См. Цены.

Что шлюз изменяет#

В тело вносятся две правки, и больше ничего:

  1. model. Шлюз записывает id в написании, которое ожидает обслуживающий канал. В результате поле model в ответе может иметь форму вендора, например claude-opus-5-5.
  2. Usage в потоках OpenAI. Каждый потоковый запрос Chat Completions получает stream_options.include_usage: true, чтобы вызов можно было тарифицировать. Поэтому ваш поток заканчивается одним дополнительным фрагментом, у которого массив choices пуст и который несёт usage. Убедитесь, что ваш цикл терпимо относится к пустому choices.

Отправленные вами заголовки отбрасываются, за тремя исключениями, которые пересылаются: anthropic-version, anthropic-beta и openai-beta. Если anthropic-version отсутствует, подставляется 2023-06-01. На путях Gemini параметры запроса вроде alt=sse проходят, а key удаляется.

Тела ответов возвращаются байт в байт. content-type и cache-control сохраняются, а шлюз добавляет собственные заголовки; id запросов вендоров и заголовки лимитов вендоров не передаются.

Если модель отклоняет запрос как некорректный (400, 404, 413, 422), вы получаете этот статус и причину в формате ошибок того API, к которому обращались. Любой другой сбой сначала повторяется на другом канале. Подробнее в разделах Лимиты запросов и переключение и Ошибки.

Заголовки в каждом ответе#

ЗаголовокСодержимое
x-halfoff-request-idId этого вызова, вроде req_6f1c.... Это ключ к записи в Логах; приводите его, когда пишете в поддержку.
x-halfoff-groupГруппа каналов, которая ответила, например gpt-value.
x-halfoff-channelМетка канала этой группы в URL-кодировке, например Pooled%20capacity.
x-halfoff-queued-msПрисутствует, только если вызов ждал освобождения мощности: время ожидания в миллисекундах.

Первые три перечислены в access-control-expose-headers, поэтому клиентский JavaScript может их читать.

Что вызвать нельзя#

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

  • OpenAI: сохранённые ответы (/v1/responses/{id}), устаревшие Completions, эмбеддинги, аудио, файлы и пакеты. Сам Responses API работает только для моделей GPT.
  • Anthropic: Message Batches, Files и получение одной модели по id.
  • Gemini: cachedContents (явное кеширование контекста), загрузка файлов, embedContent и Live API.

О подключении конкретного клиента см. Интеграции.