NormaHub knowledge base

Документация NormaHub API

От выпуска первого ключа до потоковых ответов и обработки ошибок. Все примеры используют актуальный production gateway.

Быстрый старт

NormaHub совместим с OpenAI SDK: в готовом коде достаточно заменить base_url и ключ. Три шага до первого ответа модели:

1. Создайте API-ключ

Ключ выпускается в личном кабинете. Значение показывается один раз при выпуске — сохраните его сразу. У каждого ключа есть собственный лимит расходов (spend limit), настраиваемый в кабинете.

2. Подключите библиотеку

# Python
pip install openai

# Node.js
npm install openai

3. Замените 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.

Аутентификация

OpenAI-compatible base URLhttps://api.normahub.cc/v1
Anthropic SDK base URLhttps://api.normahub.cc
АвторизацияBearer YOUR_API_KEY
Форматapplication/json
МоделиGET /v1/models

Все запросы к gateway требуют заголовок Authorization: Bearer <ключ>. Не помещайте ключ в URL, browser bundle, мобильное приложение, публичный репозиторий или логи.

Authorization: Bearer ВАШ_API_КЛЮЧ

Совместимость с OpenAI

NormaHub реализует OpenAI-совместимый Chat Completions API — большая часть OpenAI-кода и инструментов работает без изменений:

OpenAI SDK (Python / Node / Go / …)Да — смените base_url и ключ
Streaming (SSE)Responses API; для других маршрутов не включён
Anthropic SDK (/v1/messages)Да, для поддерживаемых Claude-моделей
Функции / tool callingТранзитом — если поддерживает модель и провайдер
Картинки на вход (vision)Транзитом — если поддерживает модель
temperature · top_p · max_tokens · stopПо модели; неподдерживаемые поля могут быть отклонены
Кэш контекста (cached tokens)Показывается в usage, отдельной скидки нет
Генерация изображенийЧерез чат личного кабинета (gpt-image-2)

Не рассчитывайте, что неизвестный параметр будет молча проигнорирован: провайдер может вернуть 400. Отправляйте только поля, поддержанные выбранной моделью и эндпоинтом.

Поддерживаемые маршруты

GET/v1/models

Активные модели, разрешённые вашему API-ключу. OpenAI-совместимый формат.

POST/v1/responses

Responses API. Единственный маршрут с полноценным streaming (SSE).

POST/v1/chat/completions

OpenAI-compatible диалог из массива messages. Без streaming.

POST/v1/messages

Anthropic-compatible Messages для Claude-моделей.

POST/v1/completions

Legacy 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": "Привет!"}
    ]
  }'
Streaming для Chat Completions и Messages в текущей версии gateway не включён: с stream: true вернётся 503 streaming_not_enabled. Для потоковой генерации используйте Responses API.

Для 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_allowed

Scope ключа не разрешает этот эндпоинт.

413request_too_large

Размер тела превышает 2 МБ.

429budget_exceeded

Достигнут лимит расходов (spend limit) API-ключа. Поднимите его в кабинете.

429rate_limit_exceeded

Слишком частые запросы или много невалидных ключей. Повторите позже.

503streaming_not_enabled

Streaming не включён для этого маршрута. Используйте stream: false.

503provider_not_configured

Провайдер модели не настроен (mock-режим).

503provider_unavailable

Внешний провайдер недоступен; все маршруты failover исчерпаны.

Ошибки и повторы

429

Сначала прочитайте error.code. budget_exceeded и insufficient_balance требуют действия в кабинете — повтор с тем же ключом не поможет. rate_limit_exceeded — повторите с exponential backoff.

5xx

1–2 повтора с джиттером, если полезного ответа ещё не было. После начала осмысленного ответа не повторяйте автоматически — риск дублирования.

400 / 413

Неверный запрос или превышен размер. Исправьте тело, повтор без изменений бессмыслен.

401 / 403

Проблема с ключом, scope или разрешением модели. Проверьте ключ и настройки.

402

Пополните баланс или поднимите spend limit ключа.

Для длинных запросов задавайте раздельные connect timeout и read timeout — 60 секунд общего таймаута для агентных сценариев может не хватить. Автоматически повторяйте только временные ошибки и всегда с ограниченным exponential backoff.

Готовые инструкции