Новинка: Nano Banana 2.1Попробовать модель
API Hubs

Эксплуатация

Выход в продакшен

Работа с ключами, вебхуки вместо опроса, идемпотентность, конечные состояния, правила повторов по коду ошибки, контроль баланса, лимиты запросов и хранение медиа.

9 мин чтенияразделов: 14

Перед запуском проверьте хранение API-ключей, опрос задач, повторные вебхуки, повторные запросы, уведомления о балансе и загрузку файлов. Пройдите чек-лист и запустите примеры с ограничениями до подключения реального трафика.


Запустите ограниченный пример Chat Completions

Проверено 19 октября 2026, Python 3.14.4, стандартная библиотека. Скачайте готовый клиент. Он отправляет один запрос без streaming на /api/v1/chat/completions, ограничивает ответ 32 токенами и выводит ответ и usage. Ключ хранится в серверной переменной:

sh
export API_STOCK_KEY="ваш-серверный-ключ"
export API_STOCK_ORIGIN="https://api.apihubs.ru"
python3 chat-completions.py

Используется GPT-6 Luna. Проверенный публичный тариф: $0.12 за миллион входных и $0.60 за миллион выходных токенов; этот короткий запрос стоит меньше $0.001. Сверьте текущую таблицу модели перед изменением промпта и лимита. Ошибка HTTP или таймаут завершает скрипт: потерянный ответ не доказывает отсутствие списания, поэтому автоматического повтора нет.

Это HTTP-клиент Chat Completions. Пример не подтверждает совместимость со всеми OpenAI endpoints, streaming SDK или генерацией медиа. Для асинхронной музыки используйте пример Suno с сохранением task ID. Порядок настройки ключа описан в справочнике.

Ключи API

  • Передавайте ключ в заголовке, а не в строке запроса. Поддерживаются Authorization: Bearer sk-… и x-api-key: sk-…. Строки запроса попадают в логи прокси и историю браузера.
  • Не отправляйте ключ в браузер или мобильное приложение. У публичного API нет ключей с ограниченными правами, коротким сроком действия или безопасной для CORS моделью. Ключ даёт полный доступ к аккаунту и балансу. Проксируйте запросы генерации через свой бэкенд.
  • Создавайте отдельный ключ для каждого окружения и сервиса. Staging, production и фоновые воркеры должны отключаться независимо, потому что ключи отзываются по одному. Разделение ключей также позволяет понять, какой сервис выполнял конкретные запросы.
  • Меняйте ключи с периодом пересечения. Создайте новый ключ, разверните его, убедитесь, что трафик переключился, и только потом отключайте старый. Отключение действует со следующего запроса: успешные проверки ключа кэшируются на час, но при отзыве ключа эта запись удаляется.
  • Храните ключи в менеджере секретов, а не в .env, добавленном в репозиторий. При утечке действуйте как при компрометации банковской карты: сначала отключите ключ, затем разбирайтесь в причинах.

Предпочитайте вебхуки опросу

Передайте URL в поле webhook при создании задачи, и готовый результат будет отправлен вам методом POST. Тело запроса побайтово совпадает с ответом метода проверки статуса: тот же объект { code, data: { taskId, status, files, output, createdTime, errorMessage } }. Оба способа можно обрабатывать одним кодом.

json
{
"model": "veo3.1-fast",
"input": { "prompt": "…", "aspectRatio": "16:9" },
"webhook": "https://example.com/hooks/api-stock/8f2c1e9a-b40d-4c77-9a1e-secret"
}

Практические правила:

  • Заголовка с подписью нет. Способ аутентификации задаётся самим URL. Используйте длинный случайный сегмент пути или параметр запроса, сравнивайте его за постоянное время, а в остальном считайте endpoint публичным.
  • Не используйте payload как подтверждение полномочий. Найдите taskId в своей базе до обработки. Задача, которую вы не отправляли, не становится вашей только потому, что так утверждает тело запроса.
  • Быстро отвечайте кодом 2xx. Доставка повторяется 13 раз с экспоненциальной задержкой, начиная с 10 секунд — примерно 22 часа попыток. Любой ответ вне диапазона 200–299 считается ошибкой. Поставьте работу в очередь и ответьте.
  • Ожидайте дубликаты. Если ответ истёк по таймауту после фиксации изменений обработчиком, доставка всё равно считается неудачной и будет повторена.

Подробности — в разделе Вебхуки.


Опрос, когда без него нельзя

Некоторые окружения не позволяют открыть входящий endpoint. В этом случае можно опрашивать GET /api/v1/task/status/{taskId}, но контролируйте частоту.

  • Подбирайте интервал под тип результата. Изображения создаются за секунды, видео и музыка — за минуты. Ориентир: 3 секунды для изображений и 8–10 секунд для аудио и видео. Опрос чаще раза в секунду лишь расходует лимит запросов.
  • Увеличивайте задержку при повторяющемся processing. Начните с указанного интервала и постепенно поднимайте его, например 5, 10 и 20 секунд с максимумом 30 секунд. Если рендер идёт две минуты, он вряд ли завершится именно в следующие 500 мс.
  • Установите общий дедлайн и считайте его таймаутом своего процесса, а не задачи. После прекращения опроса генерация продолжится на сервере. Сохраните taskId и вернитесь к нему позднее вместо повторной отправки.
  • Опрашивайте из воркера, а не из обработчика пользовательского запроса. Запрос, заблокированный на четыре минуты, остаётся ошибкой независимо от поведения API.
  • Сильно увеличивайте задержку после 429. Подробнее — в разделе Лимиты запросов.

Идемпотентность

У endpoint создания нет ключа идемпотентности. Вашей опорой для идемпотентности служит taskId — привязывайте к нему все последующие операции.

  • Сохраняйте taskId в одной транзакции с записью о намерении выполнить генерацию. Если процесс завершится между HTTP-запросом и записью, вы заплатите за результат, который не сможете получить.
  • Сделайте обработчики вебхуков и опроса идемпотентными по taskId. Оба канала могут доставить одно конечное состояние, а вебхук может прийти несколько раз. Достаточно upsert по taskId.
  • Не отправляйте задачу повторно, чтобы «проверить» её. Второй create — это вторая генерация и второе списание. Запрашивайте уже полученный taskId.
  • Защитите повторы, инициированные пользователем. Добавьте debounce кнопки или привяжите запрос к стабильному идентификатору в своей системе, чтобы двойной клик не превратился в два списания.

Обрабатывайте каждое конечное состояние

Состояний четыре, и только два из них конечные:

СтатусКонечныйЗначение
not_startedнетпоставлена в очередь, ещё не отправлена провайдеру
processingнетвыполняется у провайдера
finishedдарезультат доступен
failedдапопытки исчерпаны, средства возвращены
  • Сначала проверяйте status. finished — готовое конечное состояние даже при отсутствии files; не продолжайте опрашивать завершённую задачу.
  • После finished проверьте тип результата. Операции с медиа возвращают files, а операции с данными — Suno lyrics, timestamped-lyrics, bpm и Midjourney describe — возвращают output без файлов.
  • Читайте весь массив files. Пакет Seedream или действие Suno music возвращает несколько элементов; взяв только files[0], вы потеряете часть оплаченных результатов.
  • При failed прочитайте и сохраните errorMessage. К этому моменту платформа уже повторила запрос к текущему провайдеру до его лимита, перебрала резервных провайдеров и вернула списанные средства. С этой задачей больше ничего не произойдёт.
  • Предусмотрите обработку зависших задач. Если задача остаётся в processing намного дольше обычного времени модели, сообщите человеку, а не продолжайте бесконечный цикл.

Повторять или не повторять: решайте по коду ошибки

Ошибки всегда имеют одну форму:

json
{
"code": 402,
"error": {
"message": "Insufficient balance. Please top up your account",
"type": "PaymentRequired",
"code": "insufficient_balance"
}
}

Учитывайте HTTP-статус и error.code. Код стабилен, когда присутствует, но ответы о превышении лимита сейчас используют unknown_error, поэтому определяйте их по HTTP 429. error.message — английский текст для логов, его формулировка может меняться.

error.codeHTTPПовторять?
invalid_input400Нет — сначала исправьте тело запроса
api_key_missing401Нет — исправьте клиент
api_key_invalid401Нет — ключ отозван, неактивен или заблокирован
api_key_not_found404Нет
auth_required401Нет
insufficient_balance402Только после пополнения
task_not_found404Нет — неверный ID или аккаунт
generation_not_found404Нет
not_found404Нет — проверьте путь
unknown_error при HTTP 429429Да — после Retry-After
unknown_error500Да, с задержкой и пределом попыток

Из таблицы следуют простые правила:

  • Ошибка 4xx означает ошибку клиента, кроме 429 и 402 insufficient_balance. Повтор неизменённого тела с 400 будет всегда возвращать 400. Ошибка 429 временна: дождитесь Retry-After, добавьте случайный разброс и повторите с ограничением попыток. Повторяйте insufficient_balance только после пополнения.
  • Ошибки 5xx и транспорта стоит повторять с экспоненциальной задержкой и случайным разбросом: три-четыре попытки, затем отложите задачу.
  • Запрос create, завершившийся таймаутом на вашей стороне, мог пройти. Не повторяйте его вслепую. Сверьтесь со своей записью о намерении, прежде чем снова тратить средства.
  • Не повторяйте автоматически задачи в состоянии failed. Платформа уже выполнила повторы. В частности, отклонение политикой контента окончательно и повторится у каждого провайдера.

Полный список — в разделе Ошибки и коды ошибок.


Следите за балансом

Оплата работает по предоплате. Средства списываются при создании задачи, а не при её завершении, и автоматически возвращаются, если задача переходит в failed.

  • Проверяйте баланс до пакетного запуска, а не после ответа 402.

    sh
    curl https://api.apihubs.ru/api/v1/user/me \
    -H "Authorization: Bearer sk-your-key"
    json
    {
    "code": 200,
    "data": {
    "id": "019c1f70-2b44-70d3-9c22-6b7ad0f41e18",
    "balance": 48250,
    "createdAt": "2026-01-14T09:03:11.000Z"
    }
    }

    balance хранится в целых центах: 48250 означает $482,50. Не делите его с помощью float и не сравнивайте такие значения на равенство.

  • Оповещайте о достижении порога, а не нуля. Выберите остаток, покрывающий обычные расходы за день, и пополняйте баланс при его достижении. Ответ 402 в продакшене означает очередь неудачных задач, а не предупреждение.

  • Отслеживайте расходы через GET /api/v1/stats: endpoint агрегирует использование и траты по периоду, модели, типу и дню. Так можно заметить изменившуюся цену модели или цикл повторов, незаметно утроивший счёт.

  • Сверяйте данные со своими записями. Ваша таблица задач и ожидаемые цены должны совпадать со статистикой API. Расхождение обычно указывает на повторную отправку.

Подробнее — в разделе Цены и баланс.


Лимиты запросов

API разрешает 120 запросов за 60 секунд на IP клиента и endpoint. Маршруты создания и проверки статуса используют разные квоты, но все клиенты за одним исходящим IP делят квоту каждого маршрута.

  • Учитывайте опросы, а не только создания. Сто выполняющихся задач при опросе каждые 3 секунды создают 2000 запросов в минуту с одного IP. Это обычная причина достижения лимита.
  • Используйте отдельную квоту для каждого endpoint в своём коде и привязывайте её к исходящему IP, если воркеры используют несколько адресов.
  • С ростом нагрузки переходите на вебхуки. Они полностью устраняют трафик опроса, и лимит перестаёт быть узким местом.
  • Помните, что ограничение действует по IP. Все воркеры за одним NAT-шлюзом делят квоту endpoint; воркеры с разными исходящими IP — нет.

Подробнее — в разделе Лимиты запросов.


Копируйте медиа в своё хранилище

Каждый fileUrl в files указывает на хранилище платформы и действует 24 часа.

  • Скачивайте файл при завершении в том же обработчике, который фиксирует конечное состояние. Не откладывайте это до ночной задачи.
  • Не используйте fileUrl напрямую в продукте. Через сутки пользователи получат 404, а вы узнаете об этом только от них.
  • Храните собственную копию, исходный URL и taskId, чтобы сбой загрузки можно было диагностировать и повторить, пока ссылка жива.
  • Проверяйте загруженный файл. Повреждённое видео, которое никто не проверил, хуже отсутствующего: оно выглядит как успешный результат.
  • URL внутри output не зеркалируются. Например, обложка Suno ведёт на CDN провайдера и имеет собственный срок жизни. Копируйте всё, что хотите сохранить.

Подробнее — в разделе Файлы и хранение.


Логирование и поддержка

  • Всегда записывайте taskId каждой генерации. Это основной идентификатор в любом обращении в поддержку; без него невозможно разобраться с неудачным рендером.
  • Записывайте рядом model и input. Для воспроизведения ошибки нужно точное тело запроса, а структура input различается между моделями.
  • При ошибках сохраняйте error.code и error.message без изменений, а для задач в failed — errorMessage. Английское сообщение стабильно, безопасно для логов и поиска.
  • Не показывайте пользователю сырые ошибки провайдера. errorMessage уже содержит очищенный пользовательский текст; внутренние сообщения провайдера API не раскрывает.
  • Записывайте метрику каждого конечного состояния. Рост доли failed у одной модели сигнализирует об изменении на стороне провайдера.

Проверяйте входные данные до списания

GET /api/v1/catalog возвращает все публичные модели, их отображаемые данные, таблицу цен и схему параметров, полученную из входного DTO модели. Endpoint анонимный и не требует ключа.

sh
curl https://api.apihubs.ru/api/v1/catalog

Одна модель:

sh
curl https://api.apihubs.ru/api/v1/catalog/veo3.1-fast

Используйте каталог, чтобы:

  • Проверять форму на клиенте по тем же enum и диапазонам, которые применяет API, и не отправлять заведомо недопустимый resolution.
  • Находить новые модели без деплоя. Публичный сайт сам строится по этому каталогу; новая модель появляется в нём сразу после добавления.
  • Замечать расхождения. Если жёстко заданный enum клиента перестал совпадать с каталогом, обновите его до того, как проблему найдёт пользователь.

Каталог не заменяет обработку ответов 400. Это удобный источник данных, а контрактом остаётся DTO.


Краткий чек-лист

  • Один ключ на окружение; меняйте с пересечением; не передавайте в браузер.
  • Сначала вебхуки, затем опрос с задержкой; добавьте секрет в URL вебхука.
  • taskId — ключ идемпотентности; сохраните его сразу.
  • Сначала проверяйте status, затем наличие files или output.
  • Повторяйте 5xx, не повторяйте обычные 4xx и задачи в failed.
  • Оповещайте о пороге баланса, не ждите 402.
  • Укладывайтесь в 120 запросов за 60 секунд с учётом опросов.
  • Копируйте медиа в течение 24 часов; храните taskId без ограничения срока.

Следующие шаги

Запустите это со своим ключом

Выберите модель в каталоге API Hubs, проверьте цену и попробуйте её в браузере или через API.

Читать дальше