Асинхронная генерация видео: надёжный polling задач Seedance через API

Асинхронная генерация видео требует иной инженерной дисциплины, чем обычный запрос «получил ответ — показал пользователю». Заявка создаёт задачу, результат появляется позже, а клиенту нужны предсказуемые статусы, понятные ошибки и безопасная обработка URL готового файла. В этой статье разберём, как построить такой контур для Seedance через единый API Ace Data Cloud.

В качестве отправной точки пригодятся русская версия платформы, раздел Applications для работы с ключом приложения и документация API. Идея проста: приложение отправляет задачу на генерацию, сохраняет её идентификатор и затем опрашивает отдельный endpoint до терминального состояния. Это позволяет не держать HTTP-соединение открытым и не путать принятие заявки с готовностью ролика.

Какие данные нужно хранить рядом с задачей

После старта генерации сохраните идентификатор задачи в собственной базе вместе с пользователем, исходными параметрами, временем создания и вашим внутренним идентификатором операции. Не ограничивайтесь только URL: на промежуточных стадиях его может не быть, а повторная отправка того же запроса способна создать лишнюю работу и расход. Полезно также хранить последний наблюдаемый статус, число попыток опроса, trace_id при наличии и время следующей проверки.

  • task_id связывает генерацию с вашим заданием;
  • created_at помогает контролировать устаревшие операции;
  • request нужен для аудита параметров и повторного показа пользователю;
  • response содержит итоговые сведения или описание ошибки;
  • trace_id полезен при диагностике вместе с временем вызова.

Ключ приложения берите из выбранного приложения, а не из служебных данных управления платформой. В запросах он передаётся как Bearer-токен. Не помещайте секрет в репозиторий, логи CI или снимки экрана; передавайте его через переменные окружения и ограничивайте права ключа задачей сервиса.

Запрос состояния одной задачи с curl

Endpoint POST https://api.acedata.cloud/seedance/tasks принимает JSON с действием retrieve и идентификатором. Ниже рабочий минимальный пример. Замените переменные на собственные значения; в production добавьте ограничение времени запроса и обработку ненулевого кода завершения curl.

export ACEDATACLOUD_API_KEY="YOUR_ACEDATACLOUD_API_KEY"
export TASK_ID="your-task-id"

curl --silent --show-error --fail-with-body \
  --request POST "https://api.acedata.cloud/seedance/tasks" \
  --header "Authorization: Bearer ${ACEDATACLOUD_API_KEY}" \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --data "{\"id\":\"${TASK_ID}\",\"action\":\"retrieve\"}"

Успешный HTTP-ответ означает, что сервер обработал запрос состояния, но не обязательно что видео уже готово. Ответ может быть записью задачи, списком либо пустым объектом, если идентификатор не найден. Поэтому код должен проверять структуру JSON, а затем значение статуса внутри результата, а не считать любой код 200 завершением бизнес-процесса.

Опрос без лишних запросов

Практичный вариант — фоновый worker с возрастающей задержкой. После создания задачи сделайте первую проверку через несколько секунд, затем увеличивайте интервал в разумных пределах. Если сервис при создании или в ответе рекомендует интервал, используйте именно его. Не запускайте новый рендер из-за тайм-аута клиентского запроса: сначала продолжите опрос сохранённого task_id.

Ниже пример на Python 3 с библиотекой requests. Он демонстрирует один запрос, различает ошибки транспорта и HTTP, а также не предполагает фиксированную форму поля результата. Для worker-процесса эту функцию удобно вызывать по расписанию и записывать нормализованный результат в базу.

import os
import requests

API_URL = "https://api.acedata.cloud/seedance/tasks"


def get_task(task_id: str) -> dict:
    response = requests.post(
        API_URL,
        headers={
            "Authorization": f"Bearer {os.environ['ACEDATACLOUD_API_KEY']}",
            "Accept": "application/json",
        },
        json={"id": task_id, "action": "retrieve"},
        timeout=(5, 30),
    )
    response.raise_for_status()
    payload = response.json()

    if not payload:
        return {"state": "not_found", "raw": payload}

    result = payload.get("response") or {}
    data = result.get("data") if isinstance(result, dict) else {}
    status = data.get("status") if isinstance(data, dict) else None

    return {
        "state": status or "pending_inspection",
        "task_id": payload.get("id", task_id),
        "trace_id": payload.get("trace_id"),
        "raw": payload,
    }

print(get_task(os.environ["TASK_ID"]))

В примере intentionally сохраняется исходный JSON: конкретный набор полей результата может зависеть от операции и развиваться. В прикладном слое выделите только те поля, которые нужны интерфейсу: статус, ссылку на видео после завершения, длительность, разрешение, соотношение сторон и сообщение об ошибке. Перед публикацией ссылки проверьте, что статус является терминальным успешным и адрес действительно присутствует.

Пакетная проверка и модель состояний

Когда задач много, используйте одну пакетную проверку вместо десятков одиночных запросов. Для этого передайте массив ids и действие retrieve_batch на тот же endpoint. Ограничьте размер пачки на стороне worker и обрабатывайте каждую запись независимо: одна проблемная задача не должна останавливать остальные.

curl --silent --show-error --fail-with-body \
  --request POST "https://api.acedata.cloud/seedance/tasks" \
  --header "Authorization: Bearer ${ACEDATACLOUD_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"ids":["task-a","task-b"],"action":"retrieve_batch"}'

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

Ошибки, лимиты и приёмочные проверки

Ответы 400 обычно указывают на неверные параметры, 401 — на проблему с ключом, 429 — на превышение допустимой интенсивности, а 500 — на внутреннюю ошибку. Для 429 и части 5xx используйте ограниченное число повторов с экспоненциальной задержкой и случайным разбросом. Для 400 не повторяйте запрос автоматически: сначала исправьте входные данные. Для 401 проверьте, что ключ полностью передан в окружение и префикс Bearer не продублирован.

  • Считайте задачу завершённой только после терминального статуса и наличия нужных полей результата.
  • Проверяйте фактические длительность, разрешение и соотношение сторон готового видео, а не только текст задания.
  • Не включайте API-ключи в исключения, структурированные логи и сообщения поддержки.
  • При обращении в поддержку приложите время, код ошибки и trace_id.
  • Сверяйте вызовы и расход в консоли после тестового прогона.

Такой контур отделяет запуск от наблюдения за результатом и делает интеграцию устойчивой к задержкам сети, перезапускам worker-процессов и неоднородным ответам. Когда обработчик хранит task_id, соблюдает интервалы и проверяет финальные поля, API можно безопасно включать в очередь задач, webhook-агрегатор или пользовательский интерфейс без ложных сообщений о готовности.

Comments

Popular posts from this blog

Artistic QR Code API Integration Guidance

How to Configure Claude Code with CC Switch and Ace Data Cloud