Перейти к навигации

Руководство разработчика

Руководство разработчика

Быстрый старт помогает выполнить один запрос. Здесь описано, что нужно учесть для надёжной работы приложения в рабочей среде.

Два типа вызовов

В BeatAPI есть два жизненных цикла запросов. Большинство ошибок интеграции связано с попыткой обрабатывать один как другой.

ТекстРабочие процессы, изображения, видео, эффекты
Что возвращаетсяСразу сам ответСразу ID задачи
Время выполненияСекундыОт секунд до минут
Потоковая передачаServer-Sent EventsНе применяется
Как определить завершениеПо HTTP-ответуПо статусу задачи или вебхуку
ОплатаПри получении ответаПри окончательном расчёте, с возвратом при ошибке

Текстовые вызовы синхронны: POST /v1/chat/completions, /v1/responses, /v1/messages или Gemini-совместимый метод возвращают результат. Остальные операции принимают работу и возвращают ID.

Жизненный цикл асинхронной задачи

  1. Отправьте запрос через POST /v1/images/tasks либо videos, effects, video-analysis, music-video, ecommerce-video. Ответ 201 означает принятие, а не завершение.
  2. Сохраните ID: запишите data.id и data.request_id до запуска фоновой обработки. Если приложение упадёт между ответом и записью, останется оплаченная задача, которую вы не сможете найти.
  3. Дождитесь завершения: проверяйте GET /v1/tasks/{task_id} либо зарегистрируйте вебхук.
  4. Прочитайте результат: при succeeded используйте data.output.media; при failed проверьте data.error_code и поля возврата средств.

Проверка статуса

import time
import random
import requests
TERMINAL = {"succeeded", "failed"}
def wait_for_task(api_key, task_id, timeout=900):
url = f"https://api.beatapi.io/v1/tasks/{task_id}"
headers = {"Authorization": f"Bearer {api_key}"}
deadline = time.time() + timeout
delay = 5.0
while time.time() < deadline:
response = requests.get(url, headers=headers, timeout=30)
if response.status_code == 429:
time.sleep(float(response.headers.get("Retry-After", 10)))
continue
response.raise_for_status()
task = response.json()["data"]
if task["status"] in TERMINAL:
return task
# Jitter keeps a fleet of workers from polling in lockstep.
time.sleep(delay + random.uniform(0, delay * 0.3))
delay = min(delay * 1.5, 30.0)
raise TimeoutError(f"task {task_id} did not finish within {timeout}s")

У цикла опроса важны три свойства:

  • Остановка по конечному состоянию, а не по предположению о прошедшем времени. Завершённая задача больше не меняется.
  • Увеличение задержки со случайным разбросом. Соблюдайте poll_after_seconds и распределяйте проверки параллельных задач по времени. Частые проверки не ускоряют выполнение задачи.
  • Обработка 429 как ожидания, а не окончательного сбоя. Соблюдайте Retry-After.

У Music Video есть промежуточные состояния, требующие решения, а не дальнейшего опроса: storyboard_ready и requires_action. Если считать их обычной обработкой, цикл не завершится. Все статусы и необходимые действия описаны в быстром старте.

Вебхуки вместо постоянного опроса

Зарегистрируйте обработчик через POST /v1/webhooks: BeatAPI отправит ему конечное состояние задачи. Это позволяет отказаться от основного цикла опроса, но требует доступного по HTTPS обработчика.

Проверяйте подпись по исходным байтам запроса до разбора JSON. Повторная сериализация меняет тело, и подпись не совпадёт. См. вебхуки.

Даже с вебхуками оставьте редкий контрольный опрос. Доставка может не состояться; если задача находится в processing гораздо дольше обычного для модели, проверьте её напрямую.

Ошибки

Каждая ошибка содержит стабильный error.code, английское message с причиной, retryable и request_id. Записывайте request_id: по нему поддержка найдёт конкретный вызов.

{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many requests. Wait 12 seconds, then retry.",
"retryable": true,
"request_id": "req_abc123",
"retry_after_seconds": 12
}
}
HTTPПовторять?Значение
400НетНеверный запрос. Повтор без изменений не исправит его
401Нетmissing_api_key или invalid_api_key: ключ отсутствует, неверен, просрочен, отключён или его лимит исчерпан
402Нетinsufficient_credits: баланс равен нулю или меньше цены. Пополните его
403Нетforbidden: ключу или аккаунту запрещена операция
404Нетnot_found: неизвестная модель, неверный ID либо ID другого аккаунта
409Нетidempotency_conflict: ключ идемпотентности использован с другим телом запроса
429Да, после Retry-AfterОграничение частоты или параллелизма
500, 503Да, с увеличением задержкиВременный сбой. Проверяйте известную задачу, а не создавайте её повторно

Поле retryable передаёт то же правило для конкретного кода, поэтому можно ориентироваться на него. BeatAPI не возвращает 502 или 504: такие ответы приходят с сетевого уровня, и повторяются как 503. Полный список приведён в разделе ошибок.

import requests
response = requests.post(
"https://api.beatapi.io/v1/images/tasks",
headers={"Authorization": f"Bearer {api_key}"},
json={"model": "nano-banana", "prompt": "a cute panda"},
timeout=30,
)
if not response.ok:
error = response.json().get("error", {})
# request_id is the only thing that identifies this call later.
log.error("beatapi failed", code=error.get("code"), request_id=error.get("request_id"))
if response.status_code == 402:
alert_billing()
elif response.status_code == 429:
schedule_retry(after=error.get("retry_after_seconds", 10))

Ответ 500 при создании неоднозначен: задача могла быть создана, а могла и нет. Повтор вслепую может оплатить одну работу дважды. Передавайте Idempotency-Key при создании и используйте тот же ключ при повторе: BeatAPI вернёт исходную задачу вместо создания второй.

Лимиты

Действуют два отдельных ограничения с разными последствиями.

  • Частота запросов: запросы в минуту на ключ. Превышение даёт 429 с Retry-After. Лимит растёт с общей суммой пополнений; текущий и следующий уровни видны в панели управления и в GET /api/user/self.

GET /v1/usage возвращает оба показателя: concurrency.limit и concurrency.active.

Повторные попытки без лишних списаний

  • Повторяйте 429 и 5xx с увеличением задержки. Любой 5xx от BeatAPI — это 500 или 503. Не повторяйте 400, 401, 402, 403, 404 и 409: у них retryable: false.
  • Используйте экспоненциальное увеличение задержки со случайным разбросом и верхним пределом.
  • Передавайте Idempotency-Key при каждом создании задачи, чтобы повтор запроса не приводил к двойной оплате.
  • Если после ошибки создания у вас уже есть ID, сначала проверьте статус, а не отправляйте задачу заново.
  • Ограничьте общее число попыток. Бесконечные повторы постоянно неудачного запроса превращают сбой в источник расходов.

Контроль расходов

  • Ограничивайте выход текстовых вызовов. Выходные токены дороже; max_tokens — простой способ сократить лишний ответ.
  • Подбирайте модель под шаг. Для классификации и маршрутизации не нужна самая сильная модель.
  • Учитывайте переменные тарифы. У GPT-5.6 и Grok тариф повышается после порога контекста. У двух моделей DeepSeek HC (high-cache) по будням с 09:00 до 12:00 и с 14:00 до 18:00 по Пекину и у Hunyuan hy3 действует повышенная цена в опубликованные пиковые часы. Обычные платные модели DeepSeek тарифицируются одинаково круглосуточно. Долгая сессия агента или обработка по расписанию может пересечь эти границы незаметно. Применённый тариф указан в данных об использовании для каждого ответа.
  • Отдельный ключ для каждого компонента. Поле by_api_key в GET /v1/usage разделит расходы без дополнительного учёта.
  • Учитывайте возвраты. Средства за неудачную задачу возвращаются. Если записывать только резервирование, ваши расходы не совпадут с фактическими.

Перед запуском в рабочей среде

  • Используйте точный адрес https://api.beatapi.io.
  • Храните постоянные ключи на сервере. Браузер получает краткоживущий Realtime client_secret, а не API-ключ.
  • Сохраняйте data.id и request_id до фоновой обработки.
  • Используйте случайный разброс интервалов опроса; прекращайте опрос при завершении или ожидании действия.
  • Проверяйте подписи вебхуков по исходным байтам.
  • Записывайте резервы, итоговые списания, возвраты, ошибки и конечные URL результатов.
  • Настройте уведомление о 402: эта ошибка останавливает все платные операции аккаунта одновременно.