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 по шагам
- Заведите аккаунт и получите ключ
nx_…. - Вынесите base_url и api_key в переменные окружения — не в код.
- Замените имя модели на
deepseek-flashилиkimi-k3.1-pro. - Прогоните один короткий запрос и сверьте usage в ответе с журналом.
- Включите стриминг и проверьте, что первый токен приходит без задержки.
Несколько моделей в одном сервисе
Модель — параметр запроса, поэтому в одном приложении можно держать дешёвую модель для рутины и дорогую для сложных случаев. Например, классификация и краткие ответы идут на 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 передаётся модели. Если провайдер вернёт ошибку формата, она придёт с понятным кодом, а не таймаутом.