HiAPI
Blog

Tutoriel

Comment utiliser l'API image-to-video hailuo 2.3 : curl, Python et un exemple de requête exécutable

Jul 11, 2026
Comment utiliser l'API image-to-video hailuo 2.3 : curl, Python et un exemple de requête exécutable

Résumé

  • De quoi s'agit-il : une recette exécutable pour utiliser l'API hailuo-2.3/image-to-video sur hiapi — transformer une image statique en une courte vidéo : créer une tâche, interroger la tâche, télécharger le .mp4.
  • Point de terminaison : POST https://api.hiapi.ai/v1/tasks, avec "model": "hailuo-2.3/image-to-video" dans le corps de la requête, puis interrogez avec GET /v1/tasks/<taskId> jusqu'à ce que la tâche atteigne un état final.
  • Deux champs d'entrée obligatoires : prompt et image_url — une seule URL publique pointant vers votre première image (pas un tableau).
  • duration est une chaîne de caractères, pas un nombre : "6" ou "10". Passer l'entier 6 sera rejeté avec un 400.
  • Le schéma est strict. Il n'existe pas de paramètres motion_strength, camera_movement ou resolution — les envoyer sera rejeté avec un 400. Le mouvement est entièrement contrôlé via le prompt.

Ce que vous allez construire, et ce qu'il vous faut préparer

Vous disposez d'une image statique — photo produit, rendu de personnage, photographie — et vous voulez que hailuo-2.3 la mette en mouvement, en générant un clip qui commence exactement sur cette image. hailuo-2.3 est réputé pour son mouvement physiquement crédible (tissus, cheveux, mécanique corporelle), ce qui en fait un excellent choix pour animer des images statiques.

Il vous faut deux choses :

  1. Une clé d'API hiapi. Créez-en une dans le dashboard. La clé ressemble à sk-... et se place dans l'en-tête Authorization: Bearer.
  2. Une URL d'image de première image accessible publiquement. L'API récupère l'image depuis image_url, elle doit donc être accessible sans authentification — un lien S3/R2/CDN convient ; un chemin localhost ou une URL signée expirée ne convient pas.

Comme chaque modèle de génération sur hiapi, ce modèle passe par l'interface de tâches asynchrone : vous POSTez une tâche, obtenez immédiatement un taskId, puis récupérez le résultat par interrogation ou par callback. Il n'existe pas de point de terminaison synchrone pour la vidéo — le rendu d'un clip prend du temps et dépasse les délais d'expiration HTTP classiques.

Schéma de la requête

Le schéma d'entrée complet de hailuo-2.3/image-to-video — vérifié contre l'API en production :

Champ Type Obligatoire Description
prompt string ✅ Décrivez le mouvement, pas la scène — la scène provient de votre image.
image_url string ✅ Une URL publique, la première image. Au singulier — pas image_urls.
duration string Facultatif "6" ou "10" (secondes). Doit être une chaîne de caractères.
prompt_optimizer boolean Facultatif Laisse la plateforme enrichir votre prompt avant la génération.

Le schéma est strict : tout ce qui ne figure pas dans ce tableau sera rejeté. Envoyez motion_strength, camera_movement ou resolution, et vous recevrez ceci tel quel :

{
  "code": 400,
  "error_code": "INVALID_REQUEST",
  "message": "invalid input: <root>: additional properties 'resolution', 'camera_movement', 'motion_strength' not allowed"
}

Deux conséquences à retenir :

  • Pas de bouton de réglage du mouvement. La physique du mouvement de hailuo-2.3 est uniquement guidée par le prompt — voir la section sur la rédaction du prompt ci-dessous.
  • Aucun contrôle de résolution ni de ratio d'aspect sur ce point de terminaison. Ne construisez pas d'UI pour des paramètres que l'API rejetterait avec un 400.

Exemple minimal exécutable : curl

Créer une tâche :

curl -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-<your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "hailuo-2.3/image-to-video",
    "input": {
      "prompt": "The woman turns toward the camera and smiles; wind gently moves her hair; slow cinematic push-in",
      "image_url": "https://your-cdn.example.com/first-frame.jpg",
      "duration": "6",
      "prompt_optimizer": true
    }
  }'

La réponse (extrait) contient l'id de tâche sous data.taskId :

{ "data": { "taskId": "..." } }

Interrogez-la :

curl -s https://api.hiapi.ai/v1/tasks/<taskId> \
  -H "Authorization: Bearer sk-<your-key>"

Pendant le rendu du clip, data.status signale un état non final. Quand il passe à "success", l'URL de la vidéo se trouve dans data.output[0].url ; s'il passe à "fail", data.error.code et data.error.message en indiquent la raison.

Téléchargez le fichier immédiatement après le succès de la tâche. L'URL de sortie est signée et comporte une expiration (expireAt) — stockez les octets dans votre propre stockage, ne faites jamais de hotlink vers l'URL de la tâche.

Exemple minimal exécutable : Python

Le script complet du même flux — création, interrogation, téléchargement :

import time
import requests

API_BASE = "https://api.hiapi.ai/v1/tasks"
API_KEY = "sk-<your-key>"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}


def create_task() -> str:
    payload = {
        "model": "hailuo-2.3/image-to-video",
        "input": {
            "prompt": (
                "The cat stretches, hops off the windowsill, and walks toward "
                "the camera; soft afternoon light, subtle handheld camera sway"
            ),
            "image_url": "https://your-cdn.example.com/first-frame.jpg",
            "duration": "6",          # string, not int: "6" or "10"
            "prompt_optimizer": True,
        },
    }
    r = requests.post(API_BASE, json=payload, headers=HEADERS, timeout=30)
    task_id = (r.json().get("data") or {}).get("taskId")
    if not task_id:
        raise RuntimeError(f"create failed ({r.status_code}): {r.text[:300]}")
    return task_id


def wait_task(task_id: str, timeout_s: int = 900, poll_s: int = 10) -> dict:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        r = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS, timeout=30)
        task = r.json().get("data") or {}
        status = task.get("status")
        if status == "success":
            return task
        if status == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"task failed: {err.get('code')}: {err.get('message')}")
        time.sleep(poll_s)
    raise TimeoutError(f"task {task_id} not finished after {timeout_s}s")


def main() -> None:
    task_id = create_task()
    print("task created:", task_id)
    task = wait_task(task_id)
    url = task["output"][0]["url"]
    # The URL is signed and expires -- download immediately, store the bytes yourself.
    video = requests.get(url, timeout=120).content
    with open("hailuo-output.mp4", "wb") as f:
        f.write(video)
    print("saved hailuo-output.mp4")


if __name__ == "__main__":
    main()

Les tâches vidéo s'exécutent nettement plus longtemps que les tâches image, c'est pourquoi le script interroge toutes les 10 secondes et fixe une limite confortable de 15 minutes. N'interrogez pas plus agressivement que toutes les ~5 secondes — cela n'accélérera pas le rendu.

Rédiger des prompts qui contrôlent le mouvement

Comme il n'existe aucun paramètre de mouvement, le prompt est votre seul volant — et pour hailuo-2.3, cela suffit. Trois règles empiriques :

  • Décrivez le mouvement, pas l'apparence. Le modèle a déjà vu votre image. "a beautiful woman in a red dress" gâche le prompt ; "she turns her head slowly to the left as the camera pushes in" l'exploite bien.
  • Exprimez le mouvement de caméra en langage courant. "slow push-in", "orbit right", "handheld sway", "static camera" — cela s'écrit dans le texte du prompt, pas dans un champ camera_movement (qui n'existe pas).
  • En cas de doute, laissez prompt_optimizer vous aider. Avec "prompt_optimizer": true, la plateforme enrichit les prompts courts avant la génération — une bonne valeur par défaut pour les entrées brèves. Désactivez-le lorsque vous voulez respecter strictement une formulation soigneusement rédigée.

Considérations pour la production

Privilégiez les callbacks à l'interrogation. Ajoutez un callback lors de la création de la tâche et hiapi POSTera le résultat final à votre point de terminaison, vous évitant ainsi de maintenir une boucle ouverte :

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

"when": "final" ne se déclenche qu'une fois à l'état final. L'interrogation convient aux scripts et notebooks ; le callback est la forme correcte pour un serveur — le rendu vidéo est suffisamment long pour qu'un worker bloqué en interrogation représente un coût réel.

Gérez explicitement ces erreurs :

  • 401 avec "code": "permission_denied" — la clé est incorrecte, ou n'a pas accès à ce modèle. Le message contient un request_id que vous pouvez transmettre au support.
  • 400 INVALID_REQUEST — violation du schéma. Le message est précis, par exemple duration: got number, want string ou image_url: missing required field "image_url". Enregistrez-le tel quel.
  • 402 lors de la création de la tâche — solde insuffisant. Notez que la vidéo coûte plus cher par tâche que l'image, une clé qui exécute encore normalement des tâches image peut donc commencer à échouer sur la création de vidéos. Rechargez, ou vérifiez votre consommation dans le dashboard.
  • La tâche passe à "fail" — échec côté génération ; vérifiez data.error. Une cause fréquente en image-to-video : l'image_url est inaccessible (bucket privé, URL signée expirée).

Rendez les tentatives idempotentes. Si votre processus meurt entre la création et le téléchargement, un redémarrage naïf créera (et facturera) un second rendu. Persistez le taskId immédiatement après le retour de create_task, et au redémarrage, continuez l'interrogation au lieu de recréer.

Lectures associées

Questions fréquentes

Quelles durées l'image-to-video de hailuo-2.3 prend-il en charge ? Deux : "6" et "10" secondes, passées sous forme de chaînes. Toute autre valeur — ou la même valeur écrite en nombre — renvoie 400 INVALID_REQUEST.

Pourquoi "duration": 6 échoue-t-il alors que "duration": "6" fonctionne ? Le schéma définit duration comme une énumération de chaînes. L'API vous le dit clairement : duration: got number, want string. C'est le 400 le plus fréquent sur ce point de terminaison.

Puis-je définir la résolution ou le ratio d'aspect de la sortie ? Non. Le point de terminaison n'expose pas de champ resolution ou aspect_ratio, en envoyer un provoque un 400 — le schéma rejette les propriétés inconnues au lieu de les ignorer.

Comment contrôler le mouvement de caméra ou l'intensité du mouvement ? Via le prompt. Il n'existe pas de paramètres camera_movement ou motion_strength — écrivez les instructions de caméra et de mouvement en langage courant ("slow push-in", "she walks toward the camera").

Puis-je fournir plusieurs images de référence ? Non — image_url est une seule chaîne, la première image. Cela diffère d'autres modèles sur hiapi qui acceptent un tableau image_urls ; le schéma est défini par modèle, consultez donc toujours la page du modèle.

Quels formats d'image sont pris en charge pour la première image ? Les formats Web standards (JPEG/PNG), fournis via une URL accessible publiquement. Si la plateforme ne peut pas récupérer l'URL, la tâche échouera après création — vérifiez donc que le lien est accessible publiquement avant de soumettre.

En quoi cela diffère-t-il de hailuo-2.3/text-to-video ? Le text-to-video invente la scène à partir de votre prompt seul ; l'image-to-video ancre la première image sur votre image et part de là pour animer. Le cycle de vie de la tâche est identique — seul le schéma d'entrée diffère (ici image_url est obligatoire, alors que le text-to-video n'a aucun champ image).

Combien cela coûte-t-il ? La vidéo est facturée à la seconde de sortie, et les tarifs évoluent — consultez la page du modèle hailuo-2.3/image-to-video et la page des tarifs pour les chiffres actuels.

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