Статусы и результаты
Получите статус задачи через polling или callback и сохраните видео.
Получите статус задачи через polling или callback и сохраните видео.
Читайте сохранённый ID через GET /v1/videos/{id} с тем же API-ключом:
umask 077
touch result.json && chmod 600 result.json || exit 1
curl --fail --silent --show-error --max-time 60 "$BASE_URL/v1/videos/$TASK_ID" -H "Authorization: Bearer $API_KEY" -o result.json
jq '{id, model, status, error}' result.json
| Статус задачи | Действие |
|---|---|
queued | Задача принята; через 10–15 секунд снова прочитайте тот же ID |
in_progress | Генерация выполняется; продолжайте GET по тому же ID |
completed | Скачайте результат; content.video_url необязателен |
failed | Остановитесь и сохраните ID; поле error может отсутствовать |
unknown или другое значение | Остановите автоматическую обработку, сохраните ID и обратитесь в поддержку |
HTTP 200 означает успешное чтение, а не готовое видео. Локальный предел ожидания 45 минут не отменяет задачу; продолжайте GET по тому же ID. Поля object: "video", model, status, progress и created_at относятся к публичной задаче. completed_at появляется после успеха. Отсутствие error при failed не означает успех.
Ответ создания содержит ID и начальный статус. Пример:
{
"id": "task_example",
"object": "video",
"model": "seedance-2.5",
"status": "queued",
"progress": 0,
"created_at": 1790000000
}
Пример задачи в работе (progress иллюстративный, не прогноз времени):
{
"id": "task_example",
"object": "video",
"model": "seedance-2.5",
"status": "in_progress",
"progress": 50,
"created_at": 1790000000
}
Пример завершения с ошибкой, без обязательного error:
{
"id": "task_example",
"object": "video",
"model": "seedance-2.5",
"status": "failed",
"progress": 100,
"created_at": 1790000000
}
Пример успешной задачи без подписанных ссылок:
{
"id": "task_example",
"object": "video",
"model": "seedance-2.5",
"status": "completed",
"progress": 100,
"created_at": 1790000000,
"completed_at": 1790000120
}
Пример успешной задачи с дополнительными ссылками:
{
"id": "task_example",
"object": "video",
"model": "seedance-2.5",
"status": "completed",
"progress": 100,
"created_at": 1790000000,
"completed_at": 1790000120,
"content": {
"video_url": "RESULT_URL",
"last_frame_url": "RESULT_URL"
}
}
Пример неизвестного статуса:
{
"id": "task_example",
"object": "video",
"model": "seedance-2.5",
"status": "unknown",
"progress": 0,
"created_at": 1790000000
}
Видео доступно для скачивания в течение 24 часов после подготовки файла. Повторное чтение задачи или доставка callback не продлевают этот срок. Сохраните видео в своём хранилище до его истечения. Подписанные media URLs дают временный доступ к файлу: не записывайте ссылки в журнал и не публикуйте их.
Укажите публичный HTTPS callback_url в теле создания, чтобы запросить итоговое уведомление. Доставка зависит от подключения; чтение задачи остаётся способом восстановления. Тело POST callback — прямой JSON с id, model, status (succeeded или failed), временем, параметрами и подписанным content при успехе либо нейтральной error при ошибке. Callback succeeded соответствует GET completed; это разные формы ответа. Нет обёртки event/data и HMAC-подписи. Заголовок X-UnlimGPT-Event-ID помогает распознавать дубликаты, но не удостоверяет отправителя. Не ожидайте уведомления для каждого промежуточного статуса или фиксированного числа попыток доставки.
Сохраните уведомление и быстро ответьте HTTP 200/204. Сопоставляйте сохранённый ID, обрабатывайте дубликаты идемпотентно и проверяйте итоговый статус авторизованным GET до действий. При пропущенном callback периодически читайте задачу. Входящий POST без аутентификации сам по себе не доказывает успешность заказа.
Пример POST-уведомления об успехе:
{
"id": "task_example",
"model": "seedance-2.5",
"status": "succeeded",
"created_at": 1790000000,
"updated_at": 1790000120,
"resolution": "720p",
"generate_audio": false,
"duration": 5,
"content": {
"video_url": "RESULT_URL",
"last_frame_url": "RESULT_URL"
}
}
Пример уведомления об ошибке:
{
"id": "task_example",
"model": "seedance-2.5",
"status": "failed",
"created_at": 1790000000,
"updated_at": 1790000120,
"resolution": "720p",
"generate_audio": false,
"error": {
"code": "model_unavailable",
"message": "model unavailable"
}
}
Ответ обработчика после сохранения уведомления:
HTTP/1.1 204 No Content
Если обработчик недоступен или не подтвердил доставку, уведомление может прийти повторно; количество попыток не используйте в логике приложения. Возвращайте успех после надёжного сохранения события, а проверку GET и скачивание выполняйте отдельно. Не используйте ссылки из непроверенного callback.
В этом руководстве нет поддерживаемых операций отмены или удаления задачи. Сохраните ID при создании; прекращение polling не отменяет заказ.
После GET completed используйте GET /v1/videos/{id}/content с API Bearer. Ответ может содержать файл или HTTP 307 с подписанной ссылкой в Location. Следующий GET по этой ссылке выполняйте без API Bearer. Если GET задачи уже вернул content.video_url, можно сразу скачать точный HTTPS URL без API-ключа. Не переходите автоматически по redirect; отклоняйте не-HTTPS URLs и встроенные credentials.
Пример получения redirect (без -L):
umask 077
touch download-headers.txt && chmod 600 download-headers.txt || exit 1
curl --silent --show-error --max-time 60 \
"$BASE_URL/v1/videos/$TASK_ID/content" \
-H "Authorization: Bearer $API_KEY" \
-D download-headers.txt -o seedance.download
Пример ответа:
HTTP/1.1 307 Temporary Redirect
Location: RESULT_URL
Видео по умолчанию MP4; для output_format: "mov" сохраняйте .mov.
Примеры прямого скачивания подписанного URL:
VIDEO_URL=$(jq -er 'select(.status=="completed") | .content.video_url | select(type=="string" and startswith("https://"))' result.json)
case "$VIDEO_URL" in https://*@*)
echo 'Embedded credentials rejected' >&2
exit 1
;;
esac
HTTP_STATUS=$(curl --fail --silent --show-error --max-time 300 \
--write-out '%{http_code}' "$VIDEO_URL" -o seedance.download)
case "$HTTP_STATUS" in
2??) mv seedance.download seedance.mp4 ;;
*)
rm -f seedance.download
exit 1
;;
esac
Некоторые авторизованные API-маршруты /content могут вернуть redirect в хранилище. При их использовании не пересылайте API-ключ на перенаправленный адрес; пример выше обращается напрямую к возвращённой подписанной ссылке. Если подписанная ссылка вернула 403, прочитайте существующую задачу для получения текущей ссылки в пределах исходного срока хранения медиа. Это может обновить подпись, но не продлевает срок жизни файла. Если исходный срок истёк или ссылка всё ещё недоступна, используйте сохранённую копию или обратитесь в поддержку с ID. Не создавайте новую генерацию.
При создании укажите return_last_frame: true. После успеха необязательное поле content.last_frame_url в GET и итоговом callback содержит подписанную ссылку на JPEG. Кадр имеет те же размеры, что и видео, и не содержит видимой метки watermark. Скачивайте его отдельно по точному HTTPS URL без API Bearer. Это выходное изображение, а не входная роль last_frame.
umask 077
FRAME_URL=$(jq -er 'select(.status=="completed") | .content.last_frame_url | select(type=="string" and startswith("https://"))' result.json) || exit 1
case "$FRAME_URL" in https://*@*)
echo 'Embedded credentials rejected' >&2
exit 1
;;
esac
DOWNLOAD_RESULT=$(curl -q --fail --silent --show-error --max-time 300 \
--write-out '%{http_code} %{content_type}' "$FRAME_URL" -o seedance-last-frame.download) || exit 1
case "$DOWNLOAD_RESULT" in
'200 image/jpeg' | '200 image/jpeg;'*) mv seedance-last-frame.download seedance-last-frame.jpg ;;
*)
rm -f seedance-last-frame.download
exit 1
;;
esac
Скрипт использует сохранённый ответ GET completed и не следует redirect. Если ссылки нет или она истекла, снова прочитайте ту же задачу; новый POST для скачивания кадра не нужен. Обновление подписанной ссылки не продлевает срок хранения файла.
Ошибка HTTP отличается от status: "failed" в успешно прочитанной задаче. Ошибки создания используют OpenAI-совместимый объект error. Чтение задачи и скачивание также могут вернуть плоский ответ code/message/data. Не привязывайте обработку к точному тексту и не требуйте type и code внутри error; сохраняйте HTTP-статус и request ID.
| Шаг / HTTP | Что делать |
|---|---|
POST 400 | Исправьте JSON, поля, диапазоны и роли медиа перед новым заказом |
POST/GET 401 или 403 | Проверьте ключ, доступ к модели, баланс и лимит ключа |
GET задачи 400 или 404 | Проверьте сохранённый ID, origin и аккаунт; не создавайте видео повторно |
GET 429 или 5xx | Подождите, учтите Retry-After, если он есть, и повторите тот же GET |
POST timeout, 5xx или ответ без ID | Исход неизвестен: проверьте кабинет или поддержку до другого POST |
/content 400 | Результат ещё не готов: снова прочитайте статус задачи |
/content 404 | Проверьте ID и доступность запрошенного файла; сохраните ID |
/content 410 | Файл больше недоступен; используйте сохранённую копию или поддержку |
/content 416 | Диапазон за пределами файла; проверьте размер и возобновляйте корректным Range |
/content 502, 503 или 504 | Повторите скачивание того же результата позже; новый POST не нужен |
Подписанный URL 403 | Обновите ссылку через GET исходной задачи; срок хранения файла не продлевается |
Пример формы ошибки запроса POST 400 (текст иллюстративный и может содержать ID запроса):
{
"error": {
"message": "Invalid request field",
"type": "new_api_error",
"code": ""
}
}
Пример /content 404:
{
"error": {
"message": "Task not found",
"type": "invalid_request_error"
}
}
Пример формы отказа авторизации, HTTP 401 (текст зависит от языка ответа):
{
"error": {
"message": "Invalid token",
"type": "new_api_error",
"code": ""
}
}
Пример ограничения частоты с необязательным временем ожидания:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Сохраните ID и подождите 30 секунд перед следующим GET. Если заголовка нет, используйте задержку с постепенным увеличением и случайным разбросом. Примеры интеграции останавливаются при окончательной ошибке и сохраняют ID.
HEAD /v1/videos/{id}/content позволяет запросить заголовки без тела. Для частичного скачивания используйте GET с Range; при прямой выдаче диапазона ответ — 206 Partial Content и Content-Range. При 307 отправьте тот же Range отдельным запросом на подписанный URL без API Bearer. Если сервер ответил 200, это полный файл: не дописывайте его к уже сохранённой части.
umask 077
touch range-headers.txt && chmod 600 range-headers.txt || exit 1
curl --silent --show-error --max-time 60 --head \
"$BASE_URL/v1/videos/$TASK_ID/content" \
-H "Authorization: Bearer $API_KEY"
curl --silent --show-error --max-time 300 \
"$BASE_URL/v1/videos/$TASK_ID/content" \
-H "Authorization: Bearer $API_KEY" -H 'Range: bytes=0-1023' \
-D range-headers.txt -o seedance.part