PL Log inOpen Studio
Preview version. Model catalog, prices, calculator and docs are live. Generation goes live at launch.

Seedance 2.5 From Python: A Working Video API Walkthrough

Generate Seedance 2.5 video from Python - request fields, text and image to video, polling, signed webhooks, parallel takes, errors and per-second cost.

With Seedance 2.5 you describe a shot in text, optionally attach a first frame, and get back an MP4 with sound, billed per second of output. In Kadraris API that is a POST /v1/generate with "model": "seedance-2.5". The call returns a task right away; the finished file arrives later by polling or webhook. The rate today is from $0.261 per second, and every request tells you its cost before it runs.

This walkthrough starts with code you can run, then covers the input fields, first frames, webhooks, batches of takes, errors and prompts.

Run a first clip

You need the requests package and a key exported as KADRARIS_API_KEY:

bash
pip install requests
export KADRARIS_API_KEY=sk-kd-...

Three small helpers do all the work: submit, wait, download.

python
import os
import time
import requests

API = "https://kadraris.com/v1"
HEADERS = {
    "Authorization": f"Bearer {os.environ['KADRARIS_API_KEY']}"
}

def submit(model, input_, webhook_url=None):
    body = {"model": model, "input": input_}
    if webhook_url:
        body["webhook_url"] = webhook_url
    r = requests.post(f"{API}/generate", headers=HEADERS,
                      json=body, timeout=30)
    if r.status_code != 202:
        err = r.json().get("error", {})
        raise RuntimeError(
            f"{r.status_code} {err.get('type')}: "
            f"{err.get('message')}")
    return r.json()

def wait(task_id, every=5, timeout=1200):
    stop_at = time.time() + timeout
    while time.time() < stop_at:
        t = requests.get(f"{API}/tasks/{task_id}",
                         headers=HEADERS, timeout=30).json()
        if t["status"] in ("succeeded", "failed"):
            return t
        time.sleep(every)
    raise TimeoutError(task_id)

def download(url, path):
    with requests.get(url, stream=True, timeout=120) as r:
        r.raise_for_status()
        with open(path, "wb") as f:
            for chunk in r.iter_content(1 << 20):
                f.write(chunk)

Now a 10-second vertical clip with sound:

python
task = submit("seedance-2.5", {
    "prompt": (
        "Handheld close-up of a baker pulling a tray of "
        "croissants from a stone oven at dawn, flour in the "
        "air, warm orange light, crackling crust and radio "
        "playing quietly"
    ),
    "duration": 10,
    "resolution": "720p",
    "aspect_ratio": "9:16",
    "generate_audio": True,
})
print("task:", task["id"], "estimate:", task["estimated_cost"])

result = wait(task["id"])
if result["status"] == "succeeded":
    video = result["output"][0]
    download(video["url"], f"{task['id']}.mp4")
    print("saved", video["width"], "x", video["height"],
          "cost:", result["cost"])
else:
    print("failed, $0 charged:", result["error"])

Save the file the moment the task succeeds. Result URLs are temporary: a pickup window, not a hard drive.

Input fields

What Seedance 2.5 accepts in Kadraris:

FieldValuesWhen to change it
prompttextshot, subject, action, light, sound
duration4 to 30 secondslonger scenes in one take
resolution480p, 720p, 1080p480p drafts, higher for finals
aspect_ratio16:9, 9:16, 1:1, 4:3, 3:4, 21:9, adaptive9:16 for Reels, Shorts, TikTok
image_urlpublic URLfirst frame for image to video
generate_audiotrue / falsefalse when you bring music

There are more, for example end_image_url, reference arrays (image_urls, video_urls) and seed. The full, current list is in the schema:

bash
curl https://kadraris.com/v1/models/seedance-2.5

I read it each time I update my code. A value outside the schema returns 422, and nothing is charged.

What the model brings

ByteDance Seed shipped Seedance 2.5 on July 31, 2026, with developer API access from early August. For API work these are the points that count:

  • Single takes up to 30 seconds. The range is 4 to 30 seconds per generation.
  • Audio built in. Dialogue, effects and music come out of the same generation as the image.
  • Big reference budget. One request can carry 30 images, 10 videos and 10 audio files.
  • Resolution. In Kadraris: 480p, 720p or 1080p.

The 30-second ceiling is the reason I use it. With Veo 3.1 you get 8 seconds per call and with Kling 3.0 up to 15, so longer pieces need stitching. Seedance 2.5 fits a whole short scene in one call.

Cost per clip

Billing is per second of generated video at from $0.261 per second (that is the 480p rate; 720p and 1080p are higher). Multiply the rate for your resolution by duration.

You do not need to calculate it yourself:

  • Before: every POST /v1/generate response carries estimated_cost.
  • After: the finished task shows the final cost.
  • On failure: cost is 0 and the hold goes back to your balance.

There is no subscription. The prepaid balance starts at $10 and does not expire. Other models' rates: AI video cost per second.

Start from your own first frame

When framing, the product's position or a person's appearance must be precise, I generate the opening frame as an image and pass it to Seedance. image_url must be a public HTTPS address.

python
task = submit("seedance-2.5", {
    "prompt": ("Camera circles a leather watch on a slate "
               "slab, soft rim light, dust drifting slowly"),
    "image_url": "https://example.com/frames/watch-hero.png",
    "duration": 6,
    "resolution": "720p",
    "aspect_ratio": "16:9",
    "generate_audio": False,
})

The first frame carries most of the result. I usually create it with Nano Banana Pro or FLUX.2 [pro] at the video's aspect ratio, so nothing gets cropped. For the image step, see Generate images with Python.

Let a webhook tell you when it is done

Polling is fine for scripts. In a web app, add webhook_url, and the completed task is delivered to that address as the same JSON that GET /v1/tasks/{id} returns.

python
task = submit(
    "seedance-2.5",
    {"prompt": "Fog rolling over a pine forest, time-lapse",
     "duration": 8, "resolution": "720p"},
    webhook_url="https://yourapp.example.com/hooks/video",
)

Every delivery is signed. The x-kadraris-signature header holds the hex HMAC-SHA256 of the unparsed body, computed with your dashboard's webhook secret. Reject anything that does not match:

python
import hashlib
import hmac

def is_valid(raw_body: bytes, signature: str,
             secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body,
                        hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature or "")

For a full receiver (retries, a safety-net poller) read async tasks and webhooks for AI video; field reference in the webhooks docs.

Several takes at once

The same prompt gives a different clip each time. I seldom keep take one, so I send three or four together and choose. Tasks are async, so this is a plain loop:

python
prompt = ("A red kite tugging on its string over a windy "
          "beach at sunset, slow dolly-in")
tasks = [
    submit("seedance-2.5", {"prompt": prompt, "duration": 5,
           "resolution": "480p", "aspect_ratio": "16:9"})
    for _ in range(4)
]
for r in (wait(t["id"]) for t in tasks):
    ok = r["status"] == "succeeded"
    print(r["id"], r["status"],
          r["output"][0]["url"] if ok else r["error"])

How I work: short drafts at 480p, choose the best, then one final render at 720p or 1080p at full length. A cheap draft is a small share of the final's cost, and a failed draft is free.

When something goes wrong

StatusCauseWhat to do
401wrong keycheck KADRARIS_API_KEY
402balance below estimated_costtop up on pricing, resubmit
422bad duration, resolution or ratioread error.message, compare with schema
429rate limit hitback off, growing delays
502provider side problempause, then retry

A task can also end with status: "failed", for instance when the content filter blocks a prompt. The error field says why, and the charge is 0.

Writing prompts for it

Seedance 2.5 reacts well to prompts that read like a shot list entry. My order:

  1. Framing and camera move, e.g. "low tracking shot" or "locked-off medium shot".
  2. Who and what: the subject and their action, kept to a clause or two.
  3. Where and when: location, hour, the light source, weather.
  4. What we hear, if audio is on: "seagulls, waves on pebbles, a far-off ferry horn".
  5. Look: a single style phrase, and only when needed.

Concrete and short beats a pile of adjectives. If a take misses, alter a single element per retry; that is how you find out what moves the model.

Choosing between video models? AI video API comparison 2026 sets Seedance 2.5 against Kling 3.0 and Veo 3.1 (Quality). In short: Seedance leads on single-take length and references, Veo 3.1 and Kling 3.0 reach higher resolutions, while Kling 3.0 can split one prompt into several shots. No code? Open Studio, on the same balance.

Questions

Can I set an end frame as well as a start frame?

Yes. Seedance 2.5 accepts end_image_url, but only together with image_url. With a first and last frame the aspect ratio is forced to adaptive.

Is 1080p worth it for drafts?

Rarely. The per-second price climbs with resolution, which is why drafts stay at 480p for me and 720p or 1080p for the take I keep.

What happens if the content filter rejects my prompt?

The task ends as failed, the error field explains the reason, and you pay $0. Rephrase and submit again.

Do I need the Python SDK for this?

No. Everything above is plain HTTP with requests. Any language with an HTTP client can do the same calls.