Skip to main content

ByteDance's Seedance 2.5 image-to-video starts the clip on a still you supply and animates it, with sound in the same pass. Clips run 4 to 30 seconds at 480p, 720p or 1080p, and an optional closing still makes the shot arrive on a set frame.

From $0.123/s

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": "bytedance/seedance-2.5/image-to-video",
  "input": {
    "aspect_ratio": "adaptive",
    "duration_seconds": 5,
    "generate_audio": true,
    "image_url": "https://example.com/reference.png",
    "output_format": "mp4",
    "prompt": "A slow aerial shot drifting over a misty pine forest at sunrise"
  }
}'

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

image_urlstringRequired

Required, and it becomes the first frame of the video. Composition, likeness and colour all come from it, which also fixes the quality ceiling of the result: getting this still right beats patching the output with prompt wording afterwards. Use a sharp frame with a clear subject and no obvious compression artefacts. Seedance's own input check may turn away a photo that shows a real, identifiable person's face. Each side must be between 300 and 6000 pixels; a smaller plate is rejected by the input check before you are charged.

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

Asks the model to watermark the video as AI-generated, as described in the model’s own documentation. Off by default; switch it on when the place you publish requires AI-made footage to be labelled. No effect on price.

Default
false
return_last_framebooleanOptional

Also returns the clip’s final frame as a separate image next to the video. Send it to image-to-video as the next opening frame to chain shots without exporting a still yourself. Off by default; no effect on price.

Default
false
seedintegerOptional

Integer seed; omit it and each run draws its own. Fix it with everything else held constant to judge what a prompt edit actually changed. It is a comparison tool, not a promise of frame-identical repeats.

Default
Not specified
Constraints
minimum:0
maximum:2147483647
enable_web_searchbooleanOptional

Off by default. Switched on, the model may search the web for information related to your prompt before it renders. It does not change the price.

Default
false
output_formatstringOptional

The container of the returned video: mp4 (the default) or mov. Both carry the same picture; mov keeps the soundtrack as uncompressed PCM, so the file is a little larger and drops straight into editing software that prefers QuickTime. Does not change the price.

Default
"mp4"
Allowed values
"mp4""mov"
generate_audiobooleanOptional

On by default. Produces a native soundtrack synchronized to the picture — room tone, movement effects, voice. Turn it off for product pieces that will be scored in post; leave it on when footsteps and ambience are part of the point.

Default
true
duration_secondsintegerOptionalAffects price

4 to 30 seconds, default 5. The longer a frame-driven shot runs, the more it drifts — anatomy and set dressing deform gradually. Confirm the direction of the motion at 5 seconds, then extend. For genuinely long sequences, several shorter takes cut together usually hold better than one 30-second take. Length scales the billed quantity linearly.

Default
5
Allowed values
456789101112131415161718192021222324252627282930
resolutionstringOptionalAffects price

480p, 720p or 1080p, default 720p, and it feeds the billed quantity directly. Note that the output tier is independent of the source still: a soft low-resolution frame rendered at 1080p is just enlarged softness. The tier you pick is the render tier, with no upscaling or enhancement layered on top.

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

Only `adaptive` is accepted here: Seedance 2.5 always frames an image-to-video clip on the geometry of the opening frame, and a fixed shape would make the task fail. If the delivery format is fixed, crop or pad the still to it before you send it. No effect on billing.

Default
"adaptive"
Allowed values
"adaptive"
last_image_urlstringOptional

Optional destination frame that **cannot be sent on its own** — it is only valid together with `image_url`. Supply both and the model choreographs the passage from one to the other, which suits transformations, unboxings and before-and-after shots. Leave it out and the prompt decides the ending. Stills that differ wildly in composition or light make the middle of the transition unpredictable. Each side must be between 300 and 6000 pixels; a smaller plate is rejected by the input check before you are charged.

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

Optional here, but worth writing: it should describe what happens after the frame rather than restate what is already visible — the action, the camera move, the passage of time, the sound. Left out, the model decides the motion on its own. Because the picture is already fixed by the still, this is the path that survives uncensored briefs — wording that Seedance's safety layer refuses from a blank page will usually run when the frame carries the content.

Default
Not specified

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": "bytedance/seedance-2.5/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 Seedance 2.5 · 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