Documentation

Documentation

Génération vidéo

Génération de vidéos à partir de texte et d’images avec la gamme Veo 3.1 de Google. Deux formats d’API : un point de terminaison synchrone qui bloque jusqu’à ce que la vidéo soit prête, et une paire asynchrone pour soumettre une tâche puis interroger son état, destinée aux charges de travail en production.

Points de terminaison : POST /v1/video/generations (synchrone, présenté en premier) et POST /v1/videos + GET /v1/videos/{id} (asynchrone, recommandé pour la production). La génération dure de 30 secondes à plusieurs minutes selon le modèle et la durée. Pour le point de terminaison synchrone, définissez un délai d’attente client généreux (600s convient).

Génération de vidéo à partir de texte

Transmettez un prompt et choisissez un modèle. aspect_ratio est facultatif.

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 (en secondes) définit la durée du clip pour les modèles facturés à la seconde : les modèles Seedance et le modèle Wan 2.7. Les modèles Veo l’ignorent ; chaque clip Veo dure 8 secondes et est facturé par vidéo.

# 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"])

Génération de vidéo à partir d’images

Ajoutez un image_url et Veo l’anime — par défaut, l’image fixe devient la première image de la vidéo.

# 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 accepte une URL https:// ou un URI base64 data: intégré. Le contenu base64 est automatiquement décodé et hébergé sur le CDN de Kunavo avant l’exécution du modèle.

Première image, dernière image et références

image_mode détermine comment Veo utilise les images. Transmettez image_urls (un tableau) pour envoyer plusieurs images.

  • frame (par défaut) — une image sert de première image ; deux images servent de première et de dernière image, et Veo génère la transition entre les deux.
  • reference — jusqu’à trois images servent de références de style et de sujet. Elles orientent l’apparence, mais ne deviennent jamais des images de la vidéo.
# 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,
)

Téléverser une image source

Si vous ne disposez que d’un fichier local, téléversez-le une fois via POST /v1/files et réutilisez l’URL permanente renvoyée.

# 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,
)

Paramètres

ParamètreTypeRemarques
modelchaîne (obligatoire)Identifiant de modèle vidéo de /v1/models.
promptchaîne (obligatoire)Description textuelle du plan.
image_urlstringImage source unique pour la génération vidéo à partir d’images. URL ou URI data:.
image_urlsstring[]Plusieurs images sources (Veo). Prioritaire sur image_url.
image_modestringVeo : « frame » (par défaut — première/dernière image) ou « reference » (références de style).
durationintDurée du clip en secondes pour les modèles facturés à la seconde, 5 si elle est omise : Seedance 2, Seedance 2 Fast et Seedance 2.0 Mini acceptent 4–15, Seedance 2.5 accepte 4–30, Wan 2.7 accepte 2–15. Une durée hors de la plage d’un modèle est rejetée avec une erreur 400. Veo ignore ce paramètre : chaque clip Veo dure 8 secondes.
aspect_ratiostringPar ex. « 16:9 », « 9:16 », « 1:1 ».
resolutionstringLorsque le modèle propose plusieurs niveaux, par ex. « 720p », « 1080p ».
inputobjectParamètre de secours — voir Raw input ci-dessous.

Paramètre de secours pour les entrées brutes

Pour les modèles facturés à la seconde, input est envoyé tel quel au fournisseur amont, à la place de l’entrée que notre adaptateur aurait construite. Les modèles Veo l’ignorent.

Les champs qui déterminent le prix font exception : resolution est toujours envoyé comme l’un des niveaux du modèle (le niveau par défaut s’il est omis ou ne correspond à aucun de ces niveaux), et duration doit être un nombre entier de secondes compris dans la plage du modèle (toute autre valeur est rejetée avec une erreur 400 ; 5 est envoyé s’il est omis). Les champs resolution et duration de la requête elle-même ne sont pas lus lorsque input est défini.

Modèles vidéo

SlugFournisseurRemarques
veo-3GoogleVeo 3.1 Fast — plus rapide et moins cher que Quality, audio natif.
veo-3-qualityGoogleVeo 3.1 Quality — fidélité maximale.
veo-3-liteGoogleVeo 3.1 Lite — niveau économique pour les aperçus.
seedance-2-5ByteDanceSeedance 2.5 — clips jusqu’à 30s, références multimodales, facturation à la seconde.
seedance-2ByteDanceSeedance 2 — cohérence entre plusieurs plans, jusqu’à 1080p, facturation à la seconde.
seedance-2-fastByteDanceSeedance 2 Fast — niveau économique, jusqu’à 720p, facturation à la seconde.
seedance-2-miniByteDanceSeedance 2.0 Mini — niveau le moins cher, uniquement en 480p/720p, pour les brouillons et la production en volume.
wan-2-7AlibabaWan 2.7 — génération cinématographique de vidéos à partir de texte, facturation à la seconde.

Consultez toujours /models pour obtenir la liste actuelle et les tarifs de chaque modèle.

Latence et délais d’attente

  • Kunavo interroge la tâche auprès du fournisseur en amont pendant jusqu’à 540 secondes avant de renvoyer une erreur 504.
  • Niveaux rapides (Veo 3.1 Fast / Lite) : souvent de 30 à 90 s.
  • Niveaux de qualité et durées plus longues : de 2 à 5 minutes.
  • Définissez le délai d’attente de votre client HTTP à au moins 600s.
  • Sur les réseaux instables ou les clients mobiles, préférez plutôt l’API asynchrone : la soumission prend quelques secondes, puis vous interrogez l’API pour obtenir le résultat.

API asynchrone (soumettre + interroger)

Points de terminaison : POST /v1/videos + GET /v1/videos/{id}. L’objet de tâche qu’ils renvoient utilise les noms de champs et les valeurs d’état de l’objet vidéo d’OpenAI, et ajoute output avec l’URL du clip. La requête est un corps JSON contenant les paramètres ci-dessus.

Le point de terminaison synchrone ci-dessus maintient la connexion HTTP ouverte jusqu’à neuf minutes — pratique pour les scripts ponctuels, mais peu fiable sur mobile et inefficace côté serveur. La paire asynchrone renvoie immédiatement un identifiant vid_* ; interrogez GET /v1/videos/{id} jusqu’à ce que status soit completed ou failed. Mêmes modèles, mêmes tarifs, mêmes URL archivées.

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"])

Depuis le SDK OpenAI, envoyez ce corps de requête avec client.post et interrogez l’état avec client.get, comme ci-dessous. La méthode client.videos.create propre au SDK ne fonctionne pas ici : elle n’accepte que les paramètres de Sora et les envoie sous forme de formulaire multipart (dans le SDK Python depuis la version 2.27.0), ce que ce point de terminaison refuse avec une erreur 400.

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"])

Structure de la réponse

ChampTypeRemarques
idstringIdentifiant de tâche préfixé par vid_. Stable malgré les changements en amont.
objectstring"video".
statusstringen attente | en cours | terminée | échouée.
modelstringSlug du modèle envoyé.
created_atintHorodatage Unix (secondes) de l’acceptation de la soumission.
completed_atint | nullHorodatage Unix (secondes) de la fin de la tâche ; null tant qu’elle est en attente.
expires_atintSecondes Unix, 30 jours après l’envoi : la tâche peut être interrogée au moins jusque-là. Ses URL de résultat n’expirent pas.
progressint0–100. Nous renvoyons actuellement 0 jusqu’à l’état final, puis 100.
outputobject | null{url, urls[], duration_seconds, resolution} une fois terminé. duration_seconds est la durée du clip demandée au modèle, null pour Veo ; resolution est le niveau de résolution du clip, ou null si aucun niveau n’a été enregistré.
errorobject | null{code, message} en cas d’échec.

En-têtes et conventions

  • Idempotency-Key (facultatif, jusqu’à 128 caractères) — si vous soumettez deux fois la même clé sur le même compte, la tâche d’origine est renvoyée au lieu d’en créer une nouvelle. Utile pour les boucles de nouvelle tentative.
  • POST /v1/videos renvoie 202 Accepted lors d’une nouvelle soumission et 200 OK lorsqu’une clé d’idempotence permet de renvoyer un résultat précédent.
  • La portée de GET /v1/videos/{id} est limitée au propriétaire : si vous interrogez une tâche appartenant à un autre compte, la réponse est 404 — nous ne confirmons jamais l’existence d’un identifiant appartenant à quelqu’un d’autre.
  • Cadence d’interrogation recommandée : toutes les 5 s, avec un délai exponentiel pouvant atteindre 30 s. Les tâches aboutissent généralement en 30 s à 5 min, selon le modèle et la résolution.

Quel format choisir ?

Synchrone /v1/video/generationsAsynchrone /v1/videos
Idéal pourScripts ponctuels, notebooksProduction, mobile, tâches de longue durée
RéseauMaintient la connexion ouverte jusqu’à 9 minEnvoyez en quelques secondes ; interrogez selon votre rythme
Récupération après échecPerdre la réponse = perdre la tâcheRéinterrogez l’identifiant à tout moment avant expires_at
ConcurrenceUn processus serveur par appelLa soumission libère immédiatement la connexion
Structure de la réponseStructure OpenAI /images/generationsStructure OpenAI /videos

Réponse et stockage

La réponse suit le format standard d’OpenAI — data[].url. L’URL est un lien permanent files.kunavo.com : Kunavo archive chaque résultat sur son propre CDN ; contrairement aux URL brutes des fournisseurs en amont, elle n’expire jamais. Chaque clip apparaît également dans /app/assets.

Étapes suivantes