HiAPI
Blog

Tutoriel

Comment utiliser l'API de génération vidéo à partir d'image hailuo 2.3 Fast : curl, Python et requêtes exécutables

Jul 10, 2026
Comment utiliser l'API de génération vidéo à partir d'image hailuo 2.3 Fast : curl, Python et requêtes exécutables

Vous avez une image statique et vous voulez la transformer en une courte vidéo. Ce guide vous montre comment intégrer hailuo-2.3-fast/image-to-video via l'API de tâches hiapi : le schéma d'entrée exact (vérifié contre le validateur en ligne), une requête curl directement copiable, un script Python complet qui interroge jusqu'à la fin de la tâche, ainsi que les détails de production — callbacks, nouvelles tentatives idempotentes, et les réponses d'erreur que vous rencontrerez réellement.

Ce dont vous avez besoin

  • Une clé API hiapi — obtenez-la depuis votre console. La clé ressemble à sk-... et se place dans l'en-tête Authorization: Bearer.
  • Une image source hébergée sur une URL HTTPS accessible publiquement. L'API reçoit une URL, pas un fichier téléversé.
  • N'importe quel client HTTP. Les exemples ci-dessous utilisent curl et requests en Python.

Le tarif à la seconde de ce modèle est indiqué sur la page de tarification.

Schéma d'entrée (vérifié)

hailuo-2.3-fast/image-to-video utilise le point de terminaison unifié de tâches asynchrones : POST /v1/tasks crée une tâche, GET /v1/tasks/<taskId> interroge la tâche. Le schéma d'entrée est strict — les champs inconnus sont rejetés avec une erreur 400, au lieu d'être ignorés silencieusement.

Champ Type Requis Description
prompt string ✅ Décrit le mouvement souhaité
image_url string ✅ Forme singulière — une chaîne URL, pas un tableau image_urls
duration string Facultatif "6" ou "10" — c'est une chaîne, pas un nombre
prompt_optimizer boolean Facultatif Laisse le backend reformuler votre prompt pour le mouvement

Le validateur intercepte trois pièges :

  1. duration doit être une chaîne. Envoyer "duration": 6 renvoie 400 avec duration: got number, want string. Envoyez "duration": "6".
  2. Le champ est image_url au singulier. Beaucoup d'autres modèles vidéo de la plateforme (Grok Imagine, Kling) reçoivent un tableau image_urls. Ce modèle reçoit une seule chaîne.
  3. Pas de resolution, aspect_ratio, seed, watermark ni end_image_url. Tous renvoient additional properties ... not allowed. Le format de sortie suit votre image source ; le contrôle du mouvement passe uniquement par le prompt.

Requête minimale exécutable : curl

curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "hailuo-2.3-fast/image-to-video",
    "input": {
      "prompt": "La caméra avance lentement, de la vapeur s'élève de la tasse de café, douce lumière du matin",
      "image_url": "https://your-cdn.example.com/coffee.jpg",
      "duration": "6"
    }
  }'

Réponse de succès :

{
  "code": 200,
  "data": { "taskId": "tk-hiapi-01KX4W3SVW8S8EHD5VX45JH95V" },
  "message": "success"
}

Ensuite, interrogez :

curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-01KX4W3SVW8S8EHD5VX45JH95V \
  -H "Authorization: Bearer sk-YOUR_KEY"

Pendant le rendu de la vidéo, data.status vaut "handling" :

{
  "code": 200,
  "data": {
    "taskId": "tk-hiapi-01KX4W3SVW8S8EHD5VX45JH95V",
    "model": "hailuo-2.3-fast/image-to-video",
    "status": "handling",
    "storage": "temp",
    "completed": 0,
    "created": 1783648872
  },
  "message": "success"
}

Les états finaux sont "success" (l'URL de la vidéo se trouve dans data.output[0].url) et "fail" (détails dans data.error).

Script Python complet

import os
import time

import requests

API_BASE = "https://api.hiapi.ai/v1/tasks"
TOKEN = os.environ["HIAPI_API_KEY"]  # sk-...
HEADERS = {"Authorization": f"Bearer {TOKEN}"}


def create_task(prompt: str, image_url: str, duration: str = "6") -> str:
    resp = requests.post(
        API_BASE,
        headers={**HEADERS, "Content-Type": "application/json"},
        json={
            "model": "hailuo-2.3-fast/image-to-video",
            "input": {
                "prompt": prompt,
                "image_url": image_url,
                "duration": duration,  # chaîne : "6" ou "10"
            },
        },
        timeout=60,
    )
    body = resp.json()
    if resp.status_code != 200 or not body.get("data", {}).get("taskId"):
        raise RuntimeError(f"création échouée : {resp.status_code} {body}")
    return body["data"]["taskId"]


def wait_task(task_id: str, timeout_s: int = 600, poll_s: int = 5) -> dict:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        task = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS, timeout=30).json()["data"]
        status = task.get("status")
        if status == "success":
            return task
        if status == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"tâche échouée : {err.get('code')} {err.get('message')}")
        time.sleep(poll_s)
    raise TimeoutError(f"la tâche {task_id} tourne toujours après {timeout_s}s")


if __name__ == "__main__":
    task_id = create_task(
        prompt="Le chat cligne des yeux, tourne la tête vers la fenêtre, une brise caresse sa fourrure",
        image_url="https://your-cdn.example.com/cat.jpg",
        duration="6",
    )
    print("tâche :", task_id)

    task = wait_task(task_id)
    video_url = task["output"][0]["url"]
    print("vidéo :", video_url)

    # L'URL se trouve sur un stockage temporaire à durée limitée — téléchargez-la immédiatement, ne la liez pas directement.
    mp4 = requests.get(video_url, timeout=120).content
    with open("output.mp4", "wb") as f:
        f.write(mp4)
    print(f"output.mp4 enregistré ({len(mp4)} octets)")

Ce script fait correctement deux choses que les solutions bricolées à la hâte négligent souvent :

  • Il télécharge la vidéo immédiatement. storage: "temp" signifie que l'URL de sortie a une durée de validité (expireAt). Persistez les octets dans votre propre stockage ; ne stockez jamais l'URL renvoyée dans votre base de données.
  • Il traite fail comme un état final. Interroger à nouveau une tâche échouée ne la ressuscite pas — créez une nouvelle tâche.

Considérations de production

Utilisez des callbacks au lieu de l'interrogation

Pour les cas d'usage hors script, enregistrez un webhook à la création de la tâche et sautez complètement la boucle d'interrogation :

{
  "model": "hailuo-2.3-fast/image-to-video",
  "input": { "prompt": "...", "image_url": "https://...", "duration": "6" },
  "callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}

Avec "when": "final", votre point de terminaison est appelé une fois lorsque la tâche atteint success ou fail. Règle empirique : l'interrogation convient aux CLI, aux tâches par lots et à tout scénario ad hoc ; dès que vous avez un serveur déjà à l'écoute, les callbacks sont préférables — les tâches vidéo prennent plusieurs minutes, et une boucle d'interrogation pendant ces minutes, c'est un slot de connexion occupé et un worker gaspillé.

Idempotence

L'appel de création n'est pas idempotent en lui-même — chaque POST /v1/tasks validé crée une nouvelle tâche (facturée). Si votre exécuteur de tâches réessaie en cas de timeout, stockez le taskId obtenu en le cléant sur votre propre identifiant de requête avant de traiter le résultat. Lors d'une nouvelle tentative, si ce clé possède déjà un taskId, interrogez-le au lieu de créer une tâche en double.

Les erreurs que vous rencontrerez réellement

Symptôme Signification Correction
401 — {"error":{"code":"permission_denied","type":"hiapi_error"}} Clé invalide, ou cette clé n'a pas accès à ce modèle Vérifiez la clé dans la console ; la réponse contient un request_id utile au support
400 INVALID_REQUEST — duration: got number, want string Un nombre JSON a été utilisé au lieu d'une chaîne "duration": "6"
400 INVALID_REQUEST — additional properties '...' not allowed Vous avez envoyé un champ hors du schéma Supprimez-le — le message indique chaque champ en infraction
400 INVALID_REQUEST — missing required field "prompt" / "image_url" Entrée vide ou nom de champ incorrect Les deux champs sont obligatoires
La tâche atteint fail après création Échec d'exécution — cause fréquente : une URL d'image que le backend ne peut pas récupérer Le validateur ne récupère pas votre image à la création ; assurez-vous que l'URL est accessible publiquement

La dernière ligne mérite d'être soulignée : la validation à la création ne porte que sur le schéma. Une image_url mal orthographiée ou privée vous donnera un taskId d'apparence normale et une tâche qui meurt quelques minutes plus tard — alertez donc sur fail, pas seulement sur les erreurs HTTP.

Lectures associées

Questions fréquentes

Puis-je définir la résolution ou le rapport d'aspect ? Non. Le schéma de hailuo-2.3-fast/image-to-video rejette resolution et aspect_ratio comme champs inconnus. Le format suit votre image source ; si vous avez besoin d'un rapport d'aspect spécifique, recadrez d'abord l'image d'entrée.

Quelles durées sont prises en charge ? Exactement deux : "6" et "10" secondes, transmises sous forme de chaînes. Toute autre valeur renvoie 400 avec value must be one of '6', '10'.

Puis-je transmettre plusieurs images de référence ? Non — le champ est une seule chaîne image_url. Les modèles qui acceptent des tableaux d'images (par exemple image_urls de Grok Imagine) utilisent un schéma différent ; ne copiez pas un corps de requête d'un modèle à l'autre sans vérifier.

Comment contrôler le mouvement ? Uniquement via le prompt — il n'y a pas de paramètre d'intensité de mouvement ni de caméra. Décrivez le mouvement explicitement (« la caméra avance lentement », « les cheveux flottent au vent »). Vous pouvez éventuellement définir "prompt_optimizer": true pour laisser le backend enrichir votre prompt.

Existe-t-il une variante plus lente et de meilleure qualité ? Oui — hailuo-2.3/image-to-video (sans -fast) est également disponible sur la plateforme, et hailuo-2.3/text-to-video couvre les scénarios sans image source. Vérifiez le schéma de chaque variante avant de basculer ; les champs d'entrée ne sont pas garantis identiques.

Combien cela coûte ? La facturation est calculée sur le nombre de secondes de vidéo générée et varie selon le modèle ; consultez les tarifs actuels sur la page de tarification.

Faut-il une clé API pour essayer ? Oui — tous les appels à l'API de tâches nécessitent une clé Bearer sk-.... Vous pouvez en créer une dans la console et d'abord tester dans l'aire de jeux de la page du modèle.

Articles similaires

Voir tous les articles →

Commencez avec des artefacts IA persistants

Obtenez votre clé API et appelez des modèles d'image, de vidéo et d'audio via une API unique basée sur les tasks.

Lire la documentation de l'API