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.

4 min readSume
All posts

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?

From Video generation, read 2026-09-29.
StepCallYou get
SubmitPOST /v1/videos202 with id, polling_url, status: pending
PollGET the polling_urlpending, in_progress, completed, failed or cancelled
DownloadGET /v1/videos/{jobId}/content?index=0The 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

All Developers posts

Written by Sume