Seedance 2.0 API

model: seedance/2

model: seedance/2-fast

model: seedance/2-mini

Одной моделью делает ролик по описанию, из присланного изображения и по изображениям-образцам. Разрешение 480p, 720p, 1080p, 4k, 7 соотношений сторон, от 1:1 до adaptive, описание до 20 000 символов. Модель ByteDance.

Одной моделью делает ролик по описанию, из присланного изображения и по изображениям-образцам. Разрешение 480p, 720p, 7 соотношений сторон, от 1:1 до adaptive, описание до 20 000 символов. Модель ByteDance.

Одной моделью делает ролик по описанию, из присланного изображения и по изображениям-образцам. Разрешение 480p, 720p, 7 соотношений сторон, от 1:1 до adaptive, описание до 20 000 символов. Модель ByteDance.

Цена: за секунду, от 15.19 кр. ($0.076) до 280.8 кр. ($1.404).

Цена: за секунду, от 12.15 кр. ($0.0608) до 43.54 кр. ($0.2177).

Цена: за секунду, от 7.79 кр. ($0.039) до 27.85 кр. ($0.1392).

кр. — кредиты, покупаются за рубли.

Режим

Песочница

Input

Столько стоит этот запуск: ставка выбранного варианта × запрошенные секунды. Столько и спишется, когда работа закончится. Считаем по полям формы: ставка варианта × запрошенные секунды. Если поле влияет на цену, а вы его не заполнили, берём самое дорогое значение. Больше 30 не бывает: выход до 15 с плюс до 15 с видео на входе. Спишется фактический расход, лишнее вернётся на баланс.

Ролик собирается после входа: новому счёту начисляют 50 кредитов, и первый ролик идёт на них. Ставка у этой модели 15.19–280.8 кр. за секунду. Войдите, чтобы начать.

{
  "model": "seedance/2",
  "input": {
    "prompt": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
    "aspect_ratio": "1:1",
    "resolution": "480p",
    "duration": 4
  }
}
Столько стоит этот запуск: ставка выбранного варианта × запрошенные секунды. Столько и спишется, когда работа закончится. Считаем по полям формы: ставка варианта × запрошенные секунды. Если поле влияет на цену, а вы его не заполнили, берём самое дорогое значение. Больше 30 не бывает: выход до 15 с плюс до 15 с видео на входе. Спишется фактический расход, лишнее вернётся на баланс.
Output

Нажмите «Запустить» — готовый ролик появится здесь

Ролик делается минуты. Задача живёт не дольше 60 минут, после чего шлюз закрывает её сам и возвращает удержание целиком.

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

Столько стоит этот запуск: ставка выбранного варианта × запрошенные секунды. Столько и спишется, когда работа закончится. Считаем по полям формы: ставка варианта × запрошенные секунды. Если поле влияет на цену, а вы его не заполнили, берём самое дорогое значение. Больше 30 не бывает: выход до 15 с плюс до 15 с видео на входе. Спишется фактический расход, лишнее вернётся на баланс.

Ролик собирается после входа: новому счёту начисляют 50 кредитов, и первый ролик идёт на них. Ставка у этой модели 12.15–43.54 кр. за секунду. Войдите, чтобы начать.

{
  "model": "seedance/2-fast",
  "input": {
    "prompt": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
    "aspect_ratio": "1:1",
    "resolution": "480p",
    "duration": 4
  }
}
Столько стоит этот запуск: ставка выбранного варианта × запрошенные секунды. Столько и спишется, когда работа закончится. Считаем по полям формы: ставка варианта × запрошенные секунды. Если поле влияет на цену, а вы его не заполнили, берём самое дорогое значение. Больше 30 не бывает: выход до 15 с плюс до 15 с видео на входе. Спишется фактический расход, лишнее вернётся на баланс.
Output

Нажмите «Запустить» — готовый ролик появится здесь

Ролик делается минуты. Задача живёт не дольше 60 минут, после чего шлюз закрывает её сам и возвращает удержание целиком.

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

Столько стоит этот запуск: ставка выбранного варианта × запрошенные секунды. Столько и спишется, когда работа закончится. Считаем по полям формы: ставка варианта × запрошенные секунды. Если поле влияет на цену, а вы его не заполнили, берём самое дорогое значение. Больше 30 не бывает: выход до 15 с плюс до 15 с видео на входе. Спишется фактический расход, лишнее вернётся на баланс.

Ролик собирается после входа: новому счёту начисляют 50 кредитов, и первый ролик идёт на них. Ставка у этой модели 7.79–27.85 кр. за секунду. Войдите, чтобы начать.

{
  "model": "seedance/2-mini",
  "input": {
    "prompt": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
    "aspect_ratio": "1:1",
    "resolution": "480p",
    "duration": 4
  }
}
Столько стоит этот запуск: ставка выбранного варианта × запрошенные секунды. Столько и спишется, когда работа закончится. Считаем по полям формы: ставка варианта × запрошенные секунды. Если поле влияет на цену, а вы его не заполнили, берём самое дорогое значение. Больше 30 не бывает: выход до 15 с плюс до 15 с видео на входе. Спишется фактический расход, лишнее вернётся на баланс.
Output

Нажмите «Запустить» — готовый ролик появится здесь

Ролик делается минуты. Задача живёт не дольше 60 минут, после чего шлюз закрывает её сам и возвращает удержание целиком.

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

Цена Seedance 2.0

Цена за секунду. Списывается фактический расход, о котором сообщил поставщик.

Вариант Наша цена Скидка Официальная
480p with video input 15.19 кр. · $0.0767,6 ₽ −10% $0.0844
480p no video input 25.31 кр. · $0.126512,65 ₽ −10% $0.1406
720p with video input 32.65 кр. · $0.163316,33 ₽ −10% $0.1814
720p no video input 54.43 кр. · $0.272227,22 ₽ −10% $0.3024
1080p with video input 73.48 кр. · $0.367436,74 ₽ −10% $0.4082
1080p no video input 122.47 кр. · $0.612461,24 ₽ −10% $0.6804
4K with video input 167.4 кр. · $0.83783,7 ₽ −10% $0.93
4K no video input 280.8 кр. · $1.404140,4 ₽ −10% $1.56

В среднем на 10% ниже официальной цены поставщика. кр. — кредиты, покупаются за рубли.

Цена за секунду. Списывается фактический расход, о котором сообщил поставщик.

Вариант Наша цена Скидка Официальная
480p with video input 12.15 кр. · $0.06086,08 ₽ −10% $0.0675
480p no video input 20.25 кр. · $0.101310,13 ₽ −10% $0.1125
720p with video input 26.12 кр. · $0.130613,06 ₽ −10% $0.1451
720p no video input 43.54 кр. · $0.217721,77 ₽ −10% $0.2419

В среднем на 10% ниже официальной цены поставщика. кр. — кредиты, покупаются за рубли.

Цена за секунду. Списывается фактический расход, о котором сообщил поставщик.

Вариант Наша цена Скидка Официальная
480P with video 7.79 кр. · $0.0393,9 ₽ −10% $0.0433
480P no video 12.98 кр. · $0.06496,49 ₽ −10% $0.0721
720P with video 16.7 кр. · $0.08358,35 ₽ −10% $0.0928
720P no video 27.85 кр. · $0.139213,92 ₽ −10% $0.1547

В среднем на 10% ниже официальной цены поставщика. кр. — кредиты, покупаются за рубли.

На время работы шлюз удерживает ставку выбранного разрешения, умноженную на запрошенную длительность; больше 8424 кредита по этой модели он не удержит ни при каком запросе (выход до 15 с плюс до 15 с видео на входе). Разница между удержанием и фактическим расходом возвращается на баланс тем же запросом состояния, который увидел итог.

Вызов через API

POST https://api.gen202.com/api/v1/jobs/createTask
GET https://api.gen202.com/api/v1/jobs/recordInfo?taskId=…

Ролик делается минуты, поэтому файл не приходит в ответ на запрос. Первый вызов ставит задачу и сразу отвечает её номером, второй по этому номеру отдаёт состояние задачи, а когда она готова — ссылку на результат. Ключ передаётся в обоих запросах заголовком Authorization: Bearer sk-… и выпускается в кабинете. На один ключ шлюз пропускает 30 постановок задачи и 300 запросов состояния в минуту.

Поля запроса

Значения проверяются до обращения к поставщику: поле не из списка модели или значение вне перечисления возвращают 422 и кредитов не тратят.

ПолеТипОбяз.Допустимые значения
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
nsfw_checker да или нет опц. true или false

Ссылка на исходный файл принимается только полным адресом на http или https: не длиннее 2048 символов, на общедоступном сайте, без имени и пароля перед адресом и без порта, кроме 80 и 443. Относительный путь, а также адреса внутри сети — localhost, 127.0.0.1, 10.0.0.5, 192.168.1.2 — и имена без точки шлюз отклоняет ответом 422, к поставщику не обращаясь.

Ответ

Оба запроса отвечают одним конвертом: {"code":…,"msg":…,"data":…}, где code повторяет HTTP-статус, а полезное лежит в data. Тот же конверт приходит и при отказе — разбирать две формы ответа не нужно.

/api/v1/jobs/createTask

ПолеТипЧто в нём
data.taskId строка Номер задачи. Больше в ответе ничего нет и быть не может — работа только началась.

/api/v1/jobs/recordInfo

ПолеТипЧто в нём
data.taskId строка Номер задачи — тот же, что вернул createTask.
data.model строка Имя модели в том виде, в каком его прислал клиент.
data.state строка Состояние задачи: waiting, queuing, generating, success или fail.
data.param строка Параметры, с которыми задача создана, — строкой JSON внутри JSON.
data.resultJson строка Результат строкой JSON внутри JSON: {"resultUrls":["https://…"]} — разбирается вторым разбором. До завершения задачи — пустая строка.
data.failCode строка Код неудачи из таблицы ниже. У остальных задач — пустая строка.
data.failMsg строка Причина неудачи словами. Иначе пустая строка.
data.costTime число Сколько задача заняла, миллисекунды. Пока не завершилась — null.
data.completeTime число Когда завершилась, миллисекунды эпохи Unix. Пока не завершилась — null.
data.createTime число Когда создана, миллисекунды эпохи Unix.
data.updateTime число Когда состояние менялось в последний раз, миллисекунды эпохи Unix.
data.creditsConsumed число Сколько кредитов списано. Ноль, пока задача не завершилась, и ноль у неудачной.

Состояния задачи

Состояние лежит в поле state ответа /api/v1/jobs/recordInfo. Первые три означают, что работа идёт: запрос надо повторить примерно через 3 секунды — чаще спрашивать нечего, столько же ждёт между опросами сам шлюз. Два последних состояния окончательные: после них задача не меняется.

stateЧто происходит
waiting Задача принята и стоит в очереди шлюза; поставщику она ещё не отправлена.
queuing Поставщик задачу принял и поставил в свою очередь.
generating Генерация идёт.
success Готово: ссылки на результат лежат в resultJson, в creditsConsumed — сколько списано.
fail Задача не удалась: причина в failCode и failMsg, удержанные кредиты возвращены целиком.

Опрос не бывает бесконечным: задача на видео живёт не дольше 60 минут, после чего шлюз закрывает её сам состоянием fail и возвращает удержанные кредиты целиком. Причина неудачи приходит двумя полями: failCode из таблицы ниже и failMsg словами.

failCodeЧто произошло
501 Поставщик вернул отказ: генерация не удалась.
408 Результата нет дольше крайнего срока задачи (60 минут для видео).
404 Поставщик не знает такой задачи.
429 Поставщик отбил создание задачи по своему лимиту.
500 Поломка на нашей стороне; подробности остаются в журнале шлюза.

Ссылка на готовый файл живёт 14 дней — столько его хранит поставщик. Файл, который нужен дольше, скачивайте к себе сразу после того, как задача пришла в состояние success.

Отказы

Тело отказа одно на все случаи: {"code":…,"msg":…,"data":null}. Задача, не дошедшая до результата, не тарифицируется — удержанные кредиты возвращаются целиком.

КодАдресКогда
400 /api/v1/jobs/createTask Тело запроса — не разбираемый JSON.
401 /api/v1/jobs/createTask Ключа нет в заголовке Authorization, либо он неверный или отключён.
402 /api/v1/jobs/createTask Свободных кредитов меньше, чем удерживается под задачу.
413 /api/v1/jobs/createTask Тело запроса больше 512 КиБ.
415 /api/v1/jobs/createTask Тело отправлено не как application/json или заголовок Content-Type не передан.
422 /api/v1/jobs/createTask Поле model пустое или его имени нет в каталоге, input — не объект, поле не из списка модели либо значение вне её перечисления. В сообщении перечислено, что принимается.
429 /api/v1/jobs/createTask Больше 30 запросов в минуту на один ключ либо больше 50 незавершённых задач на аккаунте.
500 /api/v1/jobs/createTask Поломка на нашей стороне. Подробности остаются в журнале шлюза и наружу не уходят.
401 /api/v1/jobs/recordInfo Ключа нет в заголовке Authorization, либо он неверный или отключён.
404 /api/v1/jobs/recordInfo Задачи с таким номером нет или она создана другим аккаунтом.
422 /api/v1/jobs/recordInfo Параметр taskId не передан.
429 /api/v1/jobs/recordInfo Больше 300 запросов в минуту на один ключ.
500 /api/v1/jobs/recordInfo Поломка на нашей стороне. Подробности остаются в журнале шлюза и наружу не уходят.

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

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": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
      "aspect_ratio": "1:1",
      "resolution": "480p",
      "duration": 4
    }
  }' | 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": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
        "aspect_ratio": "1:1",
        "resolution": "480p",
        "duration": 4
      }
    },
).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.

POST https://api.gen202.com/api/v1/jobs/createTask
GET https://api.gen202.com/api/v1/jobs/recordInfo?taskId=…

Ролик делается минуты, поэтому файл не приходит в ответ на запрос. Первый вызов ставит задачу и сразу отвечает её номером, второй по этому номеру отдаёт состояние задачи, а когда она готова — ссылку на результат. Ключ передаётся в обоих запросах заголовком Authorization: Bearer sk-… и выпускается в кабинете. На один ключ шлюз пропускает 30 постановок задачи и 300 запросов состояния в минуту.

Поля запроса

Значения проверяются до обращения к поставщику: поле не из списка модели или значение вне перечисления возвращают 422 и кредитов не тратят.

ПолеТипОбяз.Допустимые значения
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
nsfw_checker да или нет опц. true или false

Ссылка на исходный файл принимается только полным адресом на http или https: не длиннее 2048 символов, на общедоступном сайте, без имени и пароля перед адресом и без порта, кроме 80 и 443. Относительный путь, а также адреса внутри сети — localhost, 127.0.0.1, 10.0.0.5, 192.168.1.2 — и имена без точки шлюз отклоняет ответом 422, к поставщику не обращаясь.

Ответ

Оба запроса отвечают одним конвертом: {"code":…,"msg":…,"data":…}, где code повторяет HTTP-статус, а полезное лежит в data. Тот же конверт приходит и при отказе — разбирать две формы ответа не нужно.

/api/v1/jobs/createTask

ПолеТипЧто в нём
data.taskId строка Номер задачи. Больше в ответе ничего нет и быть не может — работа только началась.

/api/v1/jobs/recordInfo

ПолеТипЧто в нём
data.taskId строка Номер задачи — тот же, что вернул createTask.
data.model строка Имя модели в том виде, в каком его прислал клиент.
data.state строка Состояние задачи: waiting, queuing, generating, success или fail.
data.param строка Параметры, с которыми задача создана, — строкой JSON внутри JSON.
data.resultJson строка Результат строкой JSON внутри JSON: {"resultUrls":["https://…"]} — разбирается вторым разбором. До завершения задачи — пустая строка.
data.failCode строка Код неудачи из таблицы ниже. У остальных задач — пустая строка.
data.failMsg строка Причина неудачи словами. Иначе пустая строка.
data.costTime число Сколько задача заняла, миллисекунды. Пока не завершилась — null.
data.completeTime число Когда завершилась, миллисекунды эпохи Unix. Пока не завершилась — null.
data.createTime число Когда создана, миллисекунды эпохи Unix.
data.updateTime число Когда состояние менялось в последний раз, миллисекунды эпохи Unix.
data.creditsConsumed число Сколько кредитов списано. Ноль, пока задача не завершилась, и ноль у неудачной.

Состояния задачи

Состояние лежит в поле state ответа /api/v1/jobs/recordInfo. Первые три означают, что работа идёт: запрос надо повторить примерно через 3 секунды — чаще спрашивать нечего, столько же ждёт между опросами сам шлюз. Два последних состояния окончательные: после них задача не меняется.

stateЧто происходит
waiting Задача принята и стоит в очереди шлюза; поставщику она ещё не отправлена.
queuing Поставщик задачу принял и поставил в свою очередь.
generating Генерация идёт.
success Готово: ссылки на результат лежат в resultJson, в creditsConsumed — сколько списано.
fail Задача не удалась: причина в failCode и failMsg, удержанные кредиты возвращены целиком.

Опрос не бывает бесконечным: задача на видео живёт не дольше 60 минут, после чего шлюз закрывает её сам состоянием fail и возвращает удержанные кредиты целиком. Причина неудачи приходит двумя полями: failCode из таблицы ниже и failMsg словами.

failCodeЧто произошло
501 Поставщик вернул отказ: генерация не удалась.
408 Результата нет дольше крайнего срока задачи (60 минут для видео).
404 Поставщик не знает такой задачи.
429 Поставщик отбил создание задачи по своему лимиту.
500 Поломка на нашей стороне; подробности остаются в журнале шлюза.

Ссылка на готовый файл живёт 14 дней — столько его хранит поставщик. Файл, который нужен дольше, скачивайте к себе сразу после того, как задача пришла в состояние success.

Отказы

Тело отказа одно на все случаи: {"code":…,"msg":…,"data":null}. Задача, не дошедшая до результата, не тарифицируется — удержанные кредиты возвращаются целиком.

КодАдресКогда
400 /api/v1/jobs/createTask Тело запроса — не разбираемый JSON.
401 /api/v1/jobs/createTask Ключа нет в заголовке Authorization, либо он неверный или отключён.
402 /api/v1/jobs/createTask Свободных кредитов меньше, чем удерживается под задачу.
413 /api/v1/jobs/createTask Тело запроса больше 512 КиБ.
415 /api/v1/jobs/createTask Тело отправлено не как application/json или заголовок Content-Type не передан.
422 /api/v1/jobs/createTask Поле model пустое или его имени нет в каталоге, input — не объект, поле не из списка модели либо значение вне её перечисления. В сообщении перечислено, что принимается.
429 /api/v1/jobs/createTask Больше 30 запросов в минуту на один ключ либо больше 50 незавершённых задач на аккаунте.
500 /api/v1/jobs/createTask Поломка на нашей стороне. Подробности остаются в журнале шлюза и наружу не уходят.
401 /api/v1/jobs/recordInfo Ключа нет в заголовке Authorization, либо он неверный или отключён.
404 /api/v1/jobs/recordInfo Задачи с таким номером нет или она создана другим аккаунтом.
422 /api/v1/jobs/recordInfo Параметр taskId не передан.
429 /api/v1/jobs/recordInfo Больше 300 запросов в минуту на один ключ.
500 /api/v1/jobs/recordInfo Поломка на нашей стороне. Подробности остаются в журнале шлюза и наружу не уходят.

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

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": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
      "aspect_ratio": "1:1",
      "resolution": "480p",
      "duration": 4
    }
  }' | 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": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
        "aspect_ratio": "1:1",
        "resolution": "480p",
        "duration": 4
      }
    },
).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.

POST https://api.gen202.com/api/v1/jobs/createTask
GET https://api.gen202.com/api/v1/jobs/recordInfo?taskId=…

Ролик делается минуты, поэтому файл не приходит в ответ на запрос. Первый вызов ставит задачу и сразу отвечает её номером, второй по этому номеру отдаёт состояние задачи, а когда она готова — ссылку на результат. Ключ передаётся в обоих запросах заголовком Authorization: Bearer sk-… и выпускается в кабинете. На один ключ шлюз пропускает 30 постановок задачи и 300 запросов состояния в минуту.

Поля запроса

Значения проверяются до обращения к поставщику: поле не из списка модели или значение вне перечисления возвращают 422 и кредитов не тратят.

ПолеТипОбяз.Допустимые значения
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
nsfw_checker да или нет опц. true или false

Ссылка на исходный файл принимается только полным адресом на http или https: не длиннее 2048 символов, на общедоступном сайте, без имени и пароля перед адресом и без порта, кроме 80 и 443. Относительный путь, а также адреса внутри сети — localhost, 127.0.0.1, 10.0.0.5, 192.168.1.2 — и имена без точки шлюз отклоняет ответом 422, к поставщику не обращаясь.

Ответ

Оба запроса отвечают одним конвертом: {"code":…,"msg":…,"data":…}, где code повторяет HTTP-статус, а полезное лежит в data. Тот же конверт приходит и при отказе — разбирать две формы ответа не нужно.

/api/v1/jobs/createTask

ПолеТипЧто в нём
data.taskId строка Номер задачи. Больше в ответе ничего нет и быть не может — работа только началась.

/api/v1/jobs/recordInfo

ПолеТипЧто в нём
data.taskId строка Номер задачи — тот же, что вернул createTask.
data.model строка Имя модели в том виде, в каком его прислал клиент.
data.state строка Состояние задачи: waiting, queuing, generating, success или fail.
data.param строка Параметры, с которыми задача создана, — строкой JSON внутри JSON.
data.resultJson строка Результат строкой JSON внутри JSON: {"resultUrls":["https://…"]} — разбирается вторым разбором. До завершения задачи — пустая строка.
data.failCode строка Код неудачи из таблицы ниже. У остальных задач — пустая строка.
data.failMsg строка Причина неудачи словами. Иначе пустая строка.
data.costTime число Сколько задача заняла, миллисекунды. Пока не завершилась — null.
data.completeTime число Когда завершилась, миллисекунды эпохи Unix. Пока не завершилась — null.
data.createTime число Когда создана, миллисекунды эпохи Unix.
data.updateTime число Когда состояние менялось в последний раз, миллисекунды эпохи Unix.
data.creditsConsumed число Сколько кредитов списано. Ноль, пока задача не завершилась, и ноль у неудачной.

Состояния задачи

Состояние лежит в поле state ответа /api/v1/jobs/recordInfo. Первые три означают, что работа идёт: запрос надо повторить примерно через 3 секунды — чаще спрашивать нечего, столько же ждёт между опросами сам шлюз. Два последних состояния окончательные: после них задача не меняется.

stateЧто происходит
waiting Задача принята и стоит в очереди шлюза; поставщику она ещё не отправлена.
queuing Поставщик задачу принял и поставил в свою очередь.
generating Генерация идёт.
success Готово: ссылки на результат лежат в resultJson, в creditsConsumed — сколько списано.
fail Задача не удалась: причина в failCode и failMsg, удержанные кредиты возвращены целиком.

Опрос не бывает бесконечным: задача на видео живёт не дольше 60 минут, после чего шлюз закрывает её сам состоянием fail и возвращает удержанные кредиты целиком. Причина неудачи приходит двумя полями: failCode из таблицы ниже и failMsg словами.

failCodeЧто произошло
501 Поставщик вернул отказ: генерация не удалась.
408 Результата нет дольше крайнего срока задачи (60 минут для видео).
404 Поставщик не знает такой задачи.
429 Поставщик отбил создание задачи по своему лимиту.
500 Поломка на нашей стороне; подробности остаются в журнале шлюза.

Ссылка на готовый файл живёт 14 дней — столько его хранит поставщик. Файл, который нужен дольше, скачивайте к себе сразу после того, как задача пришла в состояние success.

Отказы

Тело отказа одно на все случаи: {"code":…,"msg":…,"data":null}. Задача, не дошедшая до результата, не тарифицируется — удержанные кредиты возвращаются целиком.

КодАдресКогда
400 /api/v1/jobs/createTask Тело запроса — не разбираемый JSON.
401 /api/v1/jobs/createTask Ключа нет в заголовке Authorization, либо он неверный или отключён.
402 /api/v1/jobs/createTask Свободных кредитов меньше, чем удерживается под задачу.
413 /api/v1/jobs/createTask Тело запроса больше 512 КиБ.
415 /api/v1/jobs/createTask Тело отправлено не как application/json или заголовок Content-Type не передан.
422 /api/v1/jobs/createTask Поле model пустое или его имени нет в каталоге, input — не объект, поле не из списка модели либо значение вне её перечисления. В сообщении перечислено, что принимается.
429 /api/v1/jobs/createTask Больше 30 запросов в минуту на один ключ либо больше 50 незавершённых задач на аккаунте.
500 /api/v1/jobs/createTask Поломка на нашей стороне. Подробности остаются в журнале шлюза и наружу не уходят.
401 /api/v1/jobs/recordInfo Ключа нет в заголовке Authorization, либо он неверный или отключён.
404 /api/v1/jobs/recordInfo Задачи с таким номером нет или она создана другим аккаунтом.
422 /api/v1/jobs/recordInfo Параметр taskId не передан.
429 /api/v1/jobs/recordInfo Больше 300 запросов в минуту на один ключ.
500 /api/v1/jobs/recordInfo Поломка на нашей стороне. Подробности остаются в журнале шлюза и наружу не уходят.

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

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": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
      "aspect_ratio": "1:1",
      "resolution": "480p",
      "duration": 4
    }
  }' | 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": "Рыжий кот в скафандре на фоне Земли, мягкий свет",
        "aspect_ratio": "1:1",
        "resolution": "480p",
        "duration": 4
      }
    },
).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.

Остальные модели этого семейства и их поля — в разделе документации Видео.

Что делает нейросеть Seedance 2.0

Seedance 2.0 — нейросеть ByteDance: она делает видео со звуком по описанию, снимку или готовому ролику.

Каждый уровень берёт сразу несколько работ: видео из текста, видео из изображения и видео по образцам. Переключателя между ними нет — работу задаёт то, что положено в поля запроса.

Уровней 3, и в поле model у каждого своё имя: seedance/2, seedance/2-fast и seedance/2-mini.

Обязательным не объявлено ни одно поле, и запрос с одним описанием шлюз пропустит. Весь набор полей вместе с допустимыми значениями стоит таблицей выше, а вызов целиком разобран в разделе «Seedance».

Как пользоваться Seedance 2.0 и можно ли её скачать

Работают с моделью прямо в браузере: поля заполняют в песочнице выше, а готовый ролик проигрывается там же. Скачать Seedance 2.0 к себе на компьютер нельзя: это не программа, а модель на стороне разработчика. Скачивается результат: готовый ролик забирается в mp4.

Сделать ролик в Seedance 2.0 бесплатно не получится: за каждую секунду списывается плата по ставке из таблицы выше. Приветственные кредиты новому счёту начисляют при первом входе — с них и начинается работа с моделью. Помесячной подписки за доступ к модели нет — деньги уходят только за поставленные задачи.

Официальный сайт Seedance 2.0 держит сам ByteDance, и GEN202 им не является. Мы открываем ту же модель клиентам из России: запрос уходит на наш адрес, а не за границу.

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

  1. Опишите сцену словами. В поле prompt пишут, кто в кадре, что делает и как стоит камера. Главное ставят в начало.
  2. Выберите разрешение и длительность. Поля resolution и duration задают, каким выйдет ролик, и вместе они определяют цену задачи.
  3. Приложите образцы, если они есть. Ссылки на картинки, ролики и звук кладут каждую в своё поле. Без них модель снимает по одному описанию.
  4. Решите, нужен ли звук. Звуковую дорожку включает флаг generate_audio, а сами звуки сцены называют там же, где сюжет.
  5. Проверьте описание коротким роликом. Секунда в низком разрешении стоит меньше. Замысел поэтому сверяют на коротком ролике, а удачное описание повторяют в нужном разрешении.

Чем Fast и Mini отличаются от Seedance 2.0

Различия видны прямо в полях запроса. Первое — разрешение: старший уровень доходит до 4k, а у Fast и Mini потолок 720p.

Второе мельче: поле return_last_frame стоит у Seedance 2.0 и Seedance 2.0 Fast, а у Seedance 2.0 Mini его нет. По нему вместе с готовым роликом приходит его последний кадр.

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

Какой длины и в каком разрешении выходит ролик Seedance 2.0

Длина задаётся целым числом секунд в поле duration, от 4 до 15, и эта вилка одна на все уровни.

Разрешение — обычное поле запроса, и меняется оно от вызова к вызову; отдельного имени модели под 4K не заведено.

Форм кадра 7: 1:1, 4:3, 3:4, 16:9, 9:16, 21:9, adaptive.

Что кладут в запрос Seedance 2.0 кроме описания

Образцы разложены по трём полям, и каждое берёт свой вид файла: reference_image_urls — картинки, reference_video_urls — готовые ролики, reference_audio_urls — звук. Пределы у списков разные и стоят в таблице полей.

Первый и последний кадр в эти списки не входят: под них заведены свои поля first_frame_url и last_frame_url, по одной ссылке в каждом.

Звук пишется по флагу generate_audio. Что делать с присланным материалом, объясняют там же, где пишут сюжет, — в поле prompt.

Как выглядит собранное тело запроса, видно в песочнице: форма заполнена допустимыми значениями до первой правки.

Что ByteDance обещает от Seedance 2.0

Обещания ByteDance перечислены ниже так, как он называет их сам: вход, звук, устойчивость картинки и применения.

  • Материал на входе бывает четырёх видов сразу: текст, изображения, звук и готовое видео.
  • Работ с готовым роликом две: переделать его и продлить.
  • Звук рождается вместе с картинкой и совпадает с ней по времени с точностью до миллисекунд — от шума на фоне до человеческой речи.
  • Строение предметов обещано устойчивым и на быстром движении, и на переходах между кадрами.
  • В 4K ролик отдаётся с десятибитным кодированием цвета — ради плавных переходов оттенков, кино и HDR.
  • Названные применения: съёмка фильмов и роликов, пересказ новостей, реклама товара и черновая визуализация будущих сцен.

О чём ByteDance предупреждает у Seedance 2.0

Настоящее человеческое лицо на вход не принимается ни снимком, ни роликом.

Исключение сделано для прежних выдач самих этих моделей: то, что они выдали по тому же счёту, кладётся на вход как есть.

Готовый 4K приходит в кодировании H.265, и часть проигрывателей и браузеров такой файл не откроет.

Что при съёмке в Seedance 2.0 выходит не с первой попытки

Разработчик перечисляет то, что модель умеет, а трудности работы с ней не называет. Ниже — места, которые видно только на съёмке, и обходы, которые для них нашли.

  • Если в кадре есть зеркало, стекло или мокрый асфальт, отражение может не совпасть с происходящим рядом или раздвоиться.
  • Быстрое движение даётся модели труднее спокойного. Слово «быстро» вместе с частой сменой планов и насыщенным фоном добавляет дрожание и искажения.
  • Из длинного описания выполняется не всё: часть указаний молча не попадает в ролик. Начало описания модель выполняет точнее конца, поэтому главное ставят вперёд, а украшения убирают.
  • Отдельного поля под запреты у модели нет. Слова «без размытия» и «без рук» в описании нередко приводят ровно к тому, что запрещали. Надёжнее назвать нужное: «резко и в фокусе».
  • Присланная картинка не становится первым кадром дословно — сцену модель перерисовывает по-своему. Совпадения добиваются со второй попытки: в описании прямо требуют повторить присланный кадр целиком.
  • Когда заданы и первый кадр, и последний, в месте перехода между ними предмет иногда двоится, а расположение предметов в кадре сбивается.
  • Надписи на вывесках и экранах выходят нечитаемыми. Нужный текст поэтому накладывают на готовый ролик поверх, а не просят у модели.
  • Музыку модель пишет вместе со звуками сцены. От ролика к ролику она не продолжается, а при перемонтаже кусков рвётся. Когда ролик будут резать, музыку просят не добавлять.
  • В ролике из нескольких планов один-два обычно выходят негодными. Их вырезают при монтаже, а не переснимают ролик целиком.

Сложную сцену поэтому снимают не одним роликом, а несколькими короткими и собирают их вместе при монтаже. Готовый ролик при этом кладут на вход следующего — так герои и обстановка не меняются от куска к куску.

Официальный канал BytePlus — объявление о выпуске от 14 апреля 2026 года: показано, что модель принимает на вход текст, картинку, звук и видео. Идёт чуть больше минуты.

Частые вопросы

Как вызвать Seedance 2.0 через API из России?
Запрос уходит на api.gen202.com, и это адрес GEN202, а не адрес ByteDance: обращения за границу в вызове Seedance 2.0 нет, поэтому VPN не требуется. В поле model уходит идентификатор выбранного варианта: 2.0 (seedance/2), fast (seedance/2-fast), mini (seedance/2-mini). Порядок вызова Seedance 2.0 с примерами кода разобран в документации.
Сколько стоит Seedance 2.0
Цена за секунду Seedance 2.0 — от 7.79 кр. до 280.8 кр. У Seedance 2.0 списывается фактический расход, а задача, завершившаяся ошибкой, не тарифицируется вовсе.
Нужны ли VPN и иностранная карта для Seedance 2.0
Ни то, ни другое: обращение к Seedance 2.0 идёт на наш адрес, а расчёты с ByteDance ведём мы, поэтому договариваться с ним клиенту не о чем. Оплата рублями с карты российского банка.
Как подключить Seedance 2.0 API и сделать первый вызов
Войти на сайт и выпустить ключ в кабинете: при первом входе начисляется 50 кредитов, и этот же ключ открывает Seedance 2.0 вместе с остальным каталогом. Собственного ключа ByteDance и договора с ним для вызова Seedance 2.0 не нужно.