Sociaro

bytedance/seedance-2.0

Seedance 2.0 — video from text, an image, a clip or audio.

The input is a `content` array of typed parts, each with its own role, which is how one model covers text-to-video, first/last frame, and reference-driven generation.

Slugbytedance/seedance-2.0
Kindvideo
Vendorbytedance
EndpointPOST /v1/byteplus/contents/generations/tasks

Charged per job from what the generator reports it rendered — your Logs show the exact figure for each one; see balance and billing.

Calling it

curl https://api.sociaro.com/v1/byteplus/contents/generations/tasks \
  -H "Authorization: Bearer $SOCIARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "model": "bytedance/seedance-2.0",
        "prompt": "a paper boat on a rain-soaked street",
        "resolution": "720p",
        "duration": 5
      }'

# then poll the job id it returns:
curl https://api.sociaro.com/v1/byteplus/contents/generations/tasks/JOB_ID \
  -H "Authorization: Bearer $SOCIARO_API_KEY"

Parameters

Seedance 2.0 — video from text, image, video or audio

FieldTypeRequiredDefaultValuesWhat it does
contentarrayyes—parts, each with its own type and an optional roleThis is the whole input. `{"type":"text","text":…}`, `{"type":"image_url","image_url":{"url":…},"role":"first_frame"|"last_frame"|"reference_image"}`, `{"type":"video_url",…,"role":"reference_video"}`, `{"type":"audio_url",…,"role":"reference_audio"}`. Every other model on this endpoint takes flat fields — these three do not.
resolutionstringno720p480p · 720p · 1080p · 4kA tier this model does not have is refused with a 400 naming the ones it does. The generator would ACCEPT it and silently render 720p, so the check is ours and it costs you nothing.
durationintegerno54–15, or -1Seconds. `-1` lets the model pick the length that suits the inputs.
ratiostringnoadaptive21:9 · 16:9 · 4:3 · 1:1 · 3:4 · 9:16 · adaptive
output_formatstringnomp4mp4`mov` exists on 2.5 alone — the generator refuses it here, in its own words.
generate_audiobooleanno—true · false
bitrate_modestringnothe model's ownstandard · highNot in the vendor's public reference, but the generator honours it: `high` encodes at a higher bitrate than `standard` (in one 480p measurement about 4.6–6.9 Mbps against 1.2–1.9 Mbps), and the price is the same, because these models are metered per token. The default differs by model, so leave it out to take the model's own.
return_last_framebooleannofalsetrue · falseAsks for the closing still beside the clip. It arrives as `last_frame_url`, on the same expiring /v1/media terms, at NO extra cost — useful as the `first_frame` of the next clip.
watermarkbooleannofalse heretrue · falseThe vendor defaults this on; we default it off.
seedintegernorandom—Every Seedance model we serve accepts it. The same seed gives similar, not identical, results: the vendor says complete consistency is not guaranteed.

Anything not listed here is refused rather than ignored, so a typo fails loudly instead of quietly producing something else.

Worked example

# text to video
curl https://api.sociaro.com/v1/spicy/generations \
  -H "Authorization: Bearer $SOCIARO_API_KEY" -H "Content-Type: application/json" \
  -d '{ "model": "spicy/seedance-2-0",
        "content": [{ "type": "text", "text": "a paper lantern drifting over still water at dusk" }],
        "resolution": "720p", "duration": 5 }'

# from a first frame, with a reference image, audio off
curl https://api.sociaro.com/v1/spicy/generations \
  -H "Authorization: Bearer $SOCIARO_API_KEY" -H "Content-Type: application/json" \
  -d '{ "model": "spicy/seedance-2-0",
        "content": [
          { "type": "text", "text": "she turns towards the lantern" },
          { "type": "image_url", "image_url": {"url": "https://your-cdn.example.com/first.jpg"},
            "role": "first_frame" },
          { "type": "image_url", "image_url": {"url": "https://your-cdn.example.com/style.jpg"},
            "role": "reference_image" }
        ],
        "resolution": "720p", "duration": 8, "generate_audio": false }'

The input is a content ARRAY, not flat fields. All three Seedance rows share that shape and every other model on this endpoint does not — a request written for spicy/wan2.2-i2v will not work here, and the reverse is also true.

Each part carries its own type and, for media, a role. The role is what tells the generator whether your picture is the opening frame, the closing frame, or a style reference.

Metered per token, from what the generator actually rendered — not per clip. Resolution is the biggest lever, then duration; a squarer frame has fewer pixels and comes out slightly cheaper than a 16:9 one of the same length.

A job with a video input is metered at a lower per-token rate but carries a MINIMUM cost, and that floor scales with the OUTPUT duration you ask for, not with the length of the clip you supply. A longer reference clip raises the metered charge but not the floor.

How a job runs

These routes are a pass-through: you send BytePlus's own body and get their own response back, with our additions beside it. Generation takes longer than a request should wait, so it goes in three moves — submit, poll, collect.

# 1. submit — the id comes back as `id`. There is NO `job_id` on THIS response
curl -sS https://api.sociaro.com/v1/byteplus/contents/generations/tasks \
  -H "Authorization: Bearer $SOCIARO_API_KEY" -H "Content-Type: application/json" \
  -d '{ "model": "…", "content": [ … ] }'
# -> { "id": "cgt-…" }

# 2. poll every 5-10 s
curl -sS https://api.sociaro.com/v1/byteplus/contents/generations/tasks/cgt-… \
  -H "Authorization: Bearer $SOCIARO_API_KEY"
# -> { "status": "running", … }
# -> { "status": "succeeded", "content": { "video_url": "<the vendor's own link>" },
#      "media_url": "https://api.sociaro.com/v1/media/<media_id>" }

# 3. collect — the opaque id IS the credential, no header needed
curl -sSL "https://api.sociaro.com/v1/media/<media_id>" -o out.mp4

Images use POST /v1/byteplus/images/async and its poll in the same shape, and the faster models also answer synchronously at POST /v1/byteplus/images/generations — one request, the picture in the response. A model whose render outlasts the gateway's ~2-minute ceiling is refused there by name, telling you to use the async route.

Reading the poll

Two differences will bite a client written against the poll-style routes. The SUBMIT returns id, not job_id — code reading job_id there gets nothing and cannot poll at all. And status is the VENDOR'S vocabulary: running, succeeded, failed — not processing, completed, failed. A check for completed on this route never becomes true, which reads as a job that never finishes rather than as an error.

The POLL response does carry job_id beside the vendor's id, as a convenience, and it returns the vendor's whole body — around twenty fields — rather than the five-field shape the other routes use. Gate on status rather than on the poll's HTTP code.

Prefer media_url over the vendor's raw link. Both are returned: content.video_url (or data[].url for images) is theirs, kept for compatibility and due to be removed once nobody reads it; media_url is ours. Ours points at our host, hides the vendor's storage, and expires. A second asset arrives on the same terms — return_last_frame gives content.last_frame_url with last_frame_media_url beside it.

Collecting the result

media_url is a capability: the opaque id is the credential, so no Authorization header is needed and anyone holding the link can fetch it. It expires within the hour — download rather than store the link.

Rules the gateway adds

Your key must be granted this model — 403 otherwise — and either spelling satisfies the grant: our catalogue id or the vendor's own native one.

Because this is a pass-through, unknown fields go to the generator rather than being refused here, and it is the generator that decides. The exception is resolution, which we check ourselves: an unsupported tier is silently ignored upstream, so you would ask for the higher one, be handed a lower one, and be charged. A tier we cannot price is refused at submit, before any money is spent, with a message naming the ones this model has.

Ranges and enums are the generator's own, so an out-of-range value comes back as its rejection, in its wording and with its numbers.