Seedance 2.5 z poziomu kodu - curl, Python, webhook i obsługa błędów
Seedance 2.5 przez API: zlecenie wideo w curl i Pythonie, odbiór pliku MP4, webhook z podpisem HMAC, koszt za sekundę i kody błędów, które warto obsłużyć.
Do generowania wideo z Seedance 2.5 w skrypcie potrzebujesz dwóch wywołań. POST /v1/generate z modelem seedance-2.5 przyjmuje zlecenie i oddaje identyfikator zadania, a GET /v1/tasks/{id} po minucie lub kilku zwraca link do pliku MP4. Płacisz od 1,51 zł za sekundę za każdą sekundę gotowego filmu. W tym poradniku masz przykłady w curl i Pythonie, przeliczenie kosztów, odbiornik webhooka i tabelę błędów, z którymi sam się zetknąłem.
Co dostajesz w Seedance 2.5
Model pochodzi z ByteDance Seed i został pokazany 31 lipca 2026. Producent wymienia trzy główne cechy:
- do 30 sekund filmu w jednym przebiegu,
- dźwięk powstający razem z obrazem (efekty, mowa, muzyka),
- szerokie wejście referencyjne: według ogłoszenia ByteDance jedno zadanie przyjmie do 30 obrazów, 10 filmów i 10 ścieżek audio.
Dochodzi do tego edycja gotowego wideo i przedłużanie klipu w kolejnych rundach.
Dla mnie liczy się przede wszystkim długość. Przy wersji 2.0 rolkę na 25 sekund trzeba było składać z kilku ujęć i pilnować, żeby postać między cięciami nie zmieniła twarzy. Z 2.5 cała rolka może być jednym ujęciem.
Rozdzielczości nie obiecuję na podstawie zapowiedzi, bo oficjalne materiały ByteDance jej nie podają, a różne serwisy oferują 480p, 720p, czasem 1080p. Dozwolone wartości w Kadrarisie odczytasz ze schematu modelu:
curl https://kadraris.com/v1/models/seedance-2.5Interesuje cię pole input_schema, w którym są dopuszczalne resolution, aspect_ratio i zakres duration. Ten adres jest publiczny i działa bez klucza.
Koszt za sekundę
Seedance 2.5 kosztuje od 1,51 zł za sekundę. Liczy się każda sekunda wygenerowanego filmu, więc cena klipu to stawka razy długość:
| Klip | Wzór | Do czego |
|---|---|---|
| 5 s | 5 × od 1,51 zł za sekundę | sprawdzenie promptu, przebitka |
| 10 s | 10 × od 1,51 zł za sekundę | krótkie ujęcie do reklamy |
| 15 s | 15 × od 1,51 zł za sekundę | rolka z jednego ujęcia |
| 30 s | 30 × od 1,51 zł za sekundę | cała rolka za jednym razem |
Stawka w tabeli dotyczy 480p. Domyślne 720p kosztuje za sekundę ok. 2,2 raza więcej, a 1080p ok. 5,4 raza więcej, więc 10 s w 720p to ok. 34 zł.
Na koncie polskim kwoty są w złotówkach brutto (VAT 23%), a fakturę VAT zaznaczasz przy doładowaniu. Bieżące stawki modeli wideo są w cenniku, a różnice między odmianami Seedance opisałem w poradniku Seedance 2.0 czy 2.5.
Na końcowy rachunek mocniej niż stawka wpływają dwie sprawy:
- Porażka nic nie kosztuje. Zadanie ze statusem
failedzwalnia całą blokadę, a jegocostwynosi 0. - Nieudany pomysł już tak. Film, który się wygenerował, ale ci nie pasuje, jest udaną generacją. Dlatego próbuję prompty na 5 sekundach, a pełną długość puszczam na końcu.
Cenę znasz przed startem. Odpowiedź na POST /v1/generate zawiera estimated_cost, a tyle zostaje zablokowane na saldzie, dopóki zadanie się nie zakończy.
Pierwsze zlecenie z terminala
Najpierw konto i klucz. Rejestracja trwa chwilę, najmniejsze doładowanie to 40 zł, a klucz (sk-kd-...) generujesz w panelu. Krok po kroku opisuje to szybki start.
Klucz wkładam do zmiennej środowiskowej:
export KADRARIS_API_KEY="sk-kd-..."Zlecenie pięciosekundowego klipu:
curl https://kadraris.com/v1/generate \
-H "Authorization: Bearer $KADRARIS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.5",
"input": {
"prompt": "A fisherman pulls a net onto a wooden boat at dawn, slow handheld camera, mist over the water, waves and seagulls",
"duration": 5,
"resolution": "720p",
"aspect_ratio": "16:9",
"generate_audio": true
}
}'Odpowiedź przychodzi natychmiast, z kodem 202:
{
"id": "task_8f2k...",
"object": "task",
"status": "queued",
"model": "seedance-2.5",
"estimated_cost": { "amount": "...", "currency": "PLN" },
"poll_url": "https://kadraris.com/v1/tasks/task_8f2k..."
}A tak sprawdzasz, co się dzieje z zadaniem:
curl https://kadraris.com/v1/tasks/task_8f2k... \
-H "Authorization: Bearer $KADRARIS_API_KEY"Kiedy status przejdzie w succeeded, w output pojawi się link do pliku. Działa ograniczony czas, więc ściągaj od razu.
Skrypt w Pythonie od zlecenia do pliku
Ten kod zleca film, czeka na wynik z coraz dłuższymi przerwami między zapytaniami i zapisuje MP4. Na nim opieram większość własnych prób.
import os
import time
import pathlib
import requests
API = "https://kadraris.com/v1"
KEY = os.environ["KADRARIS_API_KEY"]
HEADERS = {"Authorization": f"Bearer {KEY}"}
def generate_video(prompt, duration=5, aspect_ratio="16:9",
resolution="720p", image_url=None):
payload = {
"model": "seedance-2.5",
"input": {
"prompt": prompt,
"duration": duration,
"resolution": resolution,
"aspect_ratio": aspect_ratio,
"generate_audio": True,
},
}
if image_url:
payload["input"]["image_url"] = image_url
r = requests.post(f"{API}/generate", headers=HEADERS,
json=payload, timeout=30)
if r.status_code != 202:
msg = r.json()["error"]["message"]
raise RuntimeError(f"{r.status_code}: {msg}")
task = r.json()
print("id:", task["id"], "szacunek:", task["estimated_cost"])
return task
def wait_for_task(task_id, timeout=900):
# przerwy rosną od 3 do 20 sekund
delay, deadline = 3, time.time() + timeout
while time.time() < deadline:
task = requests.get(f"{API}/tasks/{task_id}",
headers=HEADERS, timeout=30).json()
if task["status"] in ("succeeded", "failed"):
return task
time.sleep(delay)
delay = min(delay * 1.5, 20)
raise TimeoutError(task_id)
def download(task, folder="wideo"):
pathlib.Path(folder).mkdir(exist_ok=True)
saved = []
for i, item in enumerate(task["output"]):
path = pathlib.Path(folder) / f"{task['id']}_{i}.mp4"
with requests.get(item["url"], stream=True,
timeout=180) as r:
r.raise_for_status()
with open(path, "wb") as f:
for chunk in r.iter_content(1 << 20):
f.write(chunk)
saved.append(path)
return saved
if __name__ == "__main__":
task = generate_video(
"Close-up of hands kneading dough on a floured "
"table, warm kitchen light, slow push-in, "
"soft ambient sound",
duration=5,
)
done = wait_for_task(task["id"])
if done["status"] == "succeeded":
print("koszt:", done["cost"], "pliki:", download(done))
else:
print("porażka, 0 zł:", done["error"])Co warto wiedzieć o tym kodzie:
- Przy
POSTwystarczytimeout=30. Serwer tylko przyjmuje zlecenie i nie czeka na film. - Odpytywanie zaczyna się co 3 sekundy i zwalnia do 20. Częstsze sprawdzanie przy wideo nic nie daje.
generate_audio: Truewłącza dźwięk generowany razem z obrazem. Jeśli podkład dodajesz w montażu, dajFalse.?wait=60działa też przy wideo, ale Seedance zwykle potrzebuje ponad minuty, więc tu z niego nie korzystam. Przy grafikach to wygodny skrót, o którym piszę w poradniku o generowaniu obrazów w Pythonie.
Pierwsza klatka z obrazu
Gdy zależy mi na konkretnym kadrze, np. produkcie w danym ustawieniu albo postaci z wcześniejszej grafiki, przekazuję obraz w image_url. Plik musi być dostępny pod publicznym adresem HTTPS.
task = generate_video(
prompt="The camera slowly orbits the bottle, light "
"reflections move across the glass, "
"condensation drops run down",
duration=5,
aspect_ratio="9:16",
image_url="https://twoja-domena.pl/pliki/butelka.png",
)Klatkę startową robię zwykle w Nano Banana Pro albo GPT Image 2.5 i dopiero ją animuję. Tak wychodzi taniej: obraz kosztuje mniej niż sekunda filmu, więc kompozycję poprawiam na grafikach, a do Seedance idzie gotowy kadr. Koszty całego łańcucha rozpisałem w tekście ile kosztuje rolka z AI.
Powiadomienie zamiast pętli
Przy kilkunastu równoległych zadaniach odpytywanie robi się kłopotliwe. Dodaję wtedy webhook_url. Po zakończeniu zadania Kadraris wysyła na ten adres POST z tym samym JSON-em, który zwraca GET /v1/tasks/{id}.
curl https://kadraris.com/v1/generate \
-H "Authorization: Bearer $KADRARIS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.5",
"input": { "prompt": "Rain on a neon street at night, slow dolly", "duration": 10, "aspect_ratio": "9:16" },
"webhook_url": "https://twoj-serwer.pl/kadraris-hook"
}'Każde powiadomienie niesie nagłówek x-kadraris-signature, czyli podpis HMAC-SHA256 treści żądania. Sekret znajdziesz w panelu. Najprostszy odbiornik we Flasku:
import hmac, hashlib, os
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = os.environ["KADRARIS_WEBHOOK_SECRET"].encode()
@app.post("/kadraris-hook")
def hook():
body = request.get_data()
expected = hmac.new(SECRET, body,
hashlib.sha256).hexdigest()
got = request.headers.get("x-kadraris-signature", "")
# odrzuć wszystko bez poprawnego podpisu
if not hmac.compare_digest(expected, got):
abort(401)
task = request.get_json()
if task["status"] == "succeeded":
print("plik:", task["output"][0]["url"])
return "", 204Format podpisu i zasady ponawiania opisuje dokumentacja webhooków.
Jak piszę prompty pod Seedance 2.5
Po kilkudziesięciu generacjach trzymam się kilku reguł:
- Angielski. Polski opis model zrozumie, ale po angielsku ruch kamery wychodzi mi bardziej przewidywalnie.
- Kamera nazwana wprost: "slow push-in", "handheld", "static tripod shot", "orbit". Bez tego model sam wymyśla ruch i zwykle przesadza.
- Dźwięk w opisie. Przy
generate_audio: truedopisuję na koniec, co słychać, np. "waves and seagulls" albo "quiet cafe chatter". - Dialog w cudzysłowie, po angielsku. Polską mowę w modelach wideo traktuję jak loterię, a lektora nagrywam osobno.
- Jedna akcja na ujęcie. Pół minuty kusi, żeby zmieścić całą historię. Lepiej sprawdza się jedna czynność opisana dokładnie.
Więcej o budowie opisu ujęcia znajdziesz w poradniku prompty do wideo AI.
Kody błędów
| Kod | Przyczyna | Moja reakcja |
|---|---|---|
| 401 | brak klucza albo zły klucz | sprawdzam KADRARIS_API_KEY |
| 402 | saldo mniejsze niż estimated_cost | doładowuję, BLIK wpływa od razu |
| 404 | literówka w nazwie modelu | poprawiam na seedance-2.5 |
| 422 | parametr spoza schematu, np. rozdzielczość | czytam error.message, porównuję z input_schema |
| 429 | za dużo zapytań | odczekuję i ponawiam z opóźnieniem |
| 502 | błąd po stronie dostawcy modelu | próbuję później |
Zadanie, które zostało przyjęte i dopiero potem padło, nie daje błędu HTTP. Ma status: "failed" i obiekt error. Komplet kodów jest w dokumentacji błędów.
Kiedy inny model
Seedance 2.5 biorę, gdy potrzebuję długiego ujęcia z dźwiękiem albo wielu referencji. Do krótkich, filmowych ujęć z dopracowanym światłem sprawdzam też Veo 3.1 (Quality), a przy scenach z kilkoma ujęciami Kling 3.0. Tabelę parametrów i cen znajdziesz w porównaniu API do wideo AI. Jeśli wolisz klikać, ten sam model i to samo saldo masz w Kadraris Studio.
Pytania
Czy mogę użyć Seedance 2.5 Eco w tym samym skrypcie?
Tak. Pola są takie same, więc zmieniasz tylko "model": "seedance-2.5" na "seedance-2.5-eco". Eco ma węższą listę rozdzielczości i niższą stawkę (od 2,54 zł za sekundę), więc sprawdź dozwolone wartości w jego schemacie.
Jak ustalić maksymalną długość klipu dostępną przez API?
Odczytaj input_schema z GET /v1/models/seedance-2.5. Pole duration ma tam minimum i maksimum. Producent podaje do 30 sekund w jednym przebiegu.
Co się stanie, jeśli skrypt przerwie się w trakcie czekania?
Zadanie dalej się renderuje. Zachowaj id i wywołaj samą funkcję wait_for_task po restarcie, zamiast zlecać film od nowa. Podwójne zlecenie to dwie płatne generacje.
Czy webhook przyjdzie także po nieudanym zadaniu?
Tak, powiadomienie dotyczy zakończenia zadania, więc dostaniesz je ze statusem failed i obiektem error. Taka generacja kosztuje 0 zł.


























