Skip to main content

Seedance 2.0 Mini reference-to-video from ByteDance builds one shot from your own material: up to 9 images, 3 video clips and 3 audio tracks, with no first frame. Output is 4 to 15 seconds at 480p or 720p, with sound in the same pass.

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-2.0-mini/reference-to-video",
  "input": {
    "aspect_ratio": "adaptive",
    "bitrate_mode": "standard",
    "duration_seconds": 5,
    "enable_web_search": false,
    "generate_audio": true,
    "prompt": "A slow aerial shot drifting over a misty pine forest at sunrise",
    "reference_image_urls": [
      "https://example.com/reference.png"
    ]
  }
}'

# 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

Required, and here it also does the casting. References are cited in the order you send them, so name which image is which character, what each clip contributes and what the audio is for. Briefs are not reviewed on our side; where Seedance's own layer refuses the wording, a first frame on image-to-video is the route that still gets there.

Default
Not specified
Constraints
minLength:1
watermarkbooleanOptional

Adds the model’s own visible "AI generated" watermark to the video. Off by default; switch it on when the place you publish requires AI-made footage to be labelled. No effect on price.

Default
false
bitrate_modestringOptional

Encoding bitrate of the delivered file: `standard` (default) or `high`. High keeps fine texture and fast motion crisper at the same resolution and makes the file much larger; resolution, frame rate and length stay the same. No effect on price.

Default
"standard"
Allowed values
"standard""high"
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_searchbooleanOptionalAffects price

Off by default. Switched on, the model may search the web for information related to your prompt before it renders. Requests with it on are billed at the web-search rate, which is higher than the standard rate; the estimate shown before you submit already reflects it.

Default
false
generate_audiobooleanOptional

Whether a native soundtrack is rendered in sync with the picture. On by default; switching it off does not lower the rate in this tier. Turn it off for silent loops and for material you will score in post.

Default
true
duration_secondsintegerOptionalAffects price

Length of the finished clip in whole seconds, 4 to 15, default 5; -1 lets the model choose, with the estimate assuming 15 seconds. Together with the seconds of your reference clips it is the billed quantity. The more references a shot has to reconcile, the less 4 seconds can show them all — but confirm the combination short before paying for 15.

Default
5
Allowed values
-1456789101112131415
resolutionstringOptionalAffects price

Output resolution: 480p, or 720p by default. It selects a rate tier and will not sharpen low-resolution references. Check that the references combine at a low tier, then raise it for the final take.

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

Output framing, adaptive by default: the clip takes its shape from the primary reference. When the references disagree or the placement is fixed, pick one of the six ratios from 21:9 down to 9:16 and say in the prompt how the subjects should sit inside it. No effect on what you are billed.

Default
"adaptive"
Allowed values
"16:9""9:16""4:3""3:4""1:1""21:9""adaptive"
reference_audio_urlsarray<string>Optional

Up to 3 audio files, 2 to 15 seconds each and 15 seconds together, each under 15 MB, giving the shot a sonic anchor — delivery, tempo, a musical phrase — that motion and cut points align to. Audio cannot be the only reference: send at least one image or clip with it. It is an input, not the output track: whether the finished clip carries sound is still decided by `generate_audio`.

Default
Not specified
Constraints
maxItems:3
reference_video_urlsarray<string>Conditionally requiredAffects price

Up to 3 clips supplying motion, pacing or camera language. Each clip should run 2 to 15 seconds and all of them no more than 15 seconds together; keep every file under 50 MB and at 1080p or below, or the request is rejected. Their seconds are billed on top of the output length, so trim them to the part you actually want referenced.

Default
Not specified
Constraints
maxItems:3
reference_image_urlsarray<string>Conditionally required

Up to 9 images you are authorised to use, holding a character, a product, a style or a set. Two or three angles of the same subject carry more than a stack of near-identical frames. Every request needs at least one reference image or reference clip; audio on its own is refused. This endpoint has no first- or last-frame field: when the opening picture has to be exact, use image-to-video instead. 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
maxItems:9

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.0-mini/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 Seedance 2.0 Mini · 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