alibaba/wan-2.6/image-to-videoOd 68 kredytów za klip 5-sekundowy· Obraz w wideoWan 2.6 image-to-video od Alibaba animuje jedną klatkę początkową w dokładnie 5, 10 lub 15 sekund z generowanym dźwiękiem, w 720p lub 1080p. Wybierz jedno ujęcie lub montaż wielu ujęć i dodaj audio, by wyznaczyć tempo.
Wywołujesz go nazwą modelu alibaba/wan-2.6/image-to-video w POST /jobs/createTask, od 68 kredytów za klip 5-sekundowy (0,68 USD). Nowe konta dostają 30 darmowych kredytów, więc możesz najpierw przetestować go w Playgroundzie.
- Utwórz wideo
- Obraz w wideo
- Wideo z referencji
- Do 1080p
- Klipy 2–15 s
- Do 2 obrazów referencyjnych
- Proporcje obrazu: 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 jest zbudowany ze schematu poniżej, więc przechodzi walidację bez zmian. Zamień prompt i linki na własne.
Cena
| Opcja | Kredyty za sekundę | ≈ USD |
|---|---|---|
resolution 720p | 13,5 | 0,14 USD |
resolution 1080p | 20,25 | 0,20 USD |
1 kredyt = 0,01 USD · Wszystkie opcje i ceny: GET /models/alibaba/wan-2.6/image-to-video
Każde zadanie jest zaokrąglane w górę do pełnych kredytów, więc jedno uruchomienie na najniższym poziomie kosztuje 68 kredytów za klip 5-sekundowy (0,68 USD).
Limity
- Długość klipu
- 5–15 s
- Rozdzielczości
- 720p · 1080p
- Długość promptu (znaki)
- 1,500
Parametry
Te pola pochodzą z tego samego schematu, którym sprawdzamy każde żądanie. Pola spoza listy są odrzucane.
image_urlstringWymaganeObraz, od którego zaczynasz.
promptstringOpcjonalneDescribe what happens after the opening frame: the action, the camera, and the mood.
negative_promptstringOpcjonalneCzego nie chcesz widzieć.
resolutionstringOpcjonalneZmienia cenęRozmiar wyniku. Większy jest ostrzejszy i droższy.
duration_secondsintegerOpcjonalneZmienia cenęDługość klipu. Płacisz za każdą sekundę.
audio_urlstringOpcjonalneGłos lub muzyka do użycia.
shot_typestringOpcjonalneSingle continuous shot, or a multi-shot edit.
enable_prompt_expansionbooleanOpcjonalneModel dopisuje szczegóły do krótkiego promptu.
seedintegerOpcjonalneTa sama liczba daje powtarzalny wynik.
Surowy JSON Schema
{
"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"
}Prawdziwy wynik z 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."
}
}Ten wynik powstał z przesłanego pliku: wstaw własny link w miejsce przykładowego.
Odpowiedzi, odpytywanie i callbacki
Wszystkie modele działają tak samo: utwórz zadanie, a potem odpytuj je albo poczekaj na callback.
Najpierw cena
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"
}
}'To samo ciało co createTask. Zwraca przewidywaną i maksymalną możliwą opłatę, niczego nie uruchamiając.
Przyjęte
HTTP/1.1 202 Accepted
{
"code": 200,
"msg": "success",
"data": {
"taskId": "task_01k3m8x9q2z4v7n5p6r8s0t1w3",
"state": "queued"
},
"request_id": "req_example_create"
}HTTP 202 oznacza, że zadanie przyjęto, a kredyty są zarezerwowane, jeszcze nie pobrane. Ostateczna opłata nigdy nie przekroczy rezerwacji.
Sprawdź status
curl "https://api.popnow.ai/api/v1/jobs/recordInfo?taskId=$TASK_ID" \
-H "Authorization: Bearer $POPNOW_API_KEY"Odpytuj co kilka sekund, stopniowo wydłużając odstęp, i przestań, gdy state ma wartość succeeded, failed, canceled lub expired.
Gotowe zadanie
{
"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"
}Pobierz plik z url przed expiresAt. Potrzebujesz później nowego linku? Poproś o niego przez POST /common/download-url.
Weryfikacja callbacku
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
});Gdy ustawisz callBackUrl, wyślemy ci gotowe zadanie. Zanim mu zaufasz, sprawdź podpis na surowym ciele żądania.
Wysyłanie pliku lokalnego
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.Publiczne linki https działają bez zmian. Pliki lokalne przechodzą najpierw przez upload-url i commit.

