Unlimited Seedance, Kling, Wan and Seedream 5.0, now on KreatorFlow · available now · Fair use

See passes
Espresso pouring from a portafilter into a clear glass cup in warm morning cafe light
API

Official BytePlus partnerBytePlus, a ByteDance company

Seedance API in Python and Node.js: a complete tutorial

Call the Seedance API from Python and Node.js: text-to-video and image-to-video jobs, polling, MP4 download, signed webhooks and error handling.

KreatorFlow14 min read

Short answer

To use the Seedance API from Python or Node.js, send POST https://kreatorflow.ai/v1/videos with your key in "Authorization: Bearer", then poll GET /v1/jobs/{id} until the status is completed and stream GET /v1/jobs/{id}/output to a file. No SDK is needed: requests in Python and the built-in fetch in Node 18+ are enough. A 5-second Seedance 2.0 clip at 720p costs $0.55 (as of 18 October 2026).

Updated:

Call the Seedance API from code in six steps

  1. 1

    Get an API key

    Create a key with the videos:create and jobs:read scopes in the developer console, top up from $20, and export it as KREATORFLOW_API_KEY on the machine that runs the script.

  2. 2

    Install the HTTP client

    Python: pip install requests. Node.js: nothing to install on Node 18 or later, which ships fetch.

  3. 3

    Create a video job

    POST /v1/videos with model, prompt, duration, aspect_ratio and resolution, plus an Idempotency-Key header. The answer is 202 with the job id and the quoted token cost.

  4. 4

    Poll the job

    GET /v1/jobs/{id} every 10 seconds or so until the status is completed, failed, canceled or blocked. On a 429, wait the seconds in Retry-After.

  5. 5

    Download the MP4

    When the status is completed, GET /v1/jobs/{id}/output with the same key and stream the body to a file, then copy it to your own storage.

  6. 6

    Switch to webhooks in production

    Pass webhook_url when you create the job and verify the X-KreatorFlow-Signature header in a small handler, so no worker sits in a polling loop.

Request errors a video script should handle

From the KreatorFlow API reference. Every one returns success: false with this code.

CodeHTTPWhat it meansWhat to do
invalid_api_key401The key is missing, wrong or revoked.Send Authorization: Bearer <key>. Create a new key if it was revoked.
scope_missing403The key lacks the scope this endpoint needs.Use a key with that scope.
prompt_required400No prompt (or text for speech) was sent.Add a prompt.
prompt_too_long400The prompt is over the model's limit.Shorten it; GET /v1/models lists maxPromptChars.
model_not_found400Unknown model alias.Use an id from GET /v1/models.
duration_not_supported400The model doesn't offer that length.Send one of the model's durations.
aspect_ratio_not_supported400The model doesn't offer that aspect ratio.Send one of the model's aspect ratios.
resolution_not_supported400The model doesn't offer that resolution.Send one of the model's resolutions.
insufficient_balance402Not enough tokens for the quoted cost.Top up under Billing. data.top_up_url links there.
job_not_found404No job with that id for this key's account.Check the id; jobs are private to the account.
rate_limited429Too many requests in the window.Wait the seconds in the Retry-After header.
internal_error500Something failed on our side.Retry with the same Idempotency-Key.
dispatch_disabled503Generation is paused for this environment.Retry later.

Seedance API price per clip (18 October 2026)

From the KreatorFlow developer API rate card: a 5-second clip without a video input.

ModelModel idResolutionPer second5-second clip
Seedance 2.5kreator-video-seedance-2-5720p$0.207/s$1.05 (21 tokens)
Seedance 2.5kreator-video-seedance-2-51080p$0.444/s$2.25 (45 tokens)
Seedance 2.0kreator-video-seedance-2720p$0.110/s$0.55 (11 tokens)
Seedance 2.0kreator-video-seedance-21080p$0.240/s$1.20 (24 tokens)
Seedance 2.0 Fastkreator-video-seedance-2-fast720p$0.084/s$0.45 (9 tokens)
Seedance 2.0 Fastkreator-video-seedance-2-fast1080p$0.183/s$0.95 (19 tokens)
Seedance 2.0 Minikreator-video-seedance-2-mini720p$0.067/s$0.35 (7 tokens)
Seedance 2.0 Minikreator-video-seedance-2-mini1080p$0.135/s$0.70 (14 tokens)

The create response always carries the exact quote for your request.

Seedance API price per second on KreatorFlow

Seedance 2.0 and 2.5 at 720p and above on the KreatorFlow API: price per second, tokens per 5-second clip, and clips per prepaid top-up.

Model · resolutionTokens5 s clipList rate per second$20 top-up · 400 tokens$50 top-up · 1,000 tokens$100 top-up · 2,000 tokens$250 top-up · 5,000 tokens
Seedance 2.0 · 720p11/5s$0.55$0.110/s3690181454
Seedance 2.0 · 1080p24/5s$1.20$0.240/s164183208
Seedance 2.0 · 4K58/5s$2.90$0.580/s6173486
Seedance 2.5 · 720p21/5s$1.05$0.207/s194795238
Seedance 2.5 · 1080p45/5s$2.25$0.444/s82244111
Seedance 2.5 · 4K100/5s$5.00$0.999/s4102050

Seedance 2.0 and 2.5, 720p and above. List rate per second is the rate card price; a clip is billed at that rate and its total rounded up to whole tokens ($0.05), which is why a 5-second clip can cost a cent or two more than rate x 5. Top-up columns: 5-second clips each top-up covers, without a video input. A 10-second clip uses about twice the tokens.

What do I need before I start?

A KreatorFlow developer account, an API key and a token balance. The API is plain HTTPS and JSON at https://kreatorflow.ai/v1, so there is no SDK to install and nothing to keep up to date. KreatorFlow is an official partner of BytePlus, a ByteDance company, and the models are ByteDance's Seedance family.

Keep the key on your server: read it from an environment variable, never commit it, and never send it to a browser or a mobile app. Your users talk to your backend; your backend talks to KreatorFlow.

Which fields does POST /v1/videos take?

model and prompt are required. duration (seconds), aspect_ratio (ratio also works), resolution and generate_audio shape the clip; seed fixes the randomness so you can compare prompt changes; webhook_url asks for a signed call when the job ends. Seedance 2.0 (kreator-video-seedance-2) takes 4, 5, 6, 8, 10, 12 and 15 seconds and 480p, 720p, 1080p and 4K. GET /v1/models lists every model id your key can use with its limits.

The answer comes back at once with HTTP 202: the job id and status, your balance and the quoted cost in tokens. The job then moves through created, queued, processing, completed, failed, canceled and blocked.

Create response

HTTP/1.1 202 Accepted { "success": true, "data": { "job": { "id": "3f1c0a9e-...", "mediaType": "video", "modelId": "kreator-video-seedance-2", "status": "queued", ... }, "balance_tokens": 389, "quoted_token_cost": 11, "estimated_token_cost": 11 } }

How do I call the Seedance API from Python?

Save this as kreatorflow_video.py and run python kreatorflow_video.py "your prompt". It creates a 5-second 16:9 clip at 720p, polls every 10 seconds, waits out a 429, stops after 30 minutes, and streams the finished MP4 to disk in 1 MB chunks so a large file never sits in memory.

Errors from the API raise KreatorFlowError with the HTTP status and the machine-readable code, so the calling code can branch on err.code (for example top up on insufficient_balance) instead of parsing messages.

Python 3 (requests)

# kreatorflow_video.py: create a Seedance video, wait for it, save the MP4. # pip install requests (Python 3.9+) import os import sys import time import uuid import requests BASE = "https://kreatorflow.ai/v1" KEY = os.environ["KREATORFLOW_API_KEY"] AUTH = {"Authorization": f"Bearer {KEY}"} FINAL = {"completed", "failed", "canceled", "blocked"} class KreatorFlowError(Exception): def __init__(self, status, code, message): super().__init__(f"{status} {code}: {message}") self.status, self.code = status, code def _json(res): body = res.json() if res.status_code >= 400 or not body.get("success"): raise KreatorFlowError(res.status_code, body.get("code"), body.get("error")) return body["data"] def create_video(prompt, *, model="kreator-video-seedance-2", duration=5, aspect_ratio="16:9", resolution="720p", webhook_url=None, **extra): # extra: any other POST /v1/videos field, e.g. first_frame_url for image to video. payload = {"model": model, "prompt": prompt, "duration": duration, "aspect_ratio": aspect_ratio, "resolution": resolution, **extra} if webhook_url: payload["webhook_url"] = webhook_url # One Idempotency-Key per clip: resending the same request never creates a second job. headers = {**AUTH, "Idempotency-Key": str(uuid.uuid4())} data = _json(requests.post(f"{BASE}/videos", json=payload, headers=headers, timeout=60)) print(f"job {data['job']['id']} quoted at {data['quoted_token_cost']} tokens") return data["job"] def get_job(job_id): res = requests.get(f"{BASE}/jobs/{job_id}", headers=AUTH, timeout=30) if res.status_code == 429: time.sleep(int(res.headers.get("Retry-After", "5"))) return get_job(job_id) return _json(res)["job"] def wait_for_job(job, every=10, timeout=1800): deadline = time.time() + timeout while job["status"] not in FINAL: if time.time() > deadline: raise TimeoutError(f"job {job['id']} still {job['status']}") time.sleep(every) job = get_job(job["id"]) return job def download(job_id, path): with requests.get(f"{BASE}/jobs/{job_id}/output", headers=AUTH, stream=True, timeout=120) as res: res.raise_for_status() with open(path, "wb") as out: for chunk in res.iter_content(chunk_size=1 << 20): out.write(chunk) return path if __name__ == "__main__": prompt = " ".join(sys.argv[1:]) or "A paper boat drifting down a rainy street at dusk, low angle" try: job = wait_for_job(create_video(prompt)) except KreatorFlowError as err: sys.exit(f"request failed: {err}") if job["status"] == "completed": print("saved", download(job["id"], f"{job['id']}.mp4")) else: sys.exit(f"{job['status']}: {job.get('errorCode')} {job.get('errorMessage')}")

How do I call the Seedance API from Node.js?

The same client as an ES module. Save it as kreatorflow-video.mjs and run node kreatorflow-video.mjs "your prompt". It uses only built-ins: fetch, crypto.randomUUID for the Idempotency-Key, and a stream pipeline to write the MP4 without buffering it.

The exported functions (createVideo, getJob, waitForJob, download) drop straight into an Express route, a queue worker or a Next.js server action.

Node.js 18+ (fetch, ESM)

// kreatorflow-video.mjs: create a Seedance video, wait for it, save the MP4. // Node 18+ (fetch is built in). Run: node kreatorflow-video.mjs "your prompt" import { createWriteStream } from "node:fs"; import { Readable } from "node:stream"; import { pipeline } from "node:stream/promises"; const BASE = "https://kreatorflow.ai/v1"; const AUTH = { Authorization: `Bearer ${process.env.KREATORFLOW_API_KEY}` }; const FINAL = new Set(["completed", "failed", "canceled", "blocked"]); const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); export class KreatorFlowError extends Error { constructor(status, code, message) { super(`${status} ${code}: ${message}`); this.status = status; this.code = code; } } async function json(res) { const body = await res.json(); if (!res.ok || !body.success) throw new KreatorFlowError(res.status, body.code, body.error); return body.data; } // extra: any other POST /v1/videos field, e.g. { first_frame_url } for image to video. export async function createVideo(prompt, { model = "kreator-video-seedance-2", duration = 5, aspectRatio = "16:9", resolution = "720p", webhookUrl, extra = {} } = {}) { const res = await fetch(`${BASE}/videos`, { method: "POST", // One Idempotency-Key per clip: resending the same request never creates a second job. headers: { ...AUTH, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() }, body: JSON.stringify({ model, prompt, duration, aspect_ratio: aspectRatio, resolution, ...extra, ...(webhookUrl ? { webhook_url: webhookUrl } : {}) }), }); const data = await json(res); console.log(`job ${data.job.id} quoted at ${data.quoted_token_cost} tokens`); return data.job; } export async function getJob(id) { const res = await fetch(`${BASE}/jobs/${id}`, { headers: AUTH }); if (res.status === 429) { await sleep(Number(res.headers.get("Retry-After") ?? 5) * 1000); return getJob(id); } return (await json(res)).job; } export async function waitForJob(job, { every = 10_000, timeout = 30 * 60_000 } = {}) { const deadline = Date.now() + timeout; while (!FINAL.has(job.status)) { if (Date.now() > deadline) throw new Error(`job ${job.id} still ${job.status}`); await sleep(every); job = await getJob(job.id); } return job; } export async function download(id, path) { const res = await fetch(`${BASE}/jobs/${id}/output`, { headers: AUTH }); if (!res.ok) throw new KreatorFlowError(res.status, "download_failed", await res.text()); await pipeline(Readable.fromWeb(res.body), createWriteStream(path)); return path; } const prompt = process.argv.slice(2).join(" ") || "A paper boat drifting down a rainy street at dusk, low angle"; try { const job = await waitForJob(await createVideo(prompt)); if (job.status === "completed") console.log("saved", await download(job.id, `${job.id}.mp4`)); else console.error(job.status, job.errorCode, job.errorMessage); } catch (err) { console.error("request failed:", err.message); process.exitCode = 1; }

How do I send an image-to-video job from Python or Node.js?

Use the same clients and add one field: first_frame_url with a public HTTPS image, and a prompt that describes the motion. With aspect_ratio set to adaptive, the clip keeps the shape of the picture. Add last_frame_url to set the closing frame. Images are not billed, so the clip costs the same as text to video.

For a file on your own disk, upload it first with POST /v1/uploads and send first_frame_upload_id instead of the URL; the image-to-video guide has the three upload calls in both languages.

Image to video (Python and Node.js)

# Python: animate a picture (same client, one extra field) from kreatorflow_video import create_video, download, wait_for_job job = wait_for_job(create_video( "The camera slowly pushes in. Waves roll against the rocks and the lighthouse beam sweeps the sea.", aspect_ratio="adaptive", first_frame_url="https://example.com/lighthouse.jpg", # a public HTTPS image )) if job["status"] == "completed": download(job["id"], "lighthouse.mp4") // Node.js: the same job with the ES module client import { createVideo, download, waitForJob } from "./kreatorflow-video.mjs"; const job = await waitForJob(await createVideo("The camera slowly pushes in. Waves roll against the rocks.", { aspectRatio: "adaptive", extra: { first_frame_url: "https://example.com/lighthouse.jpg" }, })); if (job.status === "completed") await download(job.id, "lighthouse.mp4");

How do I generate many videos at once?

Submit every job first, then wait for them together: the batch then takes about as long as its slowest clip, not the sum of all of them. POST /v1/videos allows 20 requests per 60 seconds per account, so pace larger batches. Drafts on the Fast model cost less; render the keepers on Seedance 2.0 with the same prompt.

Python batch

# batch.py: submit several prompts at once, then collect every clip. from concurrent.futures import ThreadPoolExecutor from kreatorflow_video import create_video, download, wait_for_job prompts = [ "A red fox trotting through fresh snow at sunrise, side tracking shot", "Steam rising from a bowl of ramen, slow push in, warm light", "A skateboarder kickflips over a puddle at golden hour, low angle", ] jobs = [create_video(p, model="kreator-video-seedance-2-fast") for p in prompts] # submit all first with ThreadPoolExecutor(max_workers=len(jobs)) as pool: for job in pool.map(wait_for_job, jobs): if job["status"] == "completed": print("saved", download(job["id"], f"{job['id']}.mp4")) else: print(job["id"], job["status"], job.get("errorCode"))

Does the Seedance API support webhooks?

Yes. Add webhook_url to the create request and KreatorFlow sends a signed POST when the job reaches its final status, with type job.completed, job.failed, job.canceled or job.blocked and the job in data.job. The headers are X-KreatorFlow-Event-Id, X-KreatorFlow-Event-Type, X-KreatorFlow-Job-Id, X-KreatorFlow-Timestamp and X-KreatorFlow-Signature.

X-KreatorFlow-Signature looks like t=<unix time>,v1=<hex>. Recompute HMAC-SHA256 of "<t>.<raw body>" with the signing secret from the console's Webhooks section, compare in constant time, reject old timestamps, and store each event id so you handle every event once. Reply 2xx quickly and do the download in the background.

Python (Flask)

# webhook.py: receive the signed "job finished" call instead of polling. # pip install flask Run: flask --app webhook run --port 8000 import hashlib import hmac import os import time from flask import Flask, abort, request app = Flask(__name__) SECRET = os.environ["KREATORFLOW_WEBHOOK_SECRET"].encode() seen = set() # use your database in production @app.post("/kreatorflow/webhook") def kreatorflow_webhook(): raw = request.get_data(as_text=True) sig = request.headers.get("X-KreatorFlow-Signature", "") parts = dict(p.split("=", 1) for p in sig.split(",") if "=" in p) expected = hmac.new(SECRET, f'{parts.get("t", "")}.{raw}'.encode(), hashlib.sha256).hexdigest() fresh = abs(time.time() - int(parts.get("t", "0"))) < 300 if not (fresh and hmac.compare_digest(expected, parts.get("v1", ""))): abort(401) event = request.get_json() if event["id"] not in seen: seen.add(event["id"]) job = event["data"]["job"] print(event["type"], job["id"], job["status"]) # queue the download here return "ok", 200

How do I verify the webhook in Node.js?

Read the raw body before parsing it: the signature covers the exact bytes that were sent. This handler uses only node:http and node:crypto.

Node.js (node:http)

// webhook.mjs: receive the signed "job finished" call (no framework needed). import { createHmac, timingSafeEqual } from "node:crypto"; import { createServer } from "node:http"; const SECRET = process.env.KREATORFLOW_WEBHOOK_SECRET; const seen = new Set(); // use your database in production function verify(raw, header = "") { const parts = Object.fromEntries(header.split(",").map((part) => part.split("="))); const expected = createHmac("sha256", SECRET).update(`${parts.t}.${raw}`).digest("hex"); const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; return fresh && typeof parts.v1 === "string" && parts.v1.length === expected.length && timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected)); } createServer(async (req, res) => { if (req.method !== "POST" || req.url !== "/kreatorflow/webhook") return res.writeHead(404).end(); let raw = ""; for await (const chunk of req) raw += chunk; if (!verify(raw, req.headers["x-kreatorflow-signature"])) return res.writeHead(401).end("bad signature"); const event = JSON.parse(raw); if (!seen.has(event.id)) { seen.add(event.id); console.log(event.type, event.data.job.id, event.data.job.status); // queue the download here } res.writeHead(200).end("ok"); }).listen(8000);

Which errors should my code handle?

Every error is JSON with success: false, a code and a readable error. Branch on code, never on the message text. A 4xx request error (the table below) means no job was created and nothing was charged: fix the request, then send it again. For internal_error, resending with the same Idempotency-Key is safe.

A job that starts and then fails ends with status failed, canceled or blocked, and carries errorCode and errorMessage. The tokens held for it are given back. Change the prompt or reference if the error points at it, then create a new job.

  • Check success on every response, not only the HTTP status
  • Stop and alert on invalid_api_key, scope_missing and insufficient_balance: resending will not help
  • Wait for Retry-After on 429 before the next call
  • Treat failed, canceled and blocked as final and log errorCode with the job id

How much does each call cost?

Video is billed per second of output at a rate set by model and resolution, and every create response carries the exact quote before the job runs. A 5-second Seedance 2.0 clip costs $0.55 at 720p and $1.20 at 1080p. The same clip on Seedance 2.0 Fast is $0.45 at 720p. One token is $0.05; top-ups start at $20. Prices as of 18 October 2026.

The cheapest Seedance 2.5, Seedance 2.0 and Seedance 2.0 Fast API at 720p and 1080p: At our list rate, a 5-second Seedance 2.5, Seedance 2.0 and Seedance 2.0 Fast video at 720p or 1080p costs less on the KreatorFlow API than the regular list price of any of the 12 other API providers we track (each provider's own page, checked 25 October 2026 to 2 November 2026). Seedance 2.0 Mini is not included: another provider lists it for the same or less at 720p.

Volume pricing for the Seedance API

The more you top up, the less Seedance video costs. Your tier comes from the token top-ups you paid for in the last 30 days. It lowers the per-second price of Seedance video and raises your video request limit, automatically, with no contract and no sales call. If your top-ups drop, the tier moves back down.

Every job's quote shows your price before it runs; a few low-priced settings get a smaller discount. Smaller top-ups add up toward a tier too. Price comparisons on this site use list prices unless a price is marked as at volume. Need more volume? Custom pricing for committed monthly spend: email harry@kreatorflow.ai.

  • Standard: Any · List price · 20 video requests / minute
  • Scale: $1,000+ · up to 5% off · 60 video requests / minute
  • Growth: $2,500+ · up to 7.5% off · 120 video requests / minute
  • Enterprise: $5,000+ · up to 10% off · 300 video requests / minute

Checked 25 October 2026 to 2 November 2026

The cheapest Seedance 2.5, Seedance 2.0 and Seedance 2.0 Fast API at 720p and 1080p

At our list rate, a 5-second Seedance 2.5, Seedance 2.0 and Seedance 2.0 Fast video at 720p or 1080p costs less on the KreatorFlow API than the regular list price of any of the 12 other API providers we track (each provider's own page, checked 25 October 2026 to 2 November 2026). Seedance 2.0 Mini is not included: another provider lists it for the same or less at 720p.

Frequently asked questions

Is there an official Seedance Python SDK?

You do not need one. The KreatorFlow API is plain REST and JSON, so the requests library (or httpx) is enough; the client in this guide is under 100 lines.

Which Node.js version do I need?

Node 18 or later, which has fetch, crypto.randomUUID and web streams built in. Older versions need a fetch package such as undici.

How long does a Seedance video take to generate?

Usually a few minutes, longer for long or high-resolution clips. Poll every 10 seconds or so, or use a webhook; never hold a browser request open while it renders.

How do I avoid paying twice if a request times out?

Send an Idempotency-Key header with each create call and reuse it when you resend that same request. The API returns the job it already created, marked idempotent_replay: true.

Can I call the Seedance API from the browser?

No. The key is a secret. Call the API from your server or a serverless function and give your users the finished file or your own link to it.

How do I download the finished video?

GET /v1/jobs/{id}/output with the same Bearer key streams the file. Save it to your own storage: the output link needs your key and does not last forever.

Can I use image or video references from code?

Yes. Send public HTTPS URLs in reference_image_urls, reference_video_urls or reference_audio_urls, or upload files first with POST /v1/uploads and pass the upload ids. The quickstart covers the upload flow.

Is there volume pricing for the Seedance API?

Yes. Once your paid top-ups in the last 30 days reach $1,000, Seedance video costs up to 5% less per second, rising to up to 10% off at $5,000 (Enterprise), and your video request limit goes up with each tier. It applies automatically, with no contract, and each job's quote shows your price before it runs. For more volume than that, email harry@kreatorflow.ai.

Your first clip from code

Create a key, top up from $20 and run the script. Every job is quoted before it runs.

More guides