bytedance/seedance-2.5/image-to-videoОт 93 кредитов за клип длиной 5 с· Видео из картинкиSeedance 2.5 image-to-video от ByteDance начинает клип с вашего изображения и оживляет его, звук — за тот же проход. Клипы по 4–30 секунд в 480p, 720p или 1080p; необязательный финальный кадр задаёт, на чём закончится план.
Вызывайте его по имени модели bytedance/seedance-2.5/image-to-video через POST /jobs/createTask, от 93 кредита за клип длиной 5 с (0,93 $). Новые аккаунты получают 30 бесплатных кредитов, чтобы вы могли сначала попробовать в Playground.
- Создать видео
- Видео из картинки
- Видео по референсам
- Продлить видео
- До 1080p
- Ролики 4–30 с
- Со звуком
- Первый и последний кадр
- До 30 референсов
- Соотношений сторон: 6
bytedance/seedance-2.5/image-to-videoPOST https://api.popnow.ai/api/v1/jobs/createTask# 1. Create the task. Keep the Idempotency-Key if you retry after a network error.
export IDEMPOTENCY_KEY="$(uuidgen)"
curl -X POST https://api.popnow.ai/api/v1/jobs/createTask \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-d '{
"model": "bytedance/seedance-2.5/image-to-video",
"input": {
"prompt": "A slow aerial shot drifting over a misty pine forest at sunrise",
"image_url": "https://media.popnow.ai/influencer/examples/marco.webp",
"aspect_ratio": "adaptive",
"resolution": "720p",
"duration_seconds": 5,
"generate_audio": true
}
}'
# 2. Copy data.taskId from the response, then poll until state is succeeded, failed, canceled or expired.
TASK_ID="TASK_ID_FROM_RESPONSE"
curl "https://api.popnow.ai/api/v1/jobs/recordInfo?taskId=$TASK_ID" \
-H "Authorization: Bearer $POPNOW_API_KEY"// Node 18+ (or any runtime with fetch). Keep the API key on your server.
const BASE = "https://api.popnow.ai/api/v1";
const headers = { Authorization: `Bearer ${process.env.POPNOW_API_KEY}` };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function call(path, init = {}) {
const res = await fetch(BASE + path, { ...init, headers: { ...headers, ...init.headers } });
const body = await res.json();
if (body.code !== 200) throw new Error(`${body.code} ${body.msg}`);
return body.data;
}
const { taskId } = await call("/jobs/createTask", {
method: "POST",
headers: { "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({
"model": "bytedance/seedance-2.5/image-to-video",
"input": {
"prompt": "A slow aerial shot drifting over a misty pine forest at sunrise",
"image_url": "https://media.popnow.ai/influencer/examples/marco.webp",
"aspect_ratio": "adaptive",
"resolution": "720p",
"duration_seconds": 5,
"generate_audio": true
}
}),
});
// Poll with backoff. A finished task may still be copying files: wait while an asset is pending.
let task;
for (let delay = 2000; ; delay = Math.min(delay * 1.5, 15000)) {
await sleep(delay);
task = await call(`/jobs/recordInfo?taskId=${taskId}`);
const copying = (task.output?.assets ?? []).some((a) => a.pending && !a.unavailable);
if (["succeeded", "failed", "canceled", "expired"].includes(task.state) && !copying) break;
}
if (task.state !== "succeeded") throw new Error(`${task.state}: ${task.errorMessage ?? "no detail"}`);
// Each asset has a signed url (no API key needed) that expires at expiresAt: save the file.
for (const asset of task.output.assets) console.log(asset.url);import os, time, uuid, requests
BASE = "https://api.popnow.ai/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['POPNOW_API_KEY']}"}
def call(method, path, **kwargs):
body = requests.request(method, BASE + path, headers={**HEADERS, **kwargs.pop("headers", {})}, timeout=30, **kwargs).json()
if body["code"] != 200:
raise RuntimeError(f'{body["code"]} {body["msg"]}')
return body["data"]
task_id = call(
"POST",
"/jobs/createTask",
headers={"Idempotency-Key": str(uuid.uuid4())}, # reuse it if you retry
json={
"model": "bytedance/seedance-2.5/image-to-video",
"input": {
"prompt": "A slow aerial shot drifting over a misty pine forest at sunrise",
"image_url": "https://media.popnow.ai/influencer/examples/marco.webp",
"aspect_ratio": "adaptive",
"resolution": "720p",
"duration_seconds": 5,
"generate_audio": True
}
},
)["taskId"]
# Poll with backoff. A finished task may still be copying files: wait while an asset is pending.
delay = 2.0
while True:
time.sleep(delay)
task = call("GET", "/jobs/recordInfo", params={"taskId": task_id})
assets = (task.get("output") or {}).get("assets") or []
copying = any(a.get("pending") and not a.get("unavailable") for a in assets)
if task["state"] in ("succeeded", "failed", "canceled", "expired") and not copying:
break
delay = min(delay * 1.5, 15.0)
if task["state"] != "succeeded":
raise RuntimeError(f'{task["state"]}: {task.get("errorMessage", "no detail")}')
# Each asset has a signed url (no API key needed) that expires at expiresAt: save the file.
for asset in assets:
print(asset["url"])input собран по схеме ниже, поэтому проходит проверку как есть. Замените промпт и ссылки на свои.
Цена
| Вариант | Кредиты за секунду | ≈ USD |
|---|---|---|
resolution 480p | 18,51 | 0,19 $ |
resolution 720p | 41,6 | 0,42 $ |
resolution 1080p | 102,35 | 1,02 $ |
1 кредит = 0,01 $ · Все варианты и цены: GET /models/bytedance/seedance-2.5/image-to-video
Каждая задача округляется вверх до целых кредитов, поэтому один запуск на самом дешёвом уровне стоит 93 кредита за клип длиной 5 с (0,93 $).
Ограничения
- Длина клипа
- 4–30 s
- Разрешения
- 480p · 720p · 1080p
- Соотношения сторон
- adaptive
Параметры
Эти поля взяты из той же схемы, по которой мы проверяем каждый запрос. Поля, которых нет в списке, отклоняются.
image_urlstringОбязательноеКартинка, с которой всё начинается.
promptstringНеобязательноеWhat to generate: subject, action, camera movement and mood.
last_image_urlstringНеобязательноеНеобязательный последний кадр, чтобы ролик закончился там, где нужно.
aspect_ratiostringНеобязательноеФорма кадра: широкая, квадратная или вертикальная.
resolutionstringНеобязательноеВлияет на ценуРазмер результата. Больше — чётче и дороже.
duration_secondsintegerНеобязательноеВлияет на ценуДлина ролика. Оплата посекундная.
generate_audiobooleanНеобязательноеВлияет на ценуДобавляет звук под картинку: голоса, атмосферу и эффекты.
output_formatstringНеобязательноеФормат файла результата.
enable_web_searchbooleanНеобязательноеСначала проверяет в интернете даты, места и свежие факты.
seedintegerНеобязательноеОдно и то же число даёт повторяемый результат.
return_last_framebooleanНеобязательноеAlso return the clip’s final frame as a separate image.
watermarkbooleanНеобязательноеAsk the model to watermark the video as AI-generated.
Исходная JSON-схема
{
"additionalProperties": false,
"properties": {
"aspect_ratio": {
"default": "adaptive",
"description": "Aspect ratio of the generated video. Seedance 2.5 frames an image-to-video clip on the first frame, so adaptive is the only accepted value.",
"enum": [
"adaptive"
],
"type": "string"
},
"duration_seconds": {
"default": 5,
"description": "Clip length in seconds. This is the billed quantity.",
"enum": [
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30
],
"type": "integer"
},
"enable_web_search": {
"default": false,
"description": "Let the model search the web for information related to the prompt before rendering.",
"type": "boolean"
},
"generate_audio": {
"default": true,
"description": "Generate a native soundtrack synchronised to the picture.",
"type": "boolean"
},
"image_url": {
"contentMediaType": [
"image/jpeg",
"image/png",
"image/webp",
"image/gif"
],
"description": "URL of the image used as the first frame.",
"type": "string"
},
"last_image_url": {
"contentMediaType": [
"image/jpeg",
"image/png",
"image/webp",
"image/gif"
],
"description": "URL of the image used as the closing frame. Only accepted together with image_url.",
"type": "string"
},
"output_format": {
"default": "mp4",
"description": "Container of the returned video.",
"enum": [
"mp4",
"mov"
],
"type": "string"
},
"prompt": {
"description": "What to generate: subject, action, camera movement and mood.",
"type": "string"
},
"resolution": {
"default": "720p",
"description": "Output resolution. Super-resolution tiers are not offered.",
"enum": [
"480p",
"720p",
"1080p"
],
"type": "string"
},
"return_last_frame": {
"default": false,
"description": "Also return the clip’s final frame as a separate image.",
"type": "boolean"
},
"seed": {
"description": "Random seed. Omit to let the model pick one.",
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
},
"watermark": {
"default": false,
"description": "Ask the model to watermark the video as AI-generated.",
"type": "boolean"
}
},
"required": [
"image_url"
],
"type": "object"
}Настоящий результат Seedance 2.5
Time is frozen: the rain and the wall of shattered glass hang dead still for a beat. Then the camera charges in fast and whips around her as she closes her raised hand into a fist; the suspended shards snap into a spiralling orbit around her, the frozen raindrops streak into long lines, and the whole shockwave blasts outward past the lens as the rain crashes down all at once. Her gown and hair whip in the blast. Extreme camera move, anamorphic flares, cold blue-white grade, shattering glass and a deep low boom.
{
"model": "bytedance/seedance-2.5/image-to-video",
"input": {
"prompt": "Time is frozen: the rain and the wall of shattered glass hang dead still for a beat. Then the camera charges in fast and whips around her as she closes her raised hand into a fist; the suspended shards snap into a spiralling orbit around her, the frozen raindrops streak into long lines, and the whole shockwave blasts outward past the lens as the rain crashes down all at once. Her gown and hair whip in the blast. Extreme camera move, anamorphic flares, cold blue-white grade, shattering glass and a deep low boom.",
"image_url": "https://media.popnow.ai/influencer/examples/marco.webp",
"aspect_ratio": "adaptive",
"resolution": "720p",
"duration_seconds": 5,
"generate_audio": true
}
}Этот результат начался с загруженного файла: подставьте свою ссылку на место ссылки из примера.
Ответы, опрос и колбэки
Все модели работают одинаково: создайте задачу, а затем опрашивайте её или дождитесь колбэка.
Сначала узнайте цену
curl -X POST https://api.popnow.ai/api/v1/jobs/quote \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "bytedance/seedance-2.5/image-to-video",
"input": {
"prompt": "A slow aerial shot drifting over a misty pine forest at sunrise",
"image_url": "https://media.popnow.ai/influencer/examples/marco.webp",
"aspect_ratio": "adaptive",
"resolution": "720p",
"duration_seconds": 5,
"generate_audio": true
}
}'То же тело, что и у createTask. Возвращает ожидаемое и максимально возможное списание и ничего не запускает.
Принято
HTTP/1.1 202 Accepted
{
"code": 200,
"msg": "success",
"data": {
"taskId": "task_01k3m8x9q2z4v7n5p6r8s0t1w3",
"state": "queued"
},
"request_id": "req_example_create"
}HTTP 202 означает, что задача принята, а кредиты зарезервированы, но ещё не списаны. Итоговое списание никогда не превысит резерв.
Проверка статуса
curl "https://api.popnow.ai/api/v1/jobs/recordInfo?taskId=$TASK_ID" \
-H "Authorization: Bearer $POPNOW_API_KEY"Опрашивайте каждые несколько секунд, постепенно увеличивая паузу, и остановитесь, когда state станет succeeded, failed, canceled или expired.
Готовая задача
{
"code": 200,
"msg": "success",
"data": {
"taskId": "task_01k3m8x9q2z4v7n5p6r8s0t1w3",
"model": "bytedance/seedance-2.5/image-to-video",
"state": "succeeded",
"output": {
"assets": [
{
"url": "https://cdn.popnow.ai/tasks/task_01k3m8x9q2z4v7n5p6r8s0t1w3/output.mp4?signature=FROM_RESPONSE",
"expiresAt": "2026-10-17T12:20:00Z",
"mime": "video/mp4",
"width": 1280,
"height": 720,
"durationSeconds": 5
}
]
},
"settled": true
},
"request_id": "req_example_record"
}Скачайте файл по url до expiresAt. Нужна новая ссылка позже? Запросите её через POST /common/download-url.
Проверка колбэка
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
import express from "express";
const app = express();
// express.raw keeps the exact bytes that were signed; express.json would not match.
app.post("/hooks/popnow", express.raw({ type: "application/json" }), (req, res) => {
const timestamp = req.header("X-Webhook-Timestamp");
const signature = req.header("X-Webhook-Signature") ?? "";
const body = JSON.parse(req.body.toString("utf8"));
const taskId = body.data?.taskId ?? body.task_id;
const digest = createHash("sha256").update(req.body).digest("hex");
const expected = createHmac("sha256", process.env.POPNOW_WEBHOOK_SECRET)
.update(`${taskId}.${timestamp}.${digest}`)
.digest("base64");
const ok = expected.length === signature.length && timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
// Check the signature first, then reject anything older than five minutes.
if (!ok || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return res.status(401).end();
res.status(200).end(); // answer fast, then fetch the task and store its files
});Если задан callBackUrl, мы пришлём вам готовую задачу. Прежде чем доверять ей, проверьте подпись по исходному телу запроса.
Отправка локального файла
curl -X POST https://api.popnow.ai/api/v1/common/upload-url \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"contentType": "image/png", "bytes": 482113}'
# PUT the file bytes to data.uploadUrl with every header in data.headers, then commit it:
curl -X POST https://api.popnow.ai/api/v1/files/$FILE_ID/commit \
-H "Authorization: Bearer $POPNOW_API_KEY"
# The commit returns a popnow://f/... reference. Put it in any image, video or
# audio field of the task input instead of a public https URL.Публичные https-ссылки работают как есть. Локальные файлы сначала проходят через upload-url и commit.

