Tutoriel
Comment appeler hailuo-2.3-fast/image-to-video via l'API HiAPI : curl et Python en pratique
La première requête exploitable du palier image-vers-vidéo rapide et économique — de l'ID du modèle au téléchargement du MP4
Ce que hailuo-2.3-fast/image-to-video peut faire pour vous
hailuo-2.3-fast/image-to-video est le palier optimisé pour la vitesse de la gamme image-vers-vidéo de HiAPI. Vous lui fournissez une image fixe, accompagnée d'un prompt textuel décrivant le mouvement souhaité, et il renvoie un court clip vidéo qui s'anime à partir de cette image. Le « fast » de l'ID du modèle est justement l'essentiel : ce palier échange une partie de la fidélité fine des modèles vidéo lourds contre une latence plus faible et un coût de rendu unitaire réduit, ce qui le rend idéal pour l'itération et la production en lot.
Choisissez ce modèle lorsque vous avez besoin d'une première version exploitable d'un clip, et non d'un plan « master » : prévisualisation de storyboard, vidéos en boucle pensées pour les réseaux sociaux, éléments de mouvement d'arrière-plan, ou phase de brouillon dans un pipeline où un modèle plus lourd se charge du rendu final. Si vos sorties exigent des plans longs, une mise en scène complexe ou une stricte cohérence d'identité sur de nombreuses images, confiez ces tâches à un palier plus lourd — tout en conservant la même structure de requête, afin qu'un changement de palier ne soit qu'une modification de configuration.
Tout ce qui suit s'articule autour d'une requête que vous pouvez réellement exécuter. L'objectif est de faire atterrir un MP4 sur le disque, pas d'obtenir une capture d'écran d'une réponse réussie.
Aperçu de la requête
Un rendu vidéo prend de quelques secondes à plusieurs minutes, c'est pourquoi l'API est asynchrone : vous soumettez une tâche, récupérez un identifiant de tâche, puis interrogez cette tâche jusqu'à ce qu'elle signale son achèvement et vous remette la sortie. Cela signifie que chaque intégration comporte deux phases — la soumission et le polling — et que seule la phase de soumission varie selon le modèle.
La requête de soumission comporte quatre éléments que vous contrôlez :
- Endpoint et méthode — une requête
POSTvers l'endpoint de génération vidéo de HiAPI. - En-tête d'authentification — votre clé, sous la forme
Authorization: Bearer $HIAPI_API_KEY. Placez la clé dans une variable d'environnement, jamais en ligne dans le code soumis. - ID du modèle — la chaîne littérale
hailuo-2.3-fast/image-to-video. C'est elle qui sélectionne le palier. - Entrée — un
promptdécrivant le mouvement, et une image de première frame. L'image est fournie sous forme d'URL accessible publiquement ou de data URI, selon ce que votre compte accepte sur cet endpoint ; commencer par une URL est le plus simple. Le corps JSON est envoyé avecContent-Type: application/json.
Rassemblez ces quatre champs à un seul endroit dans votre base de code — un dictionnaire, un fichier de configuration, un constructeur piloté par variables d'environnement — afin que passer plus tard à un autre modèle vidéo HiAPI ne demande qu'une ligne de modification plutôt qu'une réécriture. La réponse est une petite enveloppe JSON dont l'identifiant de tâche est le seul champ que vous devez persister ; le reste n'est que des métadonnées d'état.
La requête curl exploitable
Voici l'étape de soumission complète. Remplacez l'URL de l'image par une adresse à laquelle votre compte a accès, et exportez d'abord votre clé.
export HIAPI_API_KEY="your-key-here"
curl -sS -X POST "https://api.hiapi.ai/v1/video/generations" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "hailuo-2.3-fast/image-to-video",
"prompt": "slow push in, gentle breeze moving the fabric, film grain",
"image": "https://example.com/first-frame.jpg"
}'
La réponse en cas de succès est un JSON contenant l'identifiant de tâche, généralement de la forme {"id": "task_...", "status": "queued"}. La forme exacte des champs de l'enveloppe peut varier, alors parsez de façon défensive : lisez l'ID de tâche par clé et non par position, et traitez les champs inconnus comme informatifs.
En cas de débogage, ajoutez -i pour voir la ligne de statut HTTP. Un 401 signifie presque toujours que l'en-tête d'authentification est absent ou que la variable s'est développée en chaîne vide — lancez echo $HIAPI_API_KEY avant de blâmer le corps de la requête. Un 400 pointe généralement vers le corps : URL d'image injoignable, model manquant, ou nom de champ ne correspondant pas à l'endpoint.
Copiez l'ID de tâche renvoyé dans une variable shell, afin que les étapes suivantes n'aient pas à le ressaisir :
TASK_ID=$(curl -sS -X POST "https://api.hiapi.ai/v1/video/generations" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"hailuo-2.3-fast/image-to-video","prompt":"slow push in","image":"https://example.com/first-frame.jpg"}' \
| python -c 'import sys,json; print(json.load(sys.stdin)["id"])')
echo "$TASK_ID"
La même requête, en Python
La version Python garde un corps de requête strictement identique à celui de curl, de sorte que lorsque vous traquez un bug, le résultat curl et le résultat Python soient directement comparables.
import os
import requests
API_BASE = "https://api.hiapi.ai/v1"
MODEL = "hailuo-2.3-fast/image-to-video"
def submit_video(prompt: str, image_url: str) -> str:
resp = requests.post(
f"{API_BASE}/video/generations",
headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
json={
"model": MODEL,
"prompt": prompt,
"image": image_url,
},
timeout=60,
)
resp.raise_for_status()
return resp.json()["id"]
Trois habitudes importantes ici. os.environ[...] (et non .get) échoue bruyamment lorsque la variable est absente, et c'est exactement le mode d'échec souhaité. Un timeout explicite empêche un socket bloqué d'occuper indéfiniment un worker. Et raise_for_status() transforme un corps de réponse 4xx en exception journalisable, plutôt qu'en un KeyError silencieux levé à la ligne suivante à cause de l'ID manquant.
Cette fonction renvoie délibérément uniquement l'ID de tâche. La soumission et l'attente sont des préoccupations distinctes ; les mélanger rend la boucle de polling difficile à tester. Notez que l'ID du modèle n'apparaît qu'une seule fois, au niveau du module — c'est le seul endroit à modifier lorsque vous migrez vers un autre modèle vidéo HiAPI.
Interroger la tâche jusqu'à ce que la vidéo soit prête
La vérification de l'état est une requête GET vers la ressource de la tâche. Une tâche en file d'attente ou en cours renvoie un état non terminal ; une tâche terminée renvoie un état terminal accompagné de la sortie.
import time
def wait_for_video(task_id: str, timeout: float = 600.0) -> dict:
deadline = time.monotonic() + timeout
delay = 2.0
url = f"{API_BASE}/video/generations/{task_id}"
while True:
r = requests.get(
url,
headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
timeout=30,
)
r.raise_for_status()
task = r.json()
if task.get("status") == "succeeded":
return task
if task.get("status") in {"failed", "cancelled"}:
raise RuntimeError(f"task {task_id} ended: {task}")
if time.monotonic() >= deadline:
raise TimeoutError(f"task {task_id} still {task.get('status')} after {timeout}s")
time.sleep(delay)
delay = min(delay * 1.5, 15.0)
Il s'agit d'une boucle de nouvelle tentative, et non d'un sommeil fixe. Le backoff commence à deux secondes et croît progressivement jusqu'à un plafond de quinze secondes, de sorte que les rendus courts se terminent rapidement et que les rendus longs ne martèlent pas l'endpoint. La date limite est vérifiée avant le sommeil, afin que le temps réel total reste dans les limites sans dépasser d'un intervalle. Utilisez time.monotonic() plutôt que time.time(), pour qu'un ajustement d'horloge ne prolonge pas la boucle.
Traitez failed et cancelled différemment de « continuer d'attendre » : ce sont des états terminaux mais non réussis. Lorsque cela se produit, journalisez l'objet tâche complet — c'est le seul diagnostic dont vous disposez, et reproduire un échec à partir du seul ID est très pénible. Traitez les chaînes d'état inconnues comme non terminales et continuez le polling, afin qu'un nouvel état intermédiaire ne fasse pas planter votre worker.
Télécharger et traiter la sortie
Une tâche terminée porte une ou plusieurs URL de sortie. Récupérez les octets et écrivez-les là où vous gardez le contrôle.
def download_video(task: dict, dest: str) -> str:
url = task["output"][0]["url"] if isinstance(task.get("output"), list) else task["output"]["url"]
with requests.get(url, stream=True, timeout=120) as r:
r.raise_for_status()
with open(dest, "wb") as f:
for chunk in r.iter_content(chunk_size=1 << 20):
f.write(chunk)
return dest
Téléchargez en streaming plutôt qu'en appelant .content ; une vidéo peut être assez volumineuse pour qu'un cache intégral en mémoire soit un gaspillage pour un worker traitant plusieurs tâches. La forme exacte de l'URL de sortie peut varier légèrement selon le palier de modèle, alors vérifiez s'il s'agit d'une liste avant d'indexer — la branche ci-dessus couvre les deux cas.
Règle importante : traitez l'URL renvoyée comme un handle à durée de vie courte, non comme un stockage. Dès qu'une tâche réussit, copiez le fichier dans votre propre bucket ou chemin local, et servez-le depuis là. Persister l'URL en base de données et la récupérer quelques jours plus tard, c'est la cause des 403 et des tableaux de bord vides. Pour le stockage objet, la même boucle d'octets en streaming peut alimenter directement la méthode d'upload de votre client — si votre SDK accepte un objet de type fichier, aucun fichier temporaire intermédiaire n'est nécessaire.
Astuces pratiques et pièges courants
- Utilisez l'en-tête d'authentification, pas les paramètres de requête. La clé va dans
Authorization: Bearer .... Une clé collée dans une URL est une clé présente dans les journaux d'accès. - L'accessibilité de l'image compte plus que sa pertinence. Un
400à la soumission est bien plus souvent dû à une URL que l'API ne peut pas récupérer — localhost, bucket privé, hôte protégé par anti-hotlink, lien expirant — qu'à un problème de prompt. Avant de déboguer quoi que ce soit d'autre, testez cette URL dans une session de navigateur neuve. - Le type de contenu du corps. Envoyez du JSON avec
Content-Type: application/json. Si vous oubliez-Hlors d'une soumission curl, le serveur risque de ne pas parser du tout votre corps de requête. - Polling trop serré. Un
while Truesans sommeil vous fera limiter le débit sans rendre le rendu plus rapide. Le backoff plus une date limite, c'est toute l'astuce. - Aucun timeout nulle part. Définissez des timeouts sur les appels de soumission, de polling et de téléchargement. L'absence de timeout transforme un fournisseur lent en file d'attente bloquée dans votre propre système.
- Le prompt décrit le mouvement, pas l'image. Décrivez ce qui change d'une frame à l'autre — mouvement de caméra, action du sujet, éléments de l'environnement — et restez bref. Redécrire l'image statique n'apporte rien.
- Une configuration, plusieurs modèles. Placez l'ID du modèle, le prompt et l'image d'entrée dans le même constructeur de payload. Passer de ce palier rapide à un palier plus lourd ne demande alors qu'un changement de chaîne.
Le moyen le plus rapide de valider la configuration de bout en bout : lancez la soumission curl, collez l'ID de tâche dans la boucle de polling, et confirmez qu'un MP4 atterrit sur le disque — une fois cette étape franchie, le reste n'est plus qu'un travail d'empaquetage.
Articles similaires
Voir tous les articles →-
Tutoriel
Comment appeler hailuo-2.3/text-to-video via l'API HiAPI : curl, Python et exemples de requêtes exécutables
-
Tutoriel
Utiliser Kandinsky 6.0 Pro texte-vers-vidéo sur HiAPI : tutoriel curl et Python
Démonstration pratique axée sur la requête : construire l'appel du prompt au clip vidéo, soumettre la requête, interroger l'état d'achèvement et télécharger le résultat
-
Tutoriel
Comment utiliser kling-4.0/text-to-video via l'API HiAPI : curl, Python et un exemple de requête fonctionnel
Guide pratique pour les développeurs : prompts cinématographiques, audio synchronisé, interrogation asynchrone et téléchargement des sorties