Skip to main content

Seedance 1.5 Pro image-to-video from ByteDance starts the clip on a still you supply and animates it. Clips run 4 to 12 seconds at 480p, 720p or 1080p with native audio, and a camera lock keeps the framing exactly as composed.

From $0.011/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-1.5-pro/image-to-video",
  "input": {
    "aspect_ratio": "16:9",
    "duration_seconds": 5,
    "generate_audio": true,
    "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.

image_urlstringRequired

Public, directly downloadable link to the first frame; required. It fixes composition, how the subject looks, the grade, and the quality ceiling of the clip — a soft or heavily compressed source shows up magnified in motion. Text, watermarks, and logos in the still travel into the video, so clean them off first where you can. Keep the file under 10 MB, as JPEG, PNG or WebP.

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

Required, and it should say what happens after the frame rather than restate what is already visible: the action, the passage of time, the sound. With camera_fixed true, keep every described movement inside the frame — a camera instruction and a locked camera pull against each other. Because the still already fixes the picture, this is also the path that survives uncensored briefs.

Default
Not specified
Constraints
minLength:3
seedintegerOptional

Integer from 0 to 2147483647 for the random start; leave it out and every run draws its own. Hold it fixed while you change one thing at a time to see what that change did — it keeps the starting noise the same, not the clip frame for frame. Available at 480p and 720p; a 1080p request that sets it is refused. No effect on price.

Default
Not specified
Constraints
minimum:0
maximum:2147483647
camera_fixedbooleanOptional

Locks the camera, false by default. Set to true and the frame holds still while the subject moves within it — ideal for product shots and plates destined for a static background. Leave it false when you want a push, pan, or tilt, and say so in the prompt.

Default
false
generate_audiobooleanOptionalAffects price

Whether a native soundtrack is rendered in sync with the picture. On by default and priced in a higher tier; switch it off for silent product loops or when sound is added in post.

Default
true
duration_secondsintegerOptionalAffects price

Length in whole seconds, 4 to 12, defaulting to 5, and the billed quantity. The longer a shot runs, the further it drifts from its source frame, so confirm the motion at 4 or 5 seconds before extending.

Default
5
Constraints
minimum:4
maximum:12
resolutionstringOptionalAffects price

Output resolution, 720p by default and 1080p at the top; nothing higher is offered here. It sets a rate tier. Note that it governs the delivered format only — it will not rescue a soft source frame.

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

Output framing, six fixed values, 16:9 by default. There is no adaptive option in this generation, so a mismatch with the source is resolved by reframing to the requested ratio and the subject can lose its margins. Matching the source crop is the reliable route.

Default
"16:9"
Allowed values
"21:9""16:9""4:3""1:1""3:4""9:16"
last_image_urlstringOptional

Optional closing frame: send it with `image_url` and the shot has to land on this still, which makes transitions and before-and-after pairs repeatable. Give it the same proportions as the opening frame. Available at 480p and 720p; a 1080p request that sets it is refused. No effect on price.

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

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-1.5-pro/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 1.5 Pro · 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