Dokumentation
Videogenerierung
Text-zu-Video und Bild-zu-Video mit der Veo 3.1-Familie von Google. Zwei API-Formen: ein synchroner Einzelaufruf-Endpunkt, der wartet, bis der Clip fertig ist, sowie ein Paar aus Einreichungs- und Abruf-Endpunkt für asynchrone Produktions-Workloads.
Endpunkte: POST /v1/video/generations (synchron, zuerst beschrieben) sowie POST /v1/videos + GET /v1/videos/{id} (asynchron, für den Produktionseinsatz empfohlen). Die Generierung dauert je nach Modell und Dauer 30 Sekunden bis mehrere Minuten. Setzen Sie beim synchronen Endpunkt ein großzügiges Client-Timeout (600s sind sicher).
Text-zu-Video
Übergeben Sie einen prompt und wählen Sie ein Modell. aspect_ratio ist optional.
import requests
resp = requests.post(
"https://api.kunavo.com/v1/video/generations",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "veo-3",
"prompt": "a cinematic dolly-in on a red origami crane unfolding, soft light",
"aspect_ratio": "16:9",
},
timeout=600, # video generation can take minutes
)
print(resp.json()["data"][0]["url"])duration (in Sekunden) legt die Cliplänge bei den Modellen mit sekundengenauer Abrechnung fest: den Seedance-Modellen und Wan 2.7. Die Veo-Modelle ignorieren den Wert; jeder Veo-Clip ist 8 Sekunden lang und wird pro Video abgerechnet.
# The models billed per second take a clip length: here 5 seconds of
# Seedance 2. Leave "duration" out and these models make a 5-second clip.
resp = requests.post(
"https://api.kunavo.com/v1/video/generations",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "seedance-2",
"prompt": "a paper crane unfolds into a flock of birds over a city at dawn",
"duration": 5,
"aspect_ratio": "16:9",
},
timeout=600,
)
print(resp.json()["data"][0]["url"])Bild-zu-Video
Fügen Sie ein image_url hinzu, und Veo animiert es. Standardmäßig wird das Standbild zum ersten Frame des Videos.
# Image-to-video: animate a still image. The source image can be an
# https URL, or inline base64 — Kunavo hosts it for the model automatically.
resp = requests.post(
"https://api.kunavo.com/v1/video/generations",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "veo-3",
"prompt": "the crane gently lifts off the desk",
"image_url": "https://your-cdn.com/source.png",
},
timeout=600,
)
print(resp.json()["data"][0]["url"])image_url akzeptiert eine https://-URL oder einen Inline-data:-Base64-URI. Base64 wird dekodiert und automatisch auf Kunavos CDN gehostet, bevor das Modell ausgeführt wird.Erstes Frame, letztes Frame und Referenzen
image_mode legt fest, wie Veo die Bilder verwendet. Übergeben Sie image_urls (ein Array), um mehr als ein Bild zu senden.
frame(Standard) — ein Bild dient als erstes Frame; zwei Bilder werden zum ersten und letzten Frame, und Veo generiert den Übergang dazwischen.reference— bis zu drei Bilder dienen als Stil- und Motivvorlagen. Sie beeinflussen das Erscheinungsbild, erscheinen aber nie als Frame.
# First + last frame: pass two images and Veo fills in the motion.
resp = requests.post(
"https://api.kunavo.com/v1/video/generations",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "veo-3",
"prompt": "a smooth transition between the two poses",
"image_urls": [
"https://your-cdn.com/first.png",
"https://your-cdn.com/last.png",
],
"image_mode": "frame",
},
timeout=600,
)Ein Quell-Frame hochladen
Wenn Sie nur eine lokale Datei haben, laden Sie sie einmal über POST /v1/files hoch und verwenden Sie die zurückgegebene dauerhafte URL wieder.
# No public URL? Upload the still first, then animate it.
with open("frame.png", "rb") as f:
up = requests.post(
"https://api.kunavo.com/v1/files",
headers={"Authorization": f"Bearer {API_KEY}"},
files={"file": f},
).json()
resp = requests.post(
"https://api.kunavo.com/v1/video/generations",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "veo-3",
"prompt": "slow push-in, volumetric light",
"image_url": up["url"],
},
timeout=600,
)Parameter
| Parameter | Typ | Hinweise |
|---|---|---|
model | String (erforderlich) | Ein Video-Modell-Slug aus /v1/models. |
prompt | String (erforderlich) | Textbeschreibung der Aufnahme. |
image_url | string | Einzelnes Quellbild für Bild-zu-Video. URL oder data:-URI. |
image_urls | string[] | Mehrere Quellbilder (Veo). Hat Vorrang vor image_url. |
image_mode | string | Veo: „frame“ (Standard – erstes/ |
duration | int | Cliplänge in Sekunden bei den Modellen mit sekundengenauer Abrechnung, Standardwert 5: Seedance 2, Seedance 2 Fast und Seedance 2.0 Mini akzeptieren 4–15, Seedance 2.5 akzeptiert 4–30, Wan 2.7 akzeptiert 2–15. Eine Länge außerhalb des Bereichs eines Modells wird mit einem 400-Fehler abgelehnt. Veo ignoriert den Wert: Jeder Veo-Clip ist 8 Sekunden lang. |
aspect_ratio | string | z. B. „16:9“, „9:16“, „1:1“. |
resolution | string | Wenn das Modell Qualitätsstufen anbietet – z. B. „720p“, „1080p“. |
input | object | Ausweichmöglichkeit — siehe unten Raw input. |
Direkte Übergabe von Rohparametern
Bei den Modellen mit sekundengenauer Abrechnung wird input unverändert an den Upstream gesendet und ersetzt die Eingabe, die unser Adapter erstellen würde. Die Veo-Modelle ignorieren es.
Eine Ausnahme bilden die preisbestimmenden Felder: resolution wird stets als eine der Stufen des Modells gesendet (als Standardstufe, wenn es weggelassen wird oder keiner dieser Stufen entspricht), und duration muss eine ganzzahlige Anzahl von Sekunden innerhalb des Bereichs des Modells sein (jeder andere Wert wird mit einem 400-Fehler abgelehnt; wird es weggelassen, wird 5 gesendet). Die Felder resolution und duration der Anfrage selbst werden nicht ausgelesen, wenn input gesetzt ist.
Videomodelle
| Slug | Anbieter | Hinweise |
|---|---|---|
veo-3 | Veo 3.1 Fast — schneller und günstiger als Quality, native Audioausgabe. | |
veo-3-quality | Veo 3.1 Quality — höchste Wiedergabetreue. | |
veo-3-lite | Veo 3.1 Lite — kostengünstige Stufe für Vorschauen. | |
seedance-2-5 | ByteDance | Seedance 2.5 — Clips bis zu 30s, multimodale Referenzen, sekundengenaue Abrechnung. |
seedance-2 | ByteDance | Seedance 2 — Konsistenz über mehrere Einstellungen hinweg, bis zu 1080p, sekundengenaue Abrechnung. |
seedance-2-fast | ByteDance | Seedance 2 Fast — kostengünstige Stufe, bis zu 720p, sekundengenaue Abrechnung. |
seedance-2-mini | ByteDance | Seedance 2.0 Mini — günstigste Stufe, nur 480p/720p, für Entwürfe und hohe Stückzahlen. |
wan-2-7 | Alibaba | Wan 2.7 — filmische Text-zu-Video-Generierung, sekundengenaue Abrechnung. |
Prüfen Sie immer /models auf die aktuelle Modellliste und die Preise pro Modell.
Latenz & Zeitüberschreitungen
- Kunavo fragt den Upstream-Auftrag bis zu 540 Sekunden lang ab, bevor ein 504 zurückgegeben wird.
- Schnelle Stufen (Veo 3.1 Fast / Lite): oft 30–90s.
- Qualitätsstufen und längere Laufzeiten: 2–5 Minuten.
- Setzen Sie das Timeout Ihres HTTP-Clients auf mindestens 600s.
- Bei instabilen Netzwerken oder mobilen Clients empfiehlt sich stattdessen die asynchrone API – die Einreichung wird in Sekunden beantwortet, und Sie fragen das Ergebnis anschließend ab.
Asynchrone API (Submit + Poll)
Endpunkte: POST /v1/videos + GET /v1/videos/{id}. Das zurückgegebene Aufgabenobjekt verwendet die Feldnamen und Statuswerte des Videoobjekts von OpenAI und enthält zusätzlich output mit der URL des Clips. Die Anfrage besteht aus einem JSON-Body mit den oben genannten Parametern.
Der synchrone Endpunkt oben hält eine HTTP-Verbindung bis zu neun Minuten lang offen. Das ist praktisch für Skripte mit einem einzelnen Aufruf, auf Mobilgeräten jedoch unzuverlässig und auf dem Server ineffizient. Das asynchrone Endpunktpaar gibt sofort eine vid_*-ID zurück. Fragen Sie GET /v1/videos/{id} ab, bis status den Status completed oder failed hat. Dieselben Modelle, Preise und archivierten URLs.
import requests, time
# 1. Submit — returns immediately with a task id. No long-lived connection.
submit = requests.post(
"https://api.kunavo.com/v1/videos",
headers={
"Authorization": f"Bearer {API_KEY}",
# Optional: retrying with the same key inside ~24h returns the
# original task rather than submitting again.
"Idempotency-Key": "my-task-uuid",
},
json={
"model": "veo-3",
"prompt": "a cinematic dolly-in on a red origami crane",
"aspect_ratio": "16:9",
},
timeout=60,
).json()
task_id = submit["id"] # "vid_abc..."
status = submit["status"] # "queued"
# 2. Poll until terminal. Recommended cadence: 5s, backing off to 30s.
while status not in ("completed", "failed"):
time.sleep(5)
r = requests.get(
f"https://api.kunavo.com/v1/videos/{task_id}",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
).json()
status = r["status"]
if status == "failed":
raise RuntimeError(r["error"]["message"])
print(r["output"]["url"])Mit dem OpenAI-SDK senden Sie diesen Body wie unten gezeigt mit client.post und fragen den Status mit client.get ab. Die SDK-eigene Methode client.videos.create funktioniert hier nicht: Die Methode akzeptiert nur die Sora-Parameter und sendet sie als Multipart-Formular (im Python-SDK seit Version 2.27.0); diese Multipart-Anfrage weist dieser Endpunkt mit einem 400-Fehler zurück.
import time
from openai import OpenAI
client = OpenAI(api_key=API_KEY, base_url="https://api.kunavo.com/v1")
# The same JSON body, sent with the client's own post().
task = client.post(
"/videos",
body={
"model": "veo-3",
"prompt": "a cinematic dolly-in on a red origami crane",
"aspect_ratio": "16:9",
},
# object hands back the JSON as parsed. Passing dict instead raises
# after the task has been created.
cast_to=object,
options={"headers": {"Idempotency-Key": "my-task-uuid"}},
)
while task["status"] not in ("completed", "failed"):
time.sleep(5)
task = client.get(f"/videos/{task['id']}", cast_to=object)
if task["status"] == "failed":
raise RuntimeError(task["error"]["message"])
print(task["output"]["url"])Antwortformat
| Feld | Typ | Hinweise |
|---|---|---|
id | string | Aufgaben-ID mit Präfix vid_. Bleibt über Änderungen am Upstream hinweg stabil. |
object | string | "video". |
status | string | queued | in_progress | completed | failed. |
model | string | Der übermittelte Modell-Slug. |
created_at | int | Unix-Zeit in Sekunden, zu der wir die Übermittlung angenommen haben. |
completed_at | int | null | Unix-Zeit in Sekunden beim Abschluss — bei ausstehenden Aufgaben null. |
expires_at | int | Unix-Sekunden, 30 Tage nach dem Absenden: Mindestens bis dahin lässt sich die Aufgabe abfragen. Die Ergebnis-URLs der Aufgabe laufen nicht ab. |
progress | int | 0–100. Aktuell geben wir bis zum Abschluss 0 und danach 100 zurück. |
output | object | null | {url, urls[], duration_ |
error | object | null | {code, message} bei fehlgeschlagenem Vorgang. |
Header und Konventionen
Idempotency-Key(optional, bis zu 128 Zeichen) — bei zweimaliger Einreichung desselben Schlüssels für dasselbe Konto wird die ursprüngliche Aufgabe zurückgegeben, statt ein Duplikat zu erstellen. Nützlich für Wiederholungsschleifen.POST /v1/videosgibt bei einer neuen Einreichung 202 Accepted zurück und 200 OK, wenn ein Idempotenzschlüssel ein vorheriges Ergebnis erneut abruft.GET /v1/videos/{id}ist an den Eigentümer gebunden: Wird eine Aufgabe abgefragt, die zu einem anderen Konto gehört, wird 404 zurückgegeben. Wir bestätigen niemals, dass eine ID für jemand anderen existiert.- Empfohlenes Abfrageintervall: 5 s, mit exponentiellem Backoff bis zu 30 s. Je nach Modell und Auflösung sind Aufgaben üblicherweise nach 30 s – 5 min abgeschlossen.
Wann welche Variante verwenden
| Synchron /v1/ | Asynchron /v1/videos | |
|---|---|---|
| Am besten geeignet für | Einmalige Skripte, Notebooks | Produktion, Mobilgeräte, lang laufende Aufträge |
| Netzwerk | Hält die Verbindung bis zu 9 min offen | In Sekunden einreichen; nach Ihrem Zeitplan abfragen |
| Fehlerbehebung | Antwort verloren = Aufgabe verloren | ID jederzeit vor Ablauf von expires_at erneut abfragen |
| Parallelität | Ein Server-Worker pro Aufruf | Gibt den Worker nach der Einreichung sofort frei |
| Antwortformat | Form von OpenAI /images/ | Form von OpenAI /videos |
Antwort und Speicherung
Die Antwort hat das Standardformat von OpenAI — data[].url. Die URL ist ein dauerhafter files.kunavo.com-Link: Kunavo archiviert jedes Ergebnis auf seinem eigenen CDN, sodass die URL im Gegensatz zu rohen Upstream-URLs nie abläuft. Jeder Clip erscheint außerdem unter /app/assets.