Эксплуатация
Выход в продакшен
Работа с ключами, вебхуки вместо опроса, идемпотентность, конечные состояния, правила повторов по коду ошибки, контроль баланса, лимиты запросов и хранение медиа.
Прототип, который отправляет задачу и опрашивает её в цикле while, работает.
В продакшене ломается всё вокруг него: отозванный ключ, дважды пришедший
вебхук, закончившийся в три часа ночи баланс или ссылка на медиа, которая
истекла до загрузки файла вашим CDN. На этой странице собраны решения, которые
стоит принять до запуска.
В первый раз прочитайте статью по порядку, а затем используйте как чек-лист. Здесь нет ничего экзотического. На практике команды, работающие с API генерации, обычно проходят один и тот же путь: ключи утекают, вебхуки дублируются, повторный запрос приводит к двойному списанию, баланс заканчивается в пятницу, а ссылка на файл истекает за те двенадцать часов, которые проходят между генерацией и первым просмотром результата.
Творческой части посвящены другие руководства: генерация видео из текста, генерация изображений, редактирование изображений и генерация музыки. Здесь речь пойдёт о стабильной работе созданного вами продукта.
Ключи API
- Передавайте ключ в заголовке, а не в строке запроса. Поддерживаются
Authorization: Bearer sk-…иx-api-key: sk-…. Строки запроса попадают в логи прокси и историю браузера. - Не отправляйте ключ в браузер или мобильное приложение. У публичного API нет ключей с ограниченными правами, коротким сроком действия или безопасной для CORS моделью. Ключ даёт полный доступ к аккаунту и балансу. Проксируйте запросы генерации через свой бэкенд.
- Создавайте отдельный ключ для каждого окружения и сервиса. Staging, production и фоновые воркеры должны отключаться независимо, потому что ключи отзываются по одному. Разделение ключей также позволяет понять, какой сервис выполнял конкретные запросы.
- Меняйте ключи с периодом пересечения. Создайте новый ключ, разверните его, убедитесь, что трафик переключился, и только потом отключайте старый. Отключение действует со следующего запроса: успешные проверки ключа кэшируются на час, но при отзыве ключа эта запись удаляется.
- Храните ключи в менеджере секретов, а не в
.env, добавленном в репозиторий. При утечке действуйте как при компрометации банковской карты: сначала отключите ключ, затем разбирайтесь в причинах.
Предпочитайте вебхуки опросу
Передайте URL в поле webhook при создании задачи, и готовый результат будет
отправлен вам методом POST. Тело запроса побайтово совпадает с ответом метода
проверки статуса: тот же объект
{ code, data: { taskId, status, files, output, createdTime, errorMessage } }.
Оба способа можно обрабатывать одним кодом.
{"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, а операции с данными — Sunolyrics,timestamped-lyrics,bpmи Midjourneydescribe— возвращаютoutputбез файлов. - Читайте весь массив
files. Пакет Seedream или действие Sunomusicвозвращает несколько элементов; взяв толькоfiles[0], вы потеряете часть оплаченных результатов. - При
failedпрочитайте и сохранитеerrorMessage. К этому моменту платформа уже повторила запрос к текущему провайдеру до его лимита, перебрала резервных провайдеров и вернула списанные средства. С этой задачей больше ничего не произойдёт. - Предусмотрите обработку зависших задач. Если задача остаётся в
processingнамного дольше обычного времени модели, сообщите человеку, а не продолжайте бесконечный цикл.
Повторять или не повторять: решайте по коду ошибки
Ошибки всегда имеют одну форму:
{"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.code | HTTP | Повторять? |
|---|---|---|
invalid_input | 400 | Нет — сначала исправьте тело запроса |
api_key_missing | 401 | Нет — исправьте клиент |
api_key_invalid | 401 | Нет — ключ отозван, неактивен или заблокирован |
api_key_not_found | 404 | Нет |
auth_required | 401 | Нет |
insufficient_balance | 402 | Только после пополнения |
task_not_found | 404 | Нет — неверный ID или аккаунт |
generation_not_found | 404 | Нет |
not_found | 404 | Нет — проверьте путь |
unknown_error при HTTP 429 | 429 | Да — после Retry-After |
unknown_error | 500 | Да, с задержкой и пределом попыток |
Из таблицы следуют простые правила:
- Ошибка 4xx означает ошибку клиента, кроме
429и402 insufficient_balance. Повтор неизменённого тела с 400 будет всегда возвращать 400. Ошибка429временна: дождитесьRetry-After, добавьте случайный разброс и повторите с ограничением попыток. Повторяйтеinsufficient_balanceтолько после пополнения. - Ошибки 5xx и транспорта стоит повторять с экспоненциальной задержкой и случайным разбросом: три-четыре попытки, затем отложите задачу.
- Запрос create, завершившийся таймаутом на вашей стороне, мог пройти. Не повторяйте его вслепую. Сверьтесь со своей записью о намерении, прежде чем снова тратить средства.
- Не повторяйте автоматически задачи в состоянии
failed. Платформа уже выполнила повторы. В частности, отклонение политикой контента окончательно и повторится у каждого провайдера.
Полный список — в разделе Ошибки и коды ошибок.
Следите за балансом
Оплата работает по предоплате. Средства списываются при создании задачи, а
не при её завершении, и автоматически возвращаются, если задача переходит в
failed.
-
Проверяйте баланс до пакетного запуска, а не после ответа 402.
shcurl 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
анонимный и не требует ключа.
curl https://api.apihubs.ru/api/v1/catalog
Одна модель:
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.
- Жизненный цикл генерации — что происходит между
create и
finished. - Вебхуки, опрос статуса задачи.
- Ошибки и коды ошибок, лимиты запросов, цены и баланс.
- Обзор моделей.
Запустите это со своим ключом
Все модели из этого руководства доступны в каталоге API Hubs — один API-ключ, один предоплаченный баланс, без отдельной регистрации у каждого провайдера.