Seedance 2.0 API: Text-to-Video and Image-to-Video in Python
One script creates the task, polls it and saves the MP4. Here are the fields, the limits, and two 5-second clips we made on October 30, 2026.
On this page
- Short answer
- What do you need before the first call?
- Which fields does a Seedance 2.0 request take?
- The Python script
- How do you send a first frame for image to video?
- Two clips we made
- What the files measured
- Which errors and limits will you hit?
- Seedance 2.0, Fast, Mini or 2.5?
- FAQ
- Is there an official Seedance 2.0 API?
- Can I send an image URL instead of base64?
- How do I turn off the sound?
- Why did my request time out?
- How long do the video links last?
- Sources checked
Seedance 2.0 runs on OmniaKey's media API under the model id dreamina-seedance-2-0-260128. You POST a task to https://api.omniakey.com/v1/media/tasks, poll the task id, then download the MP4. Our two 5-second 720p clips, one from text and one from a still image, took 159 and 139 seconds on October 30, 2026.
Short answer
- Model id
dreamina-seedance-2-0-260128, with three operations:text_to_video,image_to_videoandreference_to_video(OmniaKey's video API docs, checked October 30, 2026). - Create, poll, download: the create call answers 202 with an id, then you GET
/v1/media/tasks/{id}every few seconds until the status issucceeded(OmniaKey's media API overview, checked October 30, 2026). - Duration runs from 4 to 15 whole seconds, and the full 2.0 id renders 480P, 720P and 1080P.
- The whole request body stays under 1 MiB, inline base64 included.
- Our two 5-second 720p renders finished in 159 seconds (text to video) and 139 seconds (first frame).
What do you need before the first call?
An OmniaKey API key, Python 3 and the exact model id. The script imports only the standard library, so it runs with no virtualenv and no package install.
Export the key as OMNIAKEY_API_KEY before the first call. The script reads it once and sends it as a Bearer token on every request, so the key stays out of the file and out of your git history.
Create with POST https://api.omniakey.com/v1/media/tasks and poll with GET https://api.omniakey.com/v1/media/tasks/{id}. Those two plus POST /v1/media/tasks/{id}/cancel are the whole media API (media API overview, checked October 30, 2026).
Type model ids in full. OmniaKey does not serve the short aliases seedance-2 and seedance-2-5, and they answer 503 no_available_channel (video API docs, checked October 30, 2026).
Video jobs are pre-charged per second of generated output, and the charge comes back automatically when a job ends failed or canceled (media API overview, checked October 30, 2026). For what each second costs now, see current rates on the Seedance 2.0 model page.
Which fields does a Seedance 2.0 request take?
Four top-level fields carry the job, and a parameters object carries the output settings. Only duration has no sensible default, so always send it.
model: the full model id (short aliases fail)type:video(the media kind)operation:text_to_video,image_to_video,reference_to_video(bare name, no prefix)input.prompt: one string (scene, action and camera, up to 12,000 characters)input.media[]:{"type": "first_frame", "url": …}(a first frame, orreference_image)
Inside parameters:
duration: 4 to 15, whole seconds (decides the price)resolution:480P,720P,1080P(Fast and Mini stop at 720P)ratio:16:9,9:16,1:1,4:3,3:4,21:9,adaptive(adaptivefollows the frame)generate_audio:trueby default, orfalse(falsegives a silent MP4)n:1(must be 1)
Both lists follow OmniaKey's video API docs and media API overview (checked October 30, 2026), with the prompt limit from the model page.
Send the bare operation name; OmniaKey adds the media-type prefix itself (video API docs, checked October 30, 2026). Parameters it does not recognize go to the provider as-is (media API overview, checked October 30, 2026).
One trap catches people who start from the vendor docs: BytePlus's own create-task API puts fields inside content[] and at the top level, so code from a BytePlus tutorial will not run unchanged against /v1/media/tasks (BytePlus's create-task API, checked October 30, 2026).
The Python script
One file runs both operations, and the only difference is the body it builds. Save it as seedance_video.py and run python3 seedance_video.py.
import base64
import json
import os
import time
import urllib.error
import urllib.request
import uuid
API = "https://api.omniakey.com/v1/media/tasks"
KEY = os.environ["OMNIAKEY_API_KEY"]
MODEL = "dreamina-seedance-2-0-260128"
def request(method, url, body=None, extra_headers=None):
headers = {"Authorization": f"Bearer {KEY}", "User-Agent": "omniakey-example/1"}
data = None
if body is not None:
headers["Content-Type"] = "application/json"
data = json.dumps(body).encode()
headers.update(extra_headers or {})
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, timeout=120) as resp:
return json.load(resp)
def poll(url, attempts=5):
for attempt in range(attempts):
try:
return request("GET", url)
except urllib.error.HTTPError as err:
if err.code < 500 or attempt == attempts - 1:
raise
except urllib.error.URLError:
if attempt == attempts - 1:
raise
time.sleep(5 * (attempt + 1))
def run_task(body, out_path):
task = request("POST", API, body, {"Idempotency-Key": str(uuid.uuid4())})["id"]
print("task", task, flush=True)
for _ in range(180): # 5-second polls, up to 15 minutes
time.sleep(5)
state = poll(f"{API}/{task}")
if state["status"] not in ("queued", "processing"):
break
else:
raise TimeoutError(f"{task} is still {state['status']}: poll it again later, do not resubmit")
if state["status"] != "succeeded":
raise RuntimeError(state.get("error") or state["status"])
asset_url = state["assets"][0]["url"] # signed link: short-lived, so download now
req = urllib.request.Request(asset_url, headers={"User-Agent": "omniakey-example/1"})
with urllib.request.urlopen(req, timeout=300) as resp, open(out_path, "wb") as f:
f.write(resp.read())
return task
def text_to_video(prompt, out_path, duration=5, resolution="720P", ratio="16:9"):
body = {
"model": MODEL,
"type": "video",
"operation": "text_to_video",
"input": {"prompt": prompt},
"parameters": {"duration": duration, "resolution": resolution, "ratio": ratio},
}
return run_task(body, out_path)
def image_to_video(prompt, image_path, out_path, duration=5, resolution="720P", audio=False):
with open(image_path, "rb") as f:
raw = f.read()
if len(raw) > 700_000: # base64 adds a third, and the whole body must stay under 1 MiB
raise ValueError("first frame too large: save it as a JPEG about 1024 px wide")
frame = "data:image/jpeg;base64," + base64.b64encode(raw).decode()
body = {
"model": MODEL,
"type": "video",
"operation": "image_to_video",
"input": {"prompt": prompt, "media": [{"type": "first_frame", "url": frame}]},
"parameters": {"duration": duration, "resolution": resolution, "ratio": "adaptive",
"generate_audio": audio},
}
return run_task(body, out_path)
if __name__ == "__main__":
print("done", text_to_video(
"Close-up of a tall glass on a wooden bar counter. Sparkling water is poured over ice; bubbles "
"rise and the ice cubes clink and shift. Warm evening light from the left. The camera slowly "
"pushes in. Sound of pouring, fizzing and clinking ice. No people, no text.",
"sparkling-glass.mp4",
))
print("done", image_to_video(
"The cat slowly wakes, stretches its front paws and yawns, then looks toward the window. "
"Raindrops keep running down the glass. The camera stays still.",
"cat.jpg",
"cat-wakes.mp4",
))
Six parts of this file matter before you change anything.
- Every create request carries a random
Idempotency-Key; replaying that key with the same body returns the original task with HTTP 200 and never charges twice (media API overview, checked October 30, 2026). - The loop sleeps 5 seconds between polls and stops after 180 tries, which is 15 minutes.
- A timeout means the job is still rendering. Poll the same task id again later and never resubmit, or you pay for a second clip.
assets[0].urlis a signed link that lasts about an hour, so the script downloads as soon as the task succeeds (media API overview, checked October 30, 2026).progress.percentcan sit at 0 while the job runs, so the loop followsstatusand nothing else (video API docs, checked October 30, 2026).- The image function refuses a file over 700,000 bytes, because base64 adds a third and the body has to stay under 1 MiB.
The request shape matches Kling 3.0 on the same API, so switching models changes the model id and the parameters, not the code around them.
How do you send a first frame for image to video?
Put the picture in input.media[] with "type": "first_frame", inline as a data:image/jpeg;base64,... URI, the shape our cat clip used.
input.media[] accepts a public HTTPS URL or an inline data URI, and the whole body is capped at 1 MiB (video API docs, checked October 30, 2026). OmniaKey's own test saw an ordinary HTTPS frame link rejected with provider_submit_rejected, which is why the example sends a data URI (video API docs, checked October 30, 2026).
Base64 adds a third, so start from a JPEG about 1024 pixels wide: our first frame was 134,356 bytes, and a 4 MB PNG would bounce.
Keep parameters.ratio at adaptive here: it follows the shape of the picture you sent, which is how a 1024×571 still became a 1280×720 clip. For reference_to_video, the same array takes "type": "reference_image" (video API docs, checked October 30, 2026).
Two clips we made
Both tasks went out on October 30, 2026 with the file above, unchanged. The first is text to video, task task_uF2QUtbeAAUW11lXEaLxNw1ZnBUoqvNk, created at 01:39:43 UTC and succeeded at 01:42:22 UTC. The prompt asked for the sound of the pour, so audio stayed on.
Close-up of a tall glass on a wooden bar counter. Sparkling water is poured over ice; bubbles rise and the ice cubes clink and shift. Warm evening light from the left. The camera slowly pushes in. Sound of pouring, fizzing and clinking ice. No people, no text.

The frames show a glass with ice on a wooden bar, bottles and warm lights blurred behind it. The glass grows from the first frame to the last, the push-in the prompt asked for, and ends full with bubbles near the bottom.
The second is image to video, task task_u85W0jn1sQLkrszJlxqXF1hxJYhsndxK, created at 01:42:33 UTC and succeeded at 01:44:52 UTC. The first frame is a still of a ginger cat asleep on a stack of old books by a rainy window (1024×571 pixels), sent as a data URI with "type": "first_frame".
The cat slowly wakes, stretches its front paws and yawns, then looks toward the window. Raindrops keep running down the glass. The camera stays still.

The cat lifts its head, opens its eyes, stretches both front paws over the edge of the books, yawns wide and turns its head toward the window, in the order the prompt gave. The rain keeps running down the glass and the framing holds still, as asked.

These two runs cover text to video and a first frame at 720P for 5 seconds. We did not test 1080P, Fast, Mini, 2.5, reference_to_video, an HTTPS frame link, or any other duration.
What the files measured
Both downloads are H.264 MP4 at 1280×720 and 24 fps: 121 frames, 5.04 seconds.
| Clip | Glass | Cat |
|---|---|---|
| Operation | text to video | image to video |
| Time to finish | 159 s | 139 s |
| File size | 2,070,248 bytes | 1,352,027 bytes |
| Video | 1280×720, 24 fps, 121 frames | 1280×720, 24 fps, 121 frames |
| Audio | AAC track | none |
The glass clip's track carries pouring, fizzing and clinking ice, with no voices and no music. The cat clip has no track at all, because the request sent generate_audio: false.
Both task records reported usage.completion_tokens: 108900, which is 121 × 1280 × 720 ÷ 1024, the token formula BytePlus uses to estimate video billing (BytePlus's pricing page, checked October 30, 2026).
OmniaKey's docs say a 6-second 720p clip usually finishes within about a minute (video API docs, checked October 30, 2026). Ours took 159 and 139 seconds, and three 5-second 720p clips in our October 26 comparison took 144.8, 319.0 and 184.7 seconds. Plan for 2 to 5 minutes and poll rather than hold a request open.
Which errors and limits will you hit?
Check three settings first: the model id, a duration from 4 to 15, and audio, which the provider's filter can reject.
| Status / Code | What to change |
|---|---|
400 invalid_request | body or input.prompt |
400 model_price_error | duration to 4–15 s |
403 model_not_allowed | key's model access |
409 idempotency_key_reused | a new key per body |
503 no_available_channel | full id, bare operation |
| failed task, provider error | read error, then retry |
Those codes come from the media API overview and the video API docs (checked October 30, 2026). A task that ends failed refunds its pre-charge, so a rejected render costs you nothing but time.
Watch the audio setting. Seedance renders a track by default, and leaving it on made the provider reject a 4-second 480P request with OutputAudioSensitiveContentDetected.PolicyViolation, so send parameters.generate_audio: false unless the scene needs sound (video API docs, checked October 30, 2026).
BytePlus says the 2.0 series does not accept reference images or videos that contain real human faces (BytePlus's Seedance 2.0 page, checked October 30, 2026). Seedance 2.0 also has no seed and no camera_fixed, though both are fields on the vendor's create-task API (BytePlus's create-task API, checked October 30, 2026).
Fifteen seconds is the ceiling on this family, and duration: -1 buys nothing more; a 20- or 30-second take belongs on Seedance 2.5 (the Seedance 2.0 model page, checked October 30, 2026).
OmniaKey publishes no fixed rate limits; treat HTTP 429 as retryable with exponential backoff (OmniaKey's API errors page, checked October 30, 2026).
Seedance 2.0, Fast, Mini or 2.5?
Four tiers take the same request body, so switching costs one model id and, on Fast and Mini, a lower resolution ceiling.
- Seedance 2.0,
dreamina-seedance-2-0-260128: 480P, 720P and 1080P, 4 to 15 seconds. BytePlus's list price for a 5-second clip is $0.76 at 720p and $0.35 at 480p (BytePlus's pricing page, checked October 30, 2026). - Seedance 2.0 Fast,
dreamina-seedance-2-0-fast-260128: 480P and 720P, 4 to 15 seconds. BytePlus's list price for a 5-second clip is $0.60 at 720p and $0.28 at 480p. - Seedance 2.0 Mini,
dreamina-seedance-2-0-mini-260615: 480P and 720P, 4 to 15 seconds. BytePlus's list price for a 5-second clip is $0.38 at 720p and $0.18 at 480p. - Seedance 2.5,
dreamina-seedance-2-5-260628: the only id in the family that takes a 20- or 30-second take, and the slowest: about four minutes for a 4-second 480P clip (video API docs, checked October 30, 2026). Our Seedance 2.5 review covers the picture side.
OmniaKey's model pages report a 4-second 480P clip at about a minute on 2.0, two minutes on Fast and 100 seconds on Mini (checked October 30, 2026).
Take the full 2.0 id when you need a 1080P master, because Fast and Mini stop at 720P (video API docs, checked October 30, 2026). OmniaKey sets its own rates for these ids, so see current rates on the Seedance 2.0 model page.
FAQ
Is there an official Seedance 2.0 API?
Yes. BytePlus ModelArk sells it, and activating it there needs an account balance above USD 30, a Savings Plan or a Seedance resource pack (BytePlus's Seedance 2.0 page, checked October 30, 2026). The script above calls the same model id through OmniaKey's media API instead.
Can I send an image URL instead of base64?
OmniaKey's docs list a public HTTPS URL next to the inline data URI (video API docs, checked October 30, 2026). Our runs went inline because an HTTPS frame link came back as provider_submit_rejected in OmniaKey's own test, so try a URL on a test clip first.
How do I turn off the sound?
Send parameters.generate_audio: false, and the MP4 arrives with no audio track at all, as the cat clip did. Audio is on by default on this model.
Why did my request time out?
The clip is usually still rendering, since 5-second 720p jobs took 139 to 319 seconds in our runs. Poll the same task id again, never resubmit, and raise the 15-minute loop limit for 15-second clips.
How long do the video links last?
The signed link in assets[0].url lasts about an hour, and the task record itself is kept for 24 hours after creation (media API overview, checked October 30, 2026). Download on the first success, or poll the task again for a fresh link.
Sources checked
- BytePlus ModelArk: Seedance 2.0 guide, checked October 30, 2026
- BytePlus ModelArk: create video generation task API, checked October 30, 2026
- BytePlus ModelArk: model pricing and token formula, checked October 30, 2026
OmniaKey's own documentation, checked October 30, 2026: video API, media API overview and API errors.
Models in this article
Related articles
Seedance 2.5 review
The meaningful upgrade is 30-second storytelling and production control, not the unverified 4K headlines circulating around it.
HappyHorse 1.1 vs Seedance 2.0: Same-Prompt Test and Price
Same three prompts, same day: Seedance 2.0 made the better clips, HappyHorse 1.1 made them twice as fast.
Kling 3.0 API: Text-to-Video Request Code and Real Clips
A text-to-video task is one POST with model kling-3.0. Here is the request code, the fields it takes, and two 5-second clips we made on October 29, 2026, measured.
Recent posts
- Codex Image Generation With a Custom Provider: imagegen Setup
- Factory Droid Custom Models: Use Claude and GPT With Your Own API Key
- Gemini 4 Carbon: Release Date, Price and API Access
- GPT Image 2.5 API: Generate and Edit Images in Python
- MiniMax M3.1 Flash Review: Benchmarks, Speed and How It Compares to M3
