Документация

Seedance API

В каталоге 4 записи семейства Seedance: версии и режимы. Имя модели ставится в поле model запроса createTask, а всё остальное уходит вложенным объектом input. У каждой модели ниже её поля, готовый вызов и ответ; общее для всех видео — на странице Видео, а сам порядок вызова разобран в разделе Как устроена задача.

Модели семейства

Идентификатор Модель Задача Страница на сайте
seedance/2-5 Seedance 2.5 text-to-video / image-to-video / reference-to-video Открыть
seedance/2 Seedance 2.0 text-to-video / image-to-video / reference-to-video Открыть
seedance/2-fast Seedance 2.0 Fast text-to-video / image-to-video / reference-to-video Открыть
seedance/2-mini Seedance 2.0 Mini text-to-video / image-to-video / reference-to-video Открыть

seedance/2-5

Поля запроса

ПолеТипОбяз.Допустимые значения
model текст обязательное seedance/2-5
input объект обязательное Параметры генерации — поля из таблицы ниже

Поля input

ПолеТипОбяз.Допустимые значения
prompt текст опц. от 3 до 30 000 символов
first_frame_url ссылка опц. одна ссылка на изображение
last_frame_url ссылка опц. одна ссылка на изображение
reference_image_urls список ссылок опц. до 30 ссылок на изображение
reference_video_urls список ссылок опц. до 10 ссылок на видео
reference_audio_urls список ссылок опц. до 10 ссылок на аудио
return_last_frame да или нет опц. true или false
generate_audio да или нет опц. true или false
resolution значение из списка опц. 480p · 720p · 1080p
aspect_ratio значение из списка опц. 1:1 · 4:3 · 3:4 · 16:9 · 9:16 · 21:9 · adaptive
duration целое число опц. целое от 4 до 30
output_format значение из списка опц. mp4 · mov
web_search да или нет опц. true или false
draft да или нет опц. true или false
draft_task_id текст опц. от 1 до 64 символов

Поле draft. Черновик. Видео выходит в 480p и стоит как обычное в 480p; из готового черновика то же видео получают в 1080p — как это устроено.

Поле draft_task_id. Номер задачи черновика. Модель делает то же видео в 1080p, а остальное берёт из черновика — порядок двух запросов.

Черновик и тот же ролик в 1080p

Сначала делают черновик в 480p и смотрят, что получилось, затем по номеру его задачи получают тот же ролик в 1080p. Промпт, файлы, длительность, пропорции и звук второй запрос берёт из черновика.

Шаг 1. Черновик в 480p

К обычному запросу добавляют draft: true. Остальные поля и их правила те же. Поле resolution можно не присылать — шлюз поставит 480p; с другим значением запрос отклоняется.

draft.sh
curl -X POST "https://api.gen202.com/api/v1/jobs/createTask" \
  -H "Authorization: Bearer sk-ваш-ключ" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance/2-5",
    "input": {
      "prompt": "Ginger cat in a white spacesuit floats in zero gravity above Earth. It slowly turns its head toward the camera, front paws drifting; sunlight glints across the helmet visor, the blue planet reflected in the glass. The camera eases backwards, revealing a solar panel at the edge of frame. Cinematic, soft rim light, shallow depth of field.",
      "aspect_ratio": "1:1",
      "duration": 4,
      "reference_image_urls": [
        "https://gen202.com/assets/sample-nano-banana-pro.jpg"
      ],
      "draft": true
    }
  }'

Номер задачи черновика приходит в ответе полем data.taskId. Второй запрос отправляют, когда задача пришла в состояние success:

state.sh
curl "https://api.gen202.com/api/v1/jobs/recordInfo?taskId=cm8r4t0vk0001s60p2xq7f9ab" \
  -H "Authorization: Bearer sk-ваш-ключ"

Шаг 2. Тот же ролик в 1080p

В поле draft_task_id передают номер задачи черновика. Поле resolution можно не присылать — шлюз поставит 1080p; с другим значением запрос отклоняется.

final.sh
curl -X POST "https://api.gen202.com/api/v1/jobs/createTask" \
  -H "Authorization: Bearer sk-ваш-ключ" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance/2-5",
    "input": {
      "draft_task_id": "cm8r4t0vk0001s60p2xq7f9ab",
      "output_format": "mov"
    }
  }'

В ответе приходит номер новой задачи. Её состояние и ссылку на файл получают так же, как у любой задачи.

Эти поля второй запрос берёт из черновика. Присылать их вместе с draft_task_id нельзя — запрос отклоняется, даже если значения те же: prompt, first_frame_url, last_frame_url, reference_image_urls, reference_video_urls, reference_audio_urls, generate_audio, aspect_ratio, duration, web_search.

Во втором запросе можно задать:

ПолеТипДопустимые значения
resolution значение из списка только 1080p
output_format значение из списка mp4 · mov
return_last_frame да или нет true или false

Поля draft: true и draft_task_id в одном запросе не принимаются.

Черновик годится 7 дней со дня создания.

Второй запрос отклоняется сразу, без удержания кредитов, если задачи с таким номером на аккаунте нет, она создана без draft: true, ещё не закончилась, закончилась отказом или черновику больше 7 дней.

Оплата черновика и второго запроса

Черновик считается как обычный ролик в 480p: ставки те же, ролик на входе учитывается как обычно.

Второй запрос считается по ставке 1080p без ролика на входе, на длительность черновика — даже если в черновике на входе был ролик.

Длительность в запросе черновикаЧерновикВторой запрос
Число от 4 до 30480p на эту длительность1080p на эту же длительность
Не заданаУдерживается сумма за 30 секунд в 480p, списывается по длине готового ролика1080p на длину готового черновика

Пример вызова

task.sh
# 1. Поставить задачу — в ответе придёт её номер
TASK=$(curl -s https://api.gen202.com/api/v1/jobs/createTask \
  -H "Authorization: Bearer sk-ваш-ключ" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance/2-5",
    "input": {
      "prompt": "Ginger cat in a white spacesuit floats in zero gravity above Earth. It slowly turns its head toward the camera, front paws drifting; sunlight glints across the helmet visor, the blue planet reflected in the glass. The camera eases backwards, revealing a solar panel at the edge of frame. Cinematic, soft rim light, shallow depth of field.",
      "aspect_ratio": "1:1",
      "resolution": "720p",
      "duration": 4,
      "reference_image_urls": [
        "https://gen202.com/assets/sample-nano-banana-pro.jpg"
      ]
    }
  }' | jq -r '.data.taskId')

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while true; do
  RECORD=$(curl -s "https://api.gen202.com/api/v1/jobs/recordInfo?taskId=$TASK" \
    -H "Authorization: Bearer sk-ваш-ключ")
  STATE=$(echo "$RECORD" | jq -r '.data.state')
  case "$STATE" in success|fail) break;; esac
  sleep 3
done

# 3. Забрать результат. Ссылка на файл лежит в resultJson — это строка JSON
#    внутри JSON, поэтому её разбирают вторым разбором (fromjson)
echo "$RECORD" | jq -r '.data |
  if .state == "fail"
  then "отказ \(.failCode): \(.failMsg)"
  else .resultJson | fromjson | .resultUrls[0]
  end'
task.py
import json
import time

import requests

headers = {"Authorization": "Bearer sk-ваш-ключ"}

# 1. Поставить задачу — в ответе придёт её номер
created = requests.post(
    "https://api.gen202.com/api/v1/jobs/createTask",
    headers=headers,
    json={
      "model": "seedance/2-5",
      "input": {
        "prompt": "Ginger cat in a white spacesuit floats in zero gravity above Earth. It slowly turns its head toward the camera, front paws drifting; sunlight glints across the helmet visor, the blue planet reflected in the glass. The camera eases backwards, revealing a solar panel at the edge of frame. Cinematic, soft rim light, shallow depth of field.",
        "aspect_ratio": "1:1",
        "resolution": "720p",
        "duration": 4,
        "reference_image_urls": [
          "https://gen202.com/assets/sample-nano-banana-pro.jpg"
        ]
      }
    },
).json()
task_id = created["data"]["taskId"]

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while True:
    task = requests.get(
        "https://api.gen202.com/api/v1/jobs/recordInfo",
        headers=headers,
        params={"taskId": task_id},
    ).json()["data"]
    if task["state"] in ("success", "fail"):
        break
    time.sleep(3)

# 3. Разобрать итог. У неудачи причина в failCode и failMsg
if task["state"] == "fail":
    raise SystemExit("отказ " + task["failCode"] + ": " + task["failMsg"])

# resultJson — строка JSON внутри JSON, поэтому разбор второй
result = json.loads(task["resultJson"])
print(result["resultUrls"][0])
print("списано кредитов:", task["creditsConsumed"])

Пример проходит весь путь: ставит задачу, повторяет запрос состояния раз в 3 секунды, пока задача не закончится, и разбирает ответ до ссылки на файл. Разборов два, потому что поле resultJson — строка JSON внутри JSON. В теле первого запроса стоят обязательные поля модели и значения из её перечислений, поэтому он проходит проверку шлюза как есть. Подставить остаётся только свой ключ. Пример на оболочке разбирает ответ через jq; на Python своего ничего не нужно, кроме requests.

Ответ, когда задача готова

recordInfo.json
{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "cm8r4t0vk0001s60p2xq7f9ab",
    "model": "seedance/2-5",
    "state": "success",
    "resultJson": "{\"resultUrls\":[\"https://file.example/result.mp4\"]}",
    "failCode": "",
    "failMsg": ""
  }
}

Ссылка на файл лежит в resultJson — это строка, внутри которой ещё один JSON, поэтому разбирать её нужно вторым разбором. Пока задача не завершилась, там пустая строка, а в state — waiting, queuing или generating. В настоящем ответе есть ещё поле creditsConsumed со списанием; здесь его нет намеренно — сумма зависит от запрошенного варианта, а цены живут в каталоге. Все поля записи перечислены в разделе Формат ответа.

Общее для всех моделей: Как устроена задача  — чем отправить и как дождаться · Формат ответа  — где в ответе ссылка на файл · Загрузка своих файлов  — как получить ссылку на свою картинку · Отказы  — что означает отказ

seedance/2

Поля запроса

ПолеТипОбяз.Допустимые значения
model текст обязательное seedance/2
input объект обязательное Параметры генерации — поля из таблицы ниже

Поля input

ПолеТипОбяз.Допустимые значения
prompt текст обязательное от 3 до 20 000 символов
first_frame_url ссылка опц. одна ссылка на изображение
last_frame_url ссылка опц. одна ссылка на изображение
reference_image_urls список ссылок опц. до 9 ссылок на изображение
reference_video_urls список ссылок опц. до 3 ссылок на видео
reference_audio_urls список ссылок опц. до 3 ссылок на аудио
return_last_frame да или нет опц. true или false
generate_audio да или нет опц. true или false
resolution значение из списка опц. 480p · 720p · 1080p · 4k
aspect_ratio значение из списка опц. 1:1 · 4:3 · 3:4 · 16:9 · 9:16 · 21:9 · adaptive
duration целое число опц. целое от 4 до 15
web_search да или нет опц. true или false

Пример вызова

task.sh
# 1. Поставить задачу — в ответе придёт её номер
TASK=$(curl -s https://api.gen202.com/api/v1/jobs/createTask \
  -H "Authorization: Bearer sk-ваш-ключ" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance/2",
    "input": {
      "prompt": "Ginger cat in a white spacesuit floats in zero gravity above Earth. It slowly turns its head toward the camera, front paws drifting; sunlight glints across the helmet visor, the blue planet reflected in the glass. The camera eases backwards, revealing a solar panel at the edge of frame. Cinematic, soft rim light, shallow depth of field.",
      "aspect_ratio": "1:1",
      "resolution": "720p",
      "duration": 4,
      "reference_image_urls": [
        "https://gen202.com/assets/sample-nano-banana-pro.jpg"
      ]
    }
  }' | jq -r '.data.taskId')

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while true; do
  RECORD=$(curl -s "https://api.gen202.com/api/v1/jobs/recordInfo?taskId=$TASK" \
    -H "Authorization: Bearer sk-ваш-ключ")
  STATE=$(echo "$RECORD" | jq -r '.data.state')
  case "$STATE" in success|fail) break;; esac
  sleep 3
done

# 3. Забрать результат. Ссылка на файл лежит в resultJson — это строка JSON
#    внутри JSON, поэтому её разбирают вторым разбором (fromjson)
echo "$RECORD" | jq -r '.data |
  if .state == "fail"
  then "отказ \(.failCode): \(.failMsg)"
  else .resultJson | fromjson | .resultUrls[0]
  end'
task.py
import json
import time

import requests

headers = {"Authorization": "Bearer sk-ваш-ключ"}

# 1. Поставить задачу — в ответе придёт её номер
created = requests.post(
    "https://api.gen202.com/api/v1/jobs/createTask",
    headers=headers,
    json={
      "model": "seedance/2",
      "input": {
        "prompt": "Ginger cat in a white spacesuit floats in zero gravity above Earth. It slowly turns its head toward the camera, front paws drifting; sunlight glints across the helmet visor, the blue planet reflected in the glass. The camera eases backwards, revealing a solar panel at the edge of frame. Cinematic, soft rim light, shallow depth of field.",
        "aspect_ratio": "1:1",
        "resolution": "720p",
        "duration": 4,
        "reference_image_urls": [
          "https://gen202.com/assets/sample-nano-banana-pro.jpg"
        ]
      }
    },
).json()
task_id = created["data"]["taskId"]

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while True:
    task = requests.get(
        "https://api.gen202.com/api/v1/jobs/recordInfo",
        headers=headers,
        params={"taskId": task_id},
    ).json()["data"]
    if task["state"] in ("success", "fail"):
        break
    time.sleep(3)

# 3. Разобрать итог. У неудачи причина в failCode и failMsg
if task["state"] == "fail":
    raise SystemExit("отказ " + task["failCode"] + ": " + task["failMsg"])

# resultJson — строка JSON внутри JSON, поэтому разбор второй
result = json.loads(task["resultJson"])
print(result["resultUrls"][0])
print("списано кредитов:", task["creditsConsumed"])

Пример проходит весь путь: ставит задачу, повторяет запрос состояния раз в 3 секунды, пока задача не закончится, и разбирает ответ до ссылки на файл. Разборов два, потому что поле resultJson — строка JSON внутри JSON. В теле первого запроса стоят обязательные поля модели и значения из её перечислений, поэтому он проходит проверку шлюза как есть. Подставить остаётся только свой ключ. Пример на оболочке разбирает ответ через jq; на Python своего ничего не нужно, кроме requests.

Ответ, когда задача готова

recordInfo.json
{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "cm8r4t0vk0001s60p2xq7f9ab",
    "model": "seedance/2",
    "state": "success",
    "resultJson": "{\"resultUrls\":[\"https://file.example/result.mp4\"]}",
    "failCode": "",
    "failMsg": ""
  }
}

Ссылка на файл лежит в resultJson — это строка, внутри которой ещё один JSON, поэтому разбирать её нужно вторым разбором. Пока задача не завершилась, там пустая строка, а в state — waiting, queuing или generating. В настоящем ответе есть ещё поле creditsConsumed со списанием; здесь его нет намеренно — сумма зависит от запрошенного варианта, а цены живут в каталоге. Все поля записи перечислены в разделе Формат ответа.

Общее для всех моделей: Как устроена задача  — чем отправить и как дождаться · Формат ответа  — где в ответе ссылка на файл · Загрузка своих файлов  — как получить ссылку на свою картинку · Отказы  — что означает отказ

seedance/2-fast

Поля запроса

ПолеТипОбяз.Допустимые значения
model текст обязательное seedance/2-fast
input объект обязательное Параметры генерации — поля из таблицы ниже

Поля input

ПолеТипОбяз.Допустимые значения
prompt текст обязательное от 3 до 20 000 символов
first_frame_url ссылка опц. одна ссылка на изображение
last_frame_url ссылка опц. одна ссылка на изображение
reference_image_urls список ссылок опц. до 9 ссылок на изображение
reference_video_urls список ссылок опц. до 3 ссылок на видео
reference_audio_urls список ссылок опц. до 3 ссылок на аудио
return_last_frame да или нет опц. true или false
generate_audio да или нет опц. true или false
resolution значение из списка опц. 480p · 720p
aspect_ratio значение из списка опц. 1:1 · 4:3 · 3:4 · 16:9 · 9:16 · 21:9 · adaptive
duration целое число опц. целое от 4 до 15
web_search да или нет опц. true или false

Пример вызова

task.sh
# 1. Поставить задачу — в ответе придёт её номер
TASK=$(curl -s https://api.gen202.com/api/v1/jobs/createTask \
  -H "Authorization: Bearer sk-ваш-ключ" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance/2-fast",
    "input": {
      "prompt": "The ginger cat in a white spacesuit from the reference image sits on a snowy mountain peak at dawn, snowflakes drift past. Cinematic, cold blue light.",
      "aspect_ratio": "1:1",
      "resolution": "480p",
      "duration": 4,
      "reference_image_urls": [
        "https://gen202.com/assets/sample-nano-banana-pro.jpg"
      ]
    }
  }' | jq -r '.data.taskId')

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while true; do
  RECORD=$(curl -s "https://api.gen202.com/api/v1/jobs/recordInfo?taskId=$TASK" \
    -H "Authorization: Bearer sk-ваш-ключ")
  STATE=$(echo "$RECORD" | jq -r '.data.state')
  case "$STATE" in success|fail) break;; esac
  sleep 3
done

# 3. Забрать результат. Ссылка на файл лежит в resultJson — это строка JSON
#    внутри JSON, поэтому её разбирают вторым разбором (fromjson)
echo "$RECORD" | jq -r '.data |
  if .state == "fail"
  then "отказ \(.failCode): \(.failMsg)"
  else .resultJson | fromjson | .resultUrls[0]
  end'
task.py
import json
import time

import requests

headers = {"Authorization": "Bearer sk-ваш-ключ"}

# 1. Поставить задачу — в ответе придёт её номер
created = requests.post(
    "https://api.gen202.com/api/v1/jobs/createTask",
    headers=headers,
    json={
      "model": "seedance/2-fast",
      "input": {
        "prompt": "The ginger cat in a white spacesuit from the reference image sits on a snowy mountain peak at dawn, snowflakes drift past. Cinematic, cold blue light.",
        "aspect_ratio": "1:1",
        "resolution": "480p",
        "duration": 4,
        "reference_image_urls": [
          "https://gen202.com/assets/sample-nano-banana-pro.jpg"
        ]
      }
    },
).json()
task_id = created["data"]["taskId"]

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while True:
    task = requests.get(
        "https://api.gen202.com/api/v1/jobs/recordInfo",
        headers=headers,
        params={"taskId": task_id},
    ).json()["data"]
    if task["state"] in ("success", "fail"):
        break
    time.sleep(3)

# 3. Разобрать итог. У неудачи причина в failCode и failMsg
if task["state"] == "fail":
    raise SystemExit("отказ " + task["failCode"] + ": " + task["failMsg"])

# resultJson — строка JSON внутри JSON, поэтому разбор второй
result = json.loads(task["resultJson"])
print(result["resultUrls"][0])
print("списано кредитов:", task["creditsConsumed"])

Пример проходит весь путь: ставит задачу, повторяет запрос состояния раз в 3 секунды, пока задача не закончится, и разбирает ответ до ссылки на файл. Разборов два, потому что поле resultJson — строка JSON внутри JSON. В теле первого запроса стоят обязательные поля модели и значения из её перечислений, поэтому он проходит проверку шлюза как есть. Подставить остаётся только свой ключ. Пример на оболочке разбирает ответ через jq; на Python своего ничего не нужно, кроме requests.

Ответ, когда задача готова

recordInfo.json
{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "cm8r4t0vk0001s60p2xq7f9ab",
    "model": "seedance/2-fast",
    "state": "success",
    "resultJson": "{\"resultUrls\":[\"https://file.example/result.mp4\"]}",
    "failCode": "",
    "failMsg": ""
  }
}

Ссылка на файл лежит в resultJson — это строка, внутри которой ещё один JSON, поэтому разбирать её нужно вторым разбором. Пока задача не завершилась, там пустая строка, а в state — waiting, queuing или generating. В настоящем ответе есть ещё поле creditsConsumed со списанием; здесь его нет намеренно — сумма зависит от запрошенного варианта, а цены живут в каталоге. Все поля записи перечислены в разделе Формат ответа.

Общее для всех моделей: Как устроена задача  — чем отправить и как дождаться · Формат ответа  — где в ответе ссылка на файл · Загрузка своих файлов  — как получить ссылку на свою картинку · Отказы  — что означает отказ

seedance/2-mini

Поля запроса

ПолеТипОбяз.Допустимые значения
model текст обязательное seedance/2-mini
input объект обязательное Параметры генерации — поля из таблицы ниже

Поля input

ПолеТипОбяз.Допустимые значения
prompt текст обязательное от 3 до 20 000 символов
first_frame_url ссылка опц. одна ссылка на изображение
last_frame_url ссылка опц. одна ссылка на изображение
reference_image_urls список ссылок опц. до 9 ссылок на изображение
reference_video_urls список ссылок опц. до 3 ссылок на видео
reference_audio_urls список ссылок опц. до 3 ссылок на аудио
generate_audio да или нет опц. true или false
resolution значение из списка опц. 480p · 720p
aspect_ratio значение из списка опц. 1:1 · 4:3 · 3:4 · 16:9 · 9:16 · 21:9 · adaptive
duration целое число опц. целое от 4 до 15
web_search да или нет опц. true или false

Пример вызова

task.sh
# 1. Поставить задачу — в ответе придёт её номер
TASK=$(curl -s https://api.gen202.com/api/v1/jobs/createTask \
  -H "Authorization: Bearer sk-ваш-ключ" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance/2-mini",
    "input": {
      "prompt": "The ginger cat in a white spacesuit from the reference image walks along a sandy beach at sunset, waves roll in behind it. Cinematic, warm light.",
      "aspect_ratio": "1:1",
      "resolution": "480p",
      "duration": 4,
      "reference_image_urls": [
        "https://gen202.com/assets/sample-nano-banana-pro.jpg"
      ]
    }
  }' | jq -r '.data.taskId')

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while true; do
  RECORD=$(curl -s "https://api.gen202.com/api/v1/jobs/recordInfo?taskId=$TASK" \
    -H "Authorization: Bearer sk-ваш-ключ")
  STATE=$(echo "$RECORD" | jq -r '.data.state')
  case "$STATE" in success|fail) break;; esac
  sleep 3
done

# 3. Забрать результат. Ссылка на файл лежит в resultJson — это строка JSON
#    внутри JSON, поэтому её разбирают вторым разбором (fromjson)
echo "$RECORD" | jq -r '.data |
  if .state == "fail"
  then "отказ \(.failCode): \(.failMsg)"
  else .resultJson | fromjson | .resultUrls[0]
  end'
task.py
import json
import time

import requests

headers = {"Authorization": "Bearer sk-ваш-ключ"}

# 1. Поставить задачу — в ответе придёт её номер
created = requests.post(
    "https://api.gen202.com/api/v1/jobs/createTask",
    headers=headers,
    json={
      "model": "seedance/2-mini",
      "input": {
        "prompt": "The ginger cat in a white spacesuit from the reference image walks along a sandy beach at sunset, waves roll in behind it. Cinematic, warm light.",
        "aspect_ratio": "1:1",
        "resolution": "480p",
        "duration": 4,
        "reference_image_urls": [
          "https://gen202.com/assets/sample-nano-banana-pro.jpg"
        ]
      }
    },
).json()
task_id = created["data"]["taskId"]

# 2. Спрашивать состояние, пока задача не закончится: waiting, queuing и
#    generating означают «работа идёт», success и fail — окончательные
while True:
    task = requests.get(
        "https://api.gen202.com/api/v1/jobs/recordInfo",
        headers=headers,
        params={"taskId": task_id},
    ).json()["data"]
    if task["state"] in ("success", "fail"):
        break
    time.sleep(3)

# 3. Разобрать итог. У неудачи причина в failCode и failMsg
if task["state"] == "fail":
    raise SystemExit("отказ " + task["failCode"] + ": " + task["failMsg"])

# resultJson — строка JSON внутри JSON, поэтому разбор второй
result = json.loads(task["resultJson"])
print(result["resultUrls"][0])
print("списано кредитов:", task["creditsConsumed"])

Пример проходит весь путь: ставит задачу, повторяет запрос состояния раз в 3 секунды, пока задача не закончится, и разбирает ответ до ссылки на файл. Разборов два, потому что поле resultJson — строка JSON внутри JSON. В теле первого запроса стоят обязательные поля модели и значения из её перечислений, поэтому он проходит проверку шлюза как есть. Подставить остаётся только свой ключ. Пример на оболочке разбирает ответ через jq; на Python своего ничего не нужно, кроме requests.

Ответ, когда задача готова

recordInfo.json
{
  "code": 200,
  "msg": "success",
  "data": {
    "taskId": "cm8r4t0vk0001s60p2xq7f9ab",
    "model": "seedance/2-mini",
    "state": "success",
    "resultJson": "{\"resultUrls\":[\"https://file.example/result.mp4\"]}",
    "failCode": "",
    "failMsg": ""
  }
}

Ссылка на файл лежит в resultJson — это строка, внутри которой ещё один JSON, поэтому разбирать её нужно вторым разбором. Пока задача не завершилась, там пустая строка, а в state — waiting, queuing или generating. В настоящем ответе есть ещё поле creditsConsumed со списанием; здесь его нет намеренно — сумма зависит от запрошенного варианта, а цены живут в каталоге. Все поля записи перечислены в разделе Формат ответа.

Общее для всех моделей: Как устроена задача  — чем отправить и как дождаться · Формат ответа  — где в ответе ссылка на файл · Загрузка своих файлов  — как получить ссылку на свою картинку · Отказы  — что означает отказ