Skip to main content

Wan 2.6 Flash reference-to-video from Alibaba casts people, animals or objects from up to five stills and clips into a new scene described by your prompt, on the faster, lower-cost Flash tier. Clips run 2 to 10 seconds at 720p or 1080p, silent by default or with a generated soundtrack.

From $0.022/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-2.6-flash/reference-to-video",
  "input": {
    "aspect_ratio": "16:9",
    "duration_seconds": 5,
    "generate_audio": false,
    "prompt": "A slow aerial shot drifting over a misty pine forest at sunrise",
    "resolution": "720p",
    "shot_type": "single",
    "reference_video_urls": [
      "https://example.com/reference.mp4"
    ]
  }
}'

# 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.

promptstringRequired

The new scene: name each reference as character1, character2 and so on, clips first and then stills, and say what each one does, then the setting, camera and light. Up to 1,500 characters. With generate_audio on, describe the sounds you want after the action.

Default
Not specified
Constraints
minLength:1
maxLength:1500
seedintegerOptional

0 to 2147483647. Hold it while swapping one reference at a time so the random draw stays constant and the thing you changed is the only thing moving. It is a comparison tool, not a guarantee of a frame-identical rerun.

Default
Not specified
Constraints
minimum:0
maximum:2147483647
enable_prompt_expansionbooleanOptional

Off by default. Turning it on lets a one-line idea grow into a fuller brief before generation; leave it off when your prompt already names the cast and the shot precisely. With shot_type multi it is switched on regardless of this setting.

Default
false
shot_typestringOptional

single keeps one continuous take; multi lets the model cut between shots within the same scene, which suits a scene with a beat to it. On 2 to 3 seconds there is rarely room for a cut. Choosing multi also switches enable_prompt_expansion on for that request.

Default
"single"
Allowed values
"single""multi"
duration_secondsintegerOptionalAffects price

Any whole number from 2 to 10, default 5. It is the main driver of the price, and also where likeness drifts: check the last second of a long clip before committing to that length.

Default
5
Constraints
minimum:2
maximum:10
resolutionstringOptionalAffects price

720p or 1080p, default 720p. It sets the per-second rate and how long the task takes, and both resolutions accept all five aspect ratios.

Default
"720p"
Allowed values
"720p""1080p"
generate_audiobooleanOptionalAffects price

Off by default, so the clip comes back as picture only. On, the clip comes back with a generated soundtrack of voices, effects and ambience, and the per-second rate doubles at either resolution.

Default
false
aspect_ratiostringOptional

16:9, 9:16, 1:1, 4:3 or 3:4, default 16:9, available at both resolutions. It is chosen per request rather than inherited from a reference, so one set of references can be delivered in several shapes.

Default
"16:9"
Allowed values
"16:9""9:16""1:1""4:3""3:4"
reference_image_urlsarray<string>Conditionally required

Up to five still images of the people, animals or objects to cast, alongside the clips or instead of them; with the clips a request takes five references at most. They are numbered after the clips: with no clips the first image is character1, with two clips it is character3. JPEG, PNG without transparency, BMP or WEBP, 240 to 8,000 pixels on each side and up to 20 MB. Stills are not billed.

Default
Not specified
Constraints
minItems:1
maxItems:5
reference_video_urlsarray<string>Conditionally requiredAffects price

Up to three short clips of the subjects or places to cast, adding up to five seconds at most. They are numbered first, as character1, character2 and character3 in upload order. A clip carries motion and place better than a still; its seconds are billed with the output, rounded up and at least one second each, so trim before uploading. MP4 or MOV.

Default
Not specified
Constraints
minItems:1
maxItems:3
negative_promptstringOptional

What must not appear: extra people, captions, a prop from a reference you do not want carried over. Up to 500 characters.

Default
Not specified
Constraints
maxLength:500

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-2.6-flash/reference-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 2.6 Flash · Reference 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