Wan 3.0 Node.js example: generate a video with fetch
A short Node.js script that submits a Wan 3.0 job to POST /v1/videos, polls until it completes and saves the MP4, with no SDK. It uses the wan-3.0 model id.

In Node.js 18 or later you can call Wan 3.0 with the built-in fetch: POST to https://api.sume.com/v1/videos with model: "wan-3.0", poll the returned polling_url until the job is completed, then download unsigned_urls[0]. Save the script as an .mjs file and set SUME_API_KEY.
The endpoint, statuses and download flow are from the Video generation docs, read 2026-09-29.
What does the script look like?
import { writeFile } from "node:fs/promises";
const headers = {
Authorization: `Bearer ${process.env.SUME_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "wan-node-001",
};
const submit = await fetch("https://api.sume.com/v1/videos", {
method: "POST",
headers,
body: JSON.stringify({
model: "wan-3.0",
prompt: "A paper boat drifts down a rainy gutter, close camera",
duration: 6,
resolution: "720p",
}),
});
if (!submit.ok) throw new Error(await submit.text());
const { polling_url } = await submit.json();
let job;
do {
await new Promise((r) => setTimeout(r, 30_000));
job = await (await fetch(polling_url, { headers })).json();
} while (["pending", "in_progress"].includes(job.status));
if (job.status !== "completed") throw new Error(JSON.stringify(job.error));
const video = await fetch(job.unsigned_urls[0], { headers });
await writeFile("wan.mp4", Buffer.from(await video.arrayBuffer()));Why send the API key to the download URL?
The unsigned_urls entries point at api.sume.com/v1/videos/{id}/content, which the docs show fetched with the bearer header. Send the key only to that host.
What does each step return?
| Step | Call | You get |
|---|---|---|
| Submit | POST /v1/videos | 202 with id, polling_url, status: pending |
| Poll | GET the polling_url | pending, in_progress, completed, failed or cancelled |
| Download | GET /v1/videos/{jobId}/content?index=0 | The MP4 bytes; unsigned_urls[0] is this URL |
What if a request fails?
A 4xx on submit is a request problem: an unsupported duration, resolution or ratio, or too many references, each with an unsupported_capability or unsupported_parameter code. A job that ends failed carries an error field. Reuse the same Idempotency-Key when you retry a submit so you do not pay for two clips.
Should I poll every 30 seconds?
The docs suggest a reasonable interval such as 30 seconds and say generation can take from 30 seconds to several minutes. For a long clip, use a callback_url instead.
Sources
Related posts
More in Developers
- Wan 3.0 seed and size: why the API returns 400 unsupported_parameter
wan-3.0 on Sume rejects seed and size rather than ignoring them. Use resolution and aspect_ratio instead. Why the 400 happens and how to fix the request.
- Which MCP server lets Claude Code or Cursor generate video and images?
MCP servers that let Claude Code and Cursor make video and images: Sume, fal, Replicate, Runway, Higgsfield. Endpoints, sign-in, billing, setup.
- Idempotency keys for AI video APIs: retry without paying twice
An idempotency key makes a retried create return the original run or job instead of a second paid one. How Sume's Idempotency-Key works on each API.
- Signed webhooks for Sume video runs: events, retries, verification
Sume sends one HMAC-SHA256 signed POST when a Format, Action, or Agent Completion run completes or fails. Verify the raw body and dedupe on request_id.
Written by Sume