OpenAI-совместимый API за рубли

Если ваш код уже работает с OpenAI SDK, переезд — это замена двух строк: адреса и ключа. Модель передаётся полем model, остальной код остаётся прежним.

Python

from openai import OpenAI

client = OpenAI(
    base_url="https://nexgate.ru/v1",
    api_key="nx_ВАШ_КЛЮЧ",
)

resp = client.chat.completions.create(
    model="deepseek-flash",
    messages=[{"role": "user", "content": "Привет"}],
)
print(resp.choices[0].message.content)
print(resp.usage)

Node.js

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://nexgate.ru/v1",
  apiKey: "nx_ВАШ_КЛЮЧ",
});

const resp = await client.chat.completions.create({
  model: "deepseek-flash",
  messages: [{ role: "user", content: "Привет" }],
});
console.log(resp.choices[0].message.content);

curl

curl https://nexgate.ru/v1/chat/completions \
  -H "Authorization: Bearer nx_ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-flash","messages":[{"role":"user","content":"Привет"}]}'

Проверено на живом коде. Формат ответа совпадает с OpenAI: choices, usage с полями prompt_tokens, completion_tokens и total_tokens, а также finish_reason. В заголовке ответа приходит X-Request-Id — по нему поддержка находит запрос в журнале.

Что поддерживается

ВозможностьСтатус
/v1/chat/completionsДа
SSE-стриминг (stream: true)Да
Tool calling / function callingДа
/v1/modelsДа
/v1/balanceДа
Anthropic Messages (/v1/messages)Да
Изображения, аудио, embeddingsНет

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

Стриминг без сюрпризов

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

stream = client.chat.completions.create(
    model="deepseek-flash",
    messages=[{"role": "user", "content": "Напиши функцию"}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content or ""
    print(delta, end="", flush=True)

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

Ошибки и что с ними делать

КодПричинаЧто делать
401Ключ неверный или отозванПроверьте заголовок Bearer и ключ из кабинета
402Закончился балансПополнить; запрос не списан
404Неверный путьBase URL должен заканчиваться на /v1
429Слишком много запросовПодождать и повторить с задержкой
5xxОшибка провайдераПовторить; списания нет

Правило простое: повторять имеет смысл 429 и 5xx, с задержкой и ограничением числа попыток. На 401 и 402 повтор бесполезен — сначала нужно исправить ключ или пополнить баланс. Задержку между попытками лучше делать растущей: мгновенный повтор почти всегда упирается в тот же лимит. Три-четыре попытки — разумный предел, дальше ошибка должна уйти в журнал приложения.

Переезд с OpenAI по шагам

  1. Заведите аккаунт и получите ключ nx_….
  2. Вынесите base_url и api_key в переменные окружения — не в код.
  3. Замените имя модели на deepseek-flash или kimi-k3.1-pro.
  4. Прогоните один короткий запрос и сверьте usage в ответе с журналом.
  5. Включите стриминг и проверьте, что первый токен приходит без задержки.

Несколько моделей в одном сервисе

Модель — параметр запроса, поэтому в одном приложении можно держать дешёвую модель для рутины и дорогую для сложных случаев. Например, классификация и краткие ответы идут на DeepSeek Flash, а разбор длинного документа — на Kimi K3.1 Pro. Ключ и Base URL при этом одни.

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

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

Модели и цены

МодельIDВходКэшВыходКонтекст
DeepSeek Flash deepseek-flash 35 ₽ 5 ₽ 100 ₽ 1M
Kimi K3.1 Pro kimi-k3.1-pro 250 ₽ 90 ₽ 1300 ₽ 1 048 576

Цены за 1 млн токенов, актуально на 24.10.2026.

Проверка баланса

curl https://nexgate.ru/v1/balance \
  -H "Authorization: Bearer nx_ВАШ_КЛЮЧ"

Ответ содержит остаток в микротокенах и в рублях — удобно вывести в мониторинг. Баланс также виден в кабинете и на странице тарифов.

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

Код придётся переписывать?

Нет. Меняются base_url и ключ. Имена полей, структура сообщений и разбор ответа остаются как в OpenAI.

Что с таймаутами на длинных ответах?

Шлюз держит соединение и передаёт стрим по мере генерации. Для Reasoning-моделей дайте клиенту таймаут с запасом: размышления занимают время.

Можно ли использовать несколько ключей?

Да, ключей может быть несколько — например, отдельный на сервис и отдельный на локальную машину. Баланс общий, журнал показывает ключ по каждому запросу.

Как понять, что запрос ушёл через шлюз?

По заголовку X-Request-Id и строке в журнале кабинета: модель, токены, цена. Если строки нет, запрос ушёл на другой адрес.

Есть ли поддержка потокового JSON?

Да, response_format с типом JSON передаётся модели. Если провайдер вернёт ошибку формата, она придёт с понятным кодом, а не таймаутом.