Skip to main content

Alibaba's Wan 3.0 image-to-video starts from an opening frame and directs 2 to 30 seconds of motion with generated audio. An optional closing frame pins where the shot lands, at 480p, 720p or 1080p.

From $0.045/s10% below the publisher's list price

Workbench

Send a request

Quickstart

Examples use the selected endpoint’s public parameter definitions. Replace the API key and example media, then send the request from your server.

SDKs & integrations
POSThttps://api.spicyapi.ai/api/v1/jobs/createTask

API authentication

Keep the API key in the server environment variable SPICY_API_KEY. Use Bearer authentication and Content-Type: application/json for JSON requests.

Authorization: Bearer $SPICY_API_KEY
Sample outputUse this example
# Generate the idempotency key once; reuse it when retrying after a lost response.
export SPICY_IDEMPOTENCY_KEY="${SPICY_IDEMPOTENCY_KEY:-$(uuidgen)}"
curl -X POST https://api.spicyapi.ai/api/v1/jobs/createTask \
  -H "Authorization: Bearer $SPICY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $SPICY_IDEMPOTENCY_KEY" \
  -d '{
  "model": "alibaba/wan-3.0/image-to-video",
  "input": {
    "duration_seconds": 5,
    "image_url": "https://example.com/reference.png",
    "prompt": "A slow aerial shot drifting over a misty pine forest at sunrise",
    "resolution": "720p"
  }
}'

# Copy data.taskId from the response into TASK_ID, then poll the status.
TASK_ID="TASK_ID_FROM_RESPONSE"
curl "https://api.spicyapi.ai/api/v1/jobs/recordInfo?taskId=$TASK_ID" \
  -H "Authorization: Bearer $SPICY_API_KEY"

Generated from this model’s input schema — the same one the API validates every request against.

Input parameters

Place these fields inside the request body’s input object. Names and types match the API. Do not assume a default when none is specified.

This model has dependencies between fields. Fields marked conditionally required depend on other inputs; see the complete JSON Schema below for the rules.

image_urlstringRequired

The opening frame, used exactly as the first frame, and the real quality ceiling of the shot. Wardrobe, body, set and light direction are inherited from it, so the prompt only has to carry motion. JPEG, PNG without transparency, BMP or WEBP; 240 to 8000 px per side, aspect ratio no wider than 8:1, 20 MB at most.

Default
Not specified
Constraints
contentMediaType:["image/jpeg","image/png","image/webp","image/gif"]
seedintegerOptional

0 to 2147483647. Hold it steady to compare one changed field at a time; drop it for variety.

Default
Not specified
Constraints
minimum:0
maximum:2147483647
enable_prompt_expansionbooleanOptional

Off by default. When on, the prompt is rewritten before generation, which helps thin briefs and fights you on detailed ones.

Default
false
generate_audiobooleanOptional

On by default. The clip comes back with sound already locked to the picture. Set false when you are scoring the shot yourself.

Default
true
duration_secondsintegerOptionalAffects price

Any whole number from 2 to 30, or -1 for smart duration, where the model picks the length: the 30-second maximum is held up front and the charge settles on the length actually delivered. Defaults to 5. Longer clips drift more, so settle the look at 5 seconds before extending.

Default
5
Allowed values
-123456789101112131415161718192021222324252627282930
resolutionstringOptionalAffects price

480p, 720p or 1080p. Defaults to 720p. Resolution changes both the price and how long the task takes to come back.

Default
"720p"
Allowed values
"480p""720p""1080p"
enable_thinkingbooleanOptional

Has the model think through the brief and any reference material before generating. It helps with dense documents, web pages and complex reference images, and makes the run slower. Off by default; it switches on automatically when you pass a reference file or link. It does not change the price.

Default
false
aspect_ratiostringOptional

16:9, 9:16, 1:1, 4:3, 3:4 or adaptive, which lets the model choose. Leave it out and the clip inherits the opening frame's shape; a conflicting value crops.

Default
Not specified
Allowed values
"16:9""9:16""1:1""4:3""3:4""adaptive"
last_image_urlstringOptional

Optional, and only together with image_url. The clip ends exactly on this image — it is used as the last frame, not as loose guidance — so supply it when the shot has to land on a fixed composition, such as a product back on its hero angle. Same file limits as image_url.

Default
Not specified
Constraints
contentMediaType:["image/jpeg","image/png","image/webp","image/gif"]
promptstringOptional

Optional: without it the model animates the opening frame on its own. When you write one, describe the shot: subject, one action, one camera move, then light and sound. Long lists of adjectives move the result less than one clear action does.

Default
Not specified
Constraints
minLength:1

Track the task

HTTP 202 means the task was accepted, not completed. Save the returned taskId and query that task. Do not create another task while waiting for its result.

Accepted response
HTTP/1.1 202 Accepted
Location: /api/v1/jobs/recordInfo?taskId=job_01k3m8x9q2z4v7n5p6r8s0t1w3

{
  "code": 200,
  "msg": "success",
  "request_id": "req_example_create",
  "data": {
    "taskId": "job_01k3m8x9q2z4v7n5p6r8s0t1w3",
    "state": "queued",
    "estimatedCost": "0.750000"
  }
}

The accepted response includes the estimate so your app can show the same amount in task history and billing.

Read the job
curl --get "https://api.spicyapi.ai/api/v1/jobs/recordInfo" \
  --data-urlencode "taskId=$TASK_ID" \
  -H "Authorization: Bearer $SPICY_API_KEY"

Start polling at about 2 seconds and gradually increase the interval. Stop at a terminal state. After a connection failure, query the original taskId. Retry creation only with the original idempotency key and body.

Or let the callback find you
import { verifyWebhook } from "@spicyapi/sdk";

// express.raw keeps the bytes we signed; express.json never matches.
app.post("/hooks/spicy", express.raw({ type: "application/json" }), (req, res) => {
  const { payload } = verifyWebhook({
    rawBody: req.body,
    timestamp: req.header("X-Webhook-Timestamp"),
    signature: req.header("X-Webhook-Signature"),
    payloadVersion: Number(req.header("X-Webhook-Payload-Version")),
    secret: process.env.SPICY_WEBHOOK_SECRET,
  });
  res.status(200).end();
  void handleFinishedTask(payload);
});

Pass callBackUrl when you create the task and the finished record is POSTed to you — no polling loop at all. Every delivery carries X-Webhook-Timestamp, X-Webhook-Signature and X-Webhook-Payload-Version. The signature is a base64 HMAC-SHA256 over taskId.timestamp.sha256(body), so it must be checked against the raw bytes: parsing the JSON and re-serialising it changes them and the check will fail. Failed deliveries are retried, so the same taskId can arrive twice — make the handler idempotent. Answer 2xx first and do the work afterwards; a slow handler is treated as a failed delivery.

Read the result

Consume output only when state is succeeded. On failure, use errorCode, errorMessage and request_id to investigate. Example costs and states do not represent an actual bill.

Completed response example
{
  "code": 200,
  "msg": "success",
  "request_id": "req_example_record",
  "data": {
    "taskId": "job_01k3m8x9q2z4v7n5p6r8s0t1w3",
    "model": "alibaba/wan-3.0/image-to-video",
    "state": "succeeded",
    "output": {
      "assets": [
        {
          "key": "tasks/2026/08/28/job_01k3m8x9q2z4v7n5p6r8s0t1w3/3f7a1c9e5b2d48f0a6c1e3d5b7f9a1c3.mp4",
          "mime": "video/mp4",
          "url": "https://example.r2.cloudflarestorage.com/results/job_01k3m8x9q2z4v7n5p6r8s0t1w3/result.mp4?X-Amz-Signature=SIGNATURE_FROM_RESPONSE",
          "expiresAt": "2026-08-28T12:20:08.000Z"
        }
      ]
    },
    "cost": "0.750000",
    "settled": true,
    "createdAt": "2026-08-28T12:00:00Z",
    "completedAt": "2026-08-28T12:00:30Z"
  }
}

For each available asset, open or download assets[].url directly without an API Authorization header. The signed URL expires at expiresAt; poll the task again to refresh it while the asset remains within its retention period. A pending or unavailable asset has no URL. Treat signed URLs as private.

SDKs & integrations

Use SpicyAPI in your app, terminal, AI assistant or visual workflow. Choose a setup guide below.

Use it in ComfyUI

Install the SpicyAPI nodes from ComfyUI Manager or with the command below, then search for Wan 3.0 · Image to Video on the canvas. The node has the same options as this page and shows its price before it runs.

cd ComfyUI/custom_nodes
git clone https://github.com/spicyapi-ai/comfyui-spicyapi