> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.beatapi.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.beatapi.io/_mcp/server.

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

> Интеграция BeatAPI в приложение: синхронный текст, асинхронные задачи, проверка статуса, вебхуки, повторные попытки и обработка ошибок.

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

[Быстрый старт](/quick-guide) помогает выполнить один запрос. Здесь описано, что нужно учесть для надёжной работы приложения в рабочей среде.

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

В 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` и поля возврата средств.

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

```python
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`.

> **Note**
>
> У Music Video есть промежуточные состояния, требующие решения, а не дальнейшего опроса: `storyboard_ready` и `requires_action`. Если считать их обычной обработкой, цикл не завершится. Все статусы и необходимые действия описаны в [быстром старте](/quick-guide).

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

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

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

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

## Ошибки

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

```json
{
  "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`. Полный список приведён в разделе [ошибок](/errors).

```python
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))
```

> **Note**
>
> Ответ `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`: эта ошибка останавливает все платные операции аккаунта одновременно.