alibaba/wan-2.6/image-to-videoОт 68 кредитов за клип длиной 5 с· Видео из картинкиWan 2.6 image-to-video от Alibaba оживляет первый кадр ровно в 5, 10 или 15 секунд со звуком в 720p или 1080p. Выберите один дубль или монтаж из нескольких планов; аудиодорожка задаёт тайминг.
Вызывайте его по имени модели alibaba/wan-2.6/image-to-video через POST /jobs/createTask, от 68 кредитов за клип длиной 5 с (0,68 $). Новые аккаунты получают 30 бесплатных кредитов, чтобы вы могли сначала попробовать в Playground.
- Создать видео
- Видео из картинки
- Видео по референсам
- До 1080p
- Ролики 2–15 с
- До 2 референсов
- Соотношений сторон: 5
alibaba/wan-2.6/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": "alibaba/wan-2.6/image-to-video",
"input": {
"image_url": "https://media.popnow.ai/influencer/examples/marco.webp",
"resolution": "720p",
"duration_seconds": 5,
"shot_type": "single"
}
}'
# 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": "alibaba/wan-2.6/image-to-video",
"input": {
"image_url": "https://media.popnow.ai/influencer/examples/marco.webp",
"resolution": "720p",
"duration_seconds": 5,
"shot_type": "single"
}
}),
});
// 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": "alibaba/wan-2.6/image-to-video",
"input": {
"image_url": "https://media.popnow.ai/influencer/examples/marco.webp",
"resolution": "720p",
"duration_seconds": 5,
"shot_type": "single"
}
},
)["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 720p | 13,5 | 0,14 $ |
resolution 1080p | 20,25 | 0,20 $ |
1 кредит = 0,01 $ · Все варианты и цены: GET /models/alibaba/wan-2.6/image-to-video
Каждая задача округляется вверх до целых кредитов, поэтому один запуск на самом дешёвом уровне стоит 68 кредитов за клип длиной 5 с (0,68 $).
Ограничения
- Длина клипа
- 5–15 s
- Разрешения
- 720p · 1080p
- Длина промпта (символов)
- 1,500
Параметры
Эти поля взяты из той же схемы, по которой мы проверяем каждый запрос. Поля, которых нет в списке, отклоняются.
image_urlstringОбязательноеКартинка, с которой всё начинается.
promptstringНеобязательноеDescribe what happens after the opening frame: the action, the camera, and the mood.
negative_promptstringНеобязательноеЧего не должно быть в кадре.
resolutionstringНеобязательноеВлияет на ценуРазмер результата. Больше — чётче и дороже.
duration_secondsintegerНеобязательноеВлияет на ценуДлина ролика. Оплата посекундная.
audio_urlstringНеобязательноеГолос или музыка, которые будут использованы.
shot_typestringНеобязательноеSingle continuous shot, or a multi-shot edit.
enable_prompt_expansionbooleanНеобязательноеМодель дописывает детали к короткому промпту.
seedintegerНеобязательноеОдно и то же число даёт повторяемый результат.
Исходная JSON-схема
{
"additionalProperties": false,
"properties": {
"audio_url": {
"contentMediaType": [
"audio/mpeg",
"audio/wav"
],
"description": "Audio track that drives lip sync or pacing.",
"type": "string"
},
"duration_seconds": {
"default": 5,
"description": "Requested output length in seconds.",
"enum": [
5,
10,
15
],
"type": "integer"
},
"enable_prompt_expansion": {
"default": false,
"description": "Allow the service to expand the submitted creative brief before generation.",
"type": "boolean"
},
"image_url": {
"contentMediaType": [
"image/jpeg",
"image/png",
"image/webp",
"image/gif"
],
"description": "Opening frame. The clip starts from this image.",
"type": "string"
},
"negative_prompt": {
"description": "List what must not appear in the shot.",
"maxLength": 500,
"type": "string"
},
"prompt": {
"description": "Describe what happens after the opening frame: the action, the camera, and the mood.",
"maxLength": 1500,
"minLength": 1,
"type": "string"
},
"resolution": {
"default": "720p",
"description": "Output resolution.",
"enum": [
"720p",
"1080p"
],
"type": "string"
},
"seed": {
"description": "Random seed. Keep it fixed only for controlled comparisons, not as a frame-identical guarantee.",
"maximum": 2147483647,
"minimum": 0,
"type": "integer"
},
"shot_type": {
"default": "single",
"description": "Single continuous shot, or a multi-shot edit.",
"enum": [
"single",
"multi"
],
"type": "string"
}
},
"required": [
"image_url"
],
"type": "object"
}Настоящий результат Wan 2.6
The camera stays on the two of them through all three shots. Three shots cut from the one kitchen frame. 1. Close: the knife going through, her forearms dusted with flour. 2. Medium: the pan catches flame and lights both their faces. 3. Close: he comes up behind her and reaches past her waist for the pan, her head tipping back against his shoulder, both shirts soaked through with steam. Cinematic, shallow depth of field, heavy film grain, kitchen steel and gas blue.
{
"model": "alibaba/wan-2.6/image-to-video",
"input": {
"image_url": "https://media.popnow.ai/influencer/examples/marco.webp",
"resolution": "1080p",
"duration_seconds": 10,
"shot_type": "single",
"prompt": "The camera stays on the two of them through all three shots. Three shots cut from the one kitchen frame. 1. Close: the knife going through, her forearms dusted with flour. 2. Medium: the pan catches flame and lights both their faces. 3. Close: he comes up behind her and reaches past her waist for the pan, her head tipping back against his shoulder, both shirts soaked through with steam. Cinematic, shallow depth of field, heavy film grain, kitchen steel and gas blue."
}
}Этот результат начался с загруженного файла: подставьте свою ссылку на место ссылки из примера.
Ответы, опрос и колбэки
Все модели работают одинаково: создайте задачу, а затем опрашивайте её или дождитесь колбэка.
Сначала узнайте цену
curl -X POST https://api.popnow.ai/api/v1/jobs/quote \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "alibaba/wan-2.6/image-to-video",
"input": {
"image_url": "https://media.popnow.ai/influencer/examples/marco.webp",
"resolution": "720p",
"duration_seconds": 5,
"shot_type": "single"
}
}'То же тело, что и у 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": "alibaba/wan-2.6/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.

