Быстрый старт
NormaHub совместим с OpenAI SDK: в готовом коде достаточно заменить base_url и ключ. Три шага до первого ответа модели:
1. Создайте API-ключ
Ключ выпускается в личном кабинете. Значение показывается один раз при выпуске — сохраните его сразу. У каждого ключа есть собственный лимит расходов (spend limit), настраиваемый в кабинете.
2. Подключите библиотеку
# Python
pip install openai
# Node.js
npm install openai3. Замените base_url и ключ
# pip install openai
from openai import OpenAI
client = OpenAI(
api_key="ВАШ_API_КЛЮЧ", # ← ключ из личного кабинета NormaHub
base_url="https://api.normahub.cc/v1",
)
resp = client.chat.completions.create(
model="MODEL_ID", # ← ID из GET /v1/models
messages=[{"role": "user", "content": "Привет!"}],
)
print(resp.choices[0].message.content)Код остаётся таким же, как для OpenAI — меняются только адрес и ключ. Или просто вызовите API через curl:
curl https://api.normahub.cc/v1/chat/completions \
-H "Authorization: Bearer $NORMAHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "MODEL_ID",
"messages": [
{"role": "user", "content": "Ответь одним предложением"}
],
"max_tokens": 256
}'Замените MODEL_ID на идентификатор из ответа GET /v1/models.
Аутентификация
https://api.normahub.cc/v1https://api.normahub.ccBearer YOUR_API_KEYapplication/jsonGET /v1/modelsВсе запросы к gateway требуют заголовок Authorization: Bearer <ключ>. Не помещайте ключ в URL, browser bundle, мобильное приложение, публичный репозиторий или логи.
Authorization: Bearer ВАШ_API_КЛЮЧСовместимость с OpenAI
NormaHub реализует OpenAI-совместимый Chat Completions API — большая часть OpenAI-кода и инструментов работает без изменений:
Не рассчитывайте, что неизвестный параметр будет молча проигнорирован: провайдер может вернуть 400. Отправляйте только поля, поддержанные выбранной моделью и эндпоинтом.
Поддерживаемые маршруты
/v1/modelsАктивные модели, разрешённые вашему API-ключу. OpenAI-совместимый формат.
/v1/responsesResponses API. Единственный маршрут с полноценным streaming (SSE).
/v1/chat/completionsOpenAI-compatible диалог из массива messages. Без streaming.
/v1/messagesAnthropic-compatible Messages для Claude-моделей.
/v1/completionsLegacy Completions для совместимых клиентов. Без streaming.
Конкретные маршруты зависят от модели: смотрите поля endpoints и capabilities в каталоге. Если модель недоступна выбранному ключу, вернётся 403 model_not_allowed.
Каталог моделей
Каталог моделей NormaHub динамический: список, цены и лимиты синхронизируются с провайдерами и отображаются на странице моделей. Программно — тем же ключом:
curl https://api.normahub.cc/v1/models \
-H "Authorization: Bearer $NORMAHUB_API_KEY"{
"object": "list",
"data": [
{
"id": "gpt-5.6-sol",
"object": "model",
"owned_by": "normahub",
"endpoints": ["/v1/responses", "/v1/chat/completions"],
"capabilities": ["vision", "tools", "reasoning"],
"context_window": 353000,
"max_output_tokens": 128000,
"pricing": {
"currency": "RUB",
"input_price_per_million": "24.00",
"output_price_per_million": "96.00"
}
}
]
}Цены указаны за 1 млн токенов, раздельно для входа и выхода. Валюта счёта — рубли (RUB). Стоимость запроса = входные токены × входная цена + выходные токены × выходная цена, делённое на миллион.
Responses API
{
"model": "MODEL_ID",
"input": "Кратко объясни назначение API gateway",
"max_output_tokens": 256,
"stream": false
}Для потокового ответа установите stream: true. Gateway возвращает SSE и завершает учёт запроса после закрытия потока — даже если соединение оборвёт клиент, usage и стоимость будут корректно финализированы.
Chat Completions и Messages
/v1/chat/completions принимает массив сообщений с ролями. /v1/messages — нативный формат Anthropic Messages для Claude-моделей; используйте его с официальным Anthropic SDK.
{
"model": "MODEL_ID",
"messages": [{ "role": "user", "content": "Привет" }],
"max_tokens": 256
}curl https://api.normahub.cc/v1/messages \
-H "Authorization: Bearer $NORMAHUB_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "MODEL_ID",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Привет!"}
]
}'Для OpenAI-compatible SDK указывайте https://api.normahub.cc/v1. Для официального Anthropic SDK указывайте https://api.normahub.cc: клиент самостоятельно добавляет путь /v1/messages.
Стриминг (SSE)
Потоковая передача доступна в POST /v1/responses с stream: true. Ответ приходит как text/event-stream: читайте события последовательно и извлекайте содержимое из строк data:.
curl https://api.normahub.cc/v1/responses \
-H "Authorization: Bearer $NORMAHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "MODEL_ID",
"input": "Расскажи короткую историю",
"stream": true
}'Через OpenAI SDK:
stream = client.responses.stream(
model="MODEL_ID",
input="Расскажи короткую историю",
)
for event in stream:
print(event, flush=True)Не считайте разрыв TCP успешным завершением: дождитесь финального события потока, закройте reader и сохраните X-Request-Id. После частично полученного ответа не выполняйте автоматический повтор без защиты от дублирования.
Usage, стоимость и журнал
Успешный ответ содержит usage провайдера либо оценку токенов, если upstream не вернул точные значения. Gateway сохраняет входные, выходные и общие токены, финальную стоимость, cached-токены и идентификатор запроса.
curl https://api.normahub.cc/v1/chat/completions \
-H "Authorization: Bearer $NORMAHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "MODEL_ID",
"messages": [{"role": "user", "content": "Привет!"}]
}' -D -Сохраняйте заголовок X-Request-Id — он связывает клиентский запрос с записью в журнале личного кабинета и нужен поддержке для поиска запроса. История запросов, расход по моделям и баланс — в личном кабинете.
Баланс и лимиты
Оплата по факту использования: запросы списываются с предоплаченного баланса в рублях по цене за токены каждой модели. Подписок и тарифных планов нет.
У каждого API-ключа есть собственный spend limit — лимит расходов, после превышения которого запросы отклоняются с 429 budget_exceeded. Лимит меняется в личном кабинете в любой момент; balance и лимиты ключей независимы друг от друга.
Пополнение — криптовалютой через платёжного провайдера, инвойс действует ограниченное время. При промокодах активация привязана к IP. Баланс не может уйти в минус: запрос при недостатке средств завершается с 402 до генерации.
Rate Limits
Частота запросов ограничена на аккаунт и общая для всех его API-ключей. Превышение возвращает 429 rate_limit_exceeded. Отдельного лимита параллельных стримов нет: действует общий аккаунтный лимит запросов в минуту.
Невалидные ключи тоже под rate limit: серия запросов с неверным ключом с одного IP приводит к прогрессивной блокировке. Это защита от перебора — не отправляйте заведомо неверные ключи.
Нужны увеличенные лимиты — напишите в поддержку.
Коды ошибок
Ошибки gateway возвращаются в формате OpenAI:
{
"error": {
"message": "Insufficient balance",
"type": "invalid_request_error",
"code": "insufficient_balance"
}
}400invalid_jsonТело запроса — не валидный JSON.
400invalid_requestНекорректные параметры запроса. Исправьте тело, не повторяйте как есть.
401invalid_api_keyКлюч отсутствует, неверен или отозван.
402insufficient_balanceНедостаточно средств на балансе для выполнения запроса.
402provider_balance_depletedБаланс провайдера исчерпан; gateway переключается на резервный маршрут.
403model_not_allowedМодель не разрешена этому API-ключу.
403model_unavailableМодель не сертифицирована для этого эндпоинта.
403scope_not_allowedScope ключа не разрешает этот эндпоинт.
413request_too_largeРазмер тела превышает 2 МБ.
429budget_exceededДостигнут лимит расходов (spend limit) API-ключа. Поднимите его в кабинете.
429rate_limit_exceededСлишком частые запросы или много невалидных ключей. Повторите позже.
503streaming_not_enabledStreaming не включён для этого маршрута. Используйте stream: false.
503provider_not_configuredПровайдер модели не настроен (mock-режим).
503provider_unavailableВнешний провайдер недоступен; все маршруты failover исчерпаны.
Ошибки и повторы
429Сначала прочитайте error.code. budget_exceeded и insufficient_balance требуют действия в кабинете — повтор с тем же ключом не поможет. rate_limit_exceeded — повторите с exponential backoff.
5xx1–2 повтора с джиттером, если полезного ответа ещё не было. После начала осмысленного ответа не повторяйте автоматически — риск дублирования.
400 / 413Неверный запрос или превышен размер. Исправьте тело, повтор без изменений бессмыслен.
401 / 403Проблема с ключом, scope или разрешением модели. Проверьте ключ и настройки.
402Пополните баланс или поднимите spend limit ключа.
Для длинных запросов задавайте раздельные connect timeout и read timeout — 60 секунд общего таймаута для агентных сценариев может не хватить. Автоматически повторяйте только временные ошибки и всегда с ограниченным exponential backoff.
Готовые инструкции
Остались вопросы — напишите в Telegram поддержки.