HiAPI
Blog

Tutoriel

API de génération de vidéo par référence HappyHorse 1.1 : tutoriel curl + Python prêt à l'exécution

Jun 26, 2026
API de génération de vidéo par référence HappyHorse 1.1 : tutoriel curl + Python prêt à l'exécution

HappyHorse 1.1 R2V transforme jusqu'à neuf images de référence et un prompt textuel en une courte vidéo, en maintenant la cohérence du sujet, des vêtements et du style de scène à travers plusieurs plans. Sur HiAPI, ce modèle est disponible via l'endpoint asynchrone unifié des tâches, avec l'ID de modèle happyhorse-1.1/reference-to-video. Ce tutoriel présente d'abord la requête minimale fonctionnelle, puis la version production avec callback.

Objectifs et prérequis

Vous allez effectuer les opérations suivantes :

  1. Télécharger (ou héberger) 1 à 9 images de référence vers des URL HTTPS accessibles par l'API.
  2. Envoyer en POST à https://api.hiapi.ai/v1/tasks une tâche qui référence ces URL.
  3. Une fois la tâche à l'état success, lire l'adresse MP4 finale dans data.output[0].url.

Prérequis :

  • Un compte HiAPI et une clé API commençant par sk-. Vous pouvez l'obtenir depuis la console HiAPI, et consulter la documentation Authentification pour les règles d'en-tête. La clé doit être transmise sous la forme Authorization: Bearer sk-..., et ne doit jamais être placée dans du code côté client.
  • Exigences d'hébergement des images : chaque image de référence doit être au format JPEG, PNG ou WEBP, avec un côté le plus court ≥ 400 px et une taille ≤ 20 Mo. Les URL de CDN publiques conviennent ; les URL S3 signées conviennent également, tant que l'exécuteur de tâche peut y accéder.

Requête minimale fonctionnelle (curl)

Voici le corps de requête minimal accepté par le schéma. Il utilise la sortie par défaut 1080p / 16:9 / 5 secondes, vous n'avez donc qu'à définir les champs prompt et reference_image.

curl -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "happyhorse-1.1/reference-to-video",
    "input": {
      "prompt": "[Image 1] 中的角色行走在 [Image 2] 风格的场景中,电影感镜头",
      "reference_image": [
        "https://static.hiapi.ai/example/ref-1.jpg",
        "https://static.hiapi.ai/example/ref-2.jpg"
      ]
    }
  }'

La réponse est une enveloppe de tâche ; le seul champ dont vous avez réellement besoin est data.taskId :

{
  "code": 200,
  "data": { "taskId": "task_xxx", "status": "queued" }
}

Interrogez jusqu'à l'état terminal :

curl https://api.hiapi.ai/v1/tasks/task_xxx \
  -H "Authorization: Bearer sk-YOUR_KEY"

Lorsque data.status passe à success, l'URL de la vidéo se trouve dans data.output[0].url. Cette URL est accompagnée d'un horodatage expireAt ; téléchargez donc le fichier immédiatement, ou transférez-le vers un stockage persistant — ne faites pas de hotlink direct de cette adresse sur votre site.

Paramètres

L'endpoint R2V valide strictement les entrées ; les champs inconnus renvoient 400 INVALID_REQUEST. Schéma complet (vérifié en conditions réelles sur POST /v1/tasks) :

Champ Type Requis Valeurs possibles Valeur par défaut
model string Oui happyhorse-1.1/reference-to-video (valeur fixe) -
input.prompt string Oui Référencez les images avec [Image 1], [Image 2]… dans le même ordre que reference_image -
input.reference_image string[] Oui 1 à 9 URL HTTPS, format JPEG/PNG/WEBP, côté le plus court ≥ 400 px, ≤ 20 Mo chacune -
input.resolution enum Non 720p、1080p 1080p
input.aspect_ratio enum Non 16:9、9:16、3:4、4:3、4:5、5:4、1:1、9:21、21:9 16:9
input.duration integer Non 3-15 (secondes) 5
callback.url string Non URL HTTPS recevant les événements d'état terminal de la tâche -
callback.when enum Non final (seule valeur actuelle) final

Quelques règles de validation peu intuitives qu'il vaut mieux connaître avant qu'elles ne vous posent problème :

  • Même avec une seule URL d'image de référence, transmettez reference_image sous forme de tableau. Passer directement une chaîne renvoie reference_image: got string, want array.
  • duration doit être un entier compris entre 3 et 15 inclus. 999 et 1 renvoient tous deux un message d'erreur de borne explicite.
  • Il n'existe pas de seed, negative_prompt, fps, cfg_scale ni steps au niveau supérieur. L'API rejette les propriétés inconnues.

Pour la tarification complète par combinaison de résolution et de durée, consultez la page de tarification HiAPI — le coût varie simultanément avec les paramètres resolution et duration.

Python : de bout en bout + interrogation

Le bloc de code ci-dessous est un générateur autonome qui soumet la tâche, interroge jusqu'à l'état terminal et télécharge la vidéo. Il est délibérément sans dépendance (uniquement requests), pour que vous puissiez le copier dans n'importe quel service. Pour une description plus complète du cycle de vie d'une requête, consultez le démarrage rapide.

import os
import time
import requests

API = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"}


def submit_r2v(prompt: str, reference_images: list[str], duration: int = 5,
               resolution: str = "1080p", aspect_ratio: str = "16:9") -> str:
    if not 1 <= len(reference_images) <= 9:
        raise ValueError("reference_image must contain 1-9 URLs")
    payload = {
        "model": "happyhorse-1.1/reference-to-video",
        "input": {
            "prompt": prompt,
            "reference_image": reference_images,
            "resolution": resolution,
            "aspect_ratio": aspect_ratio,
            "duration": duration,
        },
    }
    r = requests.post(API, json=payload, headers=HEADERS, timeout=60)
    r.raise_for_status()
    data = r.json()
    if data.get("code") != 200:
        raise RuntimeError(f"create failed: {data}")
    return data["data"]["taskId"]


def wait_for_video(task_id: str, timeout_s: int = 600, poll_s: int = 5) -> str:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        r = requests.get(f"{API}/{task_id}", headers=HEADERS, timeout=30)
        r.raise_for_status()
        task = r.json()["data"]
        status = task["status"]
        if status == "success":
            return task["output"][0]["url"]
        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} did not finish in {timeout_s}s")


def download(url: str, path: str) -> None:
    with requests.get(url, stream=True, timeout=300) as r:
        r.raise_for_status()
        with open(path, "wb") as f:
            for chunk in r.iter_content(1 << 16):
                f.write(chunk)


if __name__ == "__main__":
    task_id = submit_r2v(
        prompt="保持 [Image 1] 中的主体设计,行走在 [Image 2] 的氛围中",
        reference_images=[
            "https://static.hiapi.ai/example/char.jpg",
            "https://static.hiapi.ai/example/mood.jpg",
        ],
        duration=5,
        resolution="720p",
        aspect_ratio="9:16",
    )
    print("submitted", task_id)
    video_url = wait_for_video(task_id)
    print("ready", video_url)
    download(video_url, "out.mp4")

Ce code fait délibérément les choses suivantes :

  • Il lit la clé depuis la variable d'environnement HIAPI_API_KEY, afin qu'elle n'entre jamais dans git ni dans les journaux de conteneur.
  • Il utilise r.raise_for_status() pour intercepter les erreurs de transport avant de parser le JSON.
  • Il ne considère comme terminaux que success et fail — tous les autres états (queued, processing, etc.) laissent la boucle continuer.
  • Il télécharge immédiatement l'URL de sortie. Les URL de sortie sont signées et expirent ; si vous avez besoin du fichier plus tard, transférez-le vers un stockage persistant ou copiez-le dans votre propre bucket.

Production : passer de l'interrogation au webhook

Une interrogation toutes les 5 secondes convient à un ordinateur portable ou à un worker à faible trafic. Pour un vrai service, enregistrez un callback lors de la création de la tâche, afin que HiAPI pousse les événements d'état terminal vers votre endpoint — votre worker reste ainsi inactif et vous ne payez plus pour des requêtes GET vides. Pour le schéma complet de requête, consultez Créer une tâche, et pour l'interrogation de secours, Obtenir les détails d'une tâche.

payload = {
    "model": "happyhorse-1.1/reference-to-video",
    "input": {
        "prompt": "[Image 1] 中的角色走过 [Image 2] 中的城市街道",
        "reference_image": [
            "https://cdn.example.com/char.jpg",
            "https://cdn.example.com/street.jpg",
        ],
        "resolution": "1080p",
        "aspect_ratio": "16:9",
        "duration": 8,
    },
    "callback": {
        "url": "https://your-domain.com/hiapi/callback",
        "when": "final",
    },
}

L'endpoint de réception doit intégrer deux choses :

  1. Idempotence par taskId. success et fail sont tous deux terminaux ; les nouvelles tentatives de HiAPI (ou les doublons produits par votre propre couche de retry) redélivreront le même taskId. Dédupliquez avant de déclencher tout effet de bord (copie S3, message Slack, événement de facturation).
  2. Interrogation de secours. Si votre endpoint tombe, le callback est perdu. Conservez une tâche de vérification légère qui appelle GET /v1/tasks/<id> pour toutes les tâches en état processing depuis plus longtemps que le pire temps de génération.

Erreurs que vous rencontrerez réellement

Les erreurs suivantes méritent un traitement explicite ; les autres sont des 5xx transitoires, qui doivent être retentés avec backoff par le SDK ou votre client HTTP.

  • 401 permission_denied — en-tête Authorization manquant, mal formé ou clé révoquée. Réémettez une clé depuis la console plutôt que de bricoler l'en-tête.
  • 400 INVALID_REQUEST avec chemin de champ — le message d'erreur vous indique précisément ce qui a échoué, par exemple reference_image: maxItems: got 10, want 9 ou aspect_ratio: value must be one of .... Validez les entrées avant la soumission par le worker, afin d'échouer rapidement en local.
  • task failed (terminal) — l'interrogation/le callback renvoie status: "fail", accompagné de error.code et error.message. Causes fréquentes : URL d'image de référence inaccessible par le worker, déclenchement de la modération NSFW, ou côté le plus court de l'image inférieur au seuil de 400 px.

Questions fréquentes

Peut-on référencer plus de 9 images ? Non. Le schéma limite reference_image à 9 images maximum. Si vous avez besoin de plus de variations, composez hors plateforme une image de référence plus dense (planche de vignettes ou storyboard) et utilisez-la comme entrée unique.

Faut-il obligatoirement utiliser les marqueurs [Image N] dans le prompt ? Ces marqueurs sont votre moyen de pointer vers une image de référence spécifique. Un prompt qui ne désigne jamais d'image est aussi valide, mais le modèle mélangera librement toutes les images de référence — ce qui n'est généralement pas l'effet souhaité pour des scénarios de cohérence multi-plans.

Pourquoi recevoir 503 nsfw_moderation_unavailable ? Il s'agit d'une erreur temporaire de la plateforme due au service de modération, sans rapport avec votre requête. Réessayez après un bref backoff.

Faut-il choisir 720p ou 1080p ? 1080p est la valeur par défaut et offre une meilleure qualité, mais coûte plus cher et rend plus lentement. Pour des extraits verticaux destinés aux réseaux sociaux, que le lecteur sous-échantillonnera de toute façon, 720p avec aspect_ratio: 9:16 est généralement un meilleur compromis.

Peut-on passer un seed pour rendre la sortie reproductible ? Pas pour l'instant. L'API traite seed comme une propriété inconnue et le rejette. Si vous avez besoin de reproductibilité, fixez précisément prompt, reference_image, aspect_ratio, resolution et duration, et acceptez une certaine variabilité entre les exécutions.

Où sont stockées les URL de sortie et combien de temps sont-elles valides ? Ce sont des URL signées à durée de vie courte ; la réponse contient expireAt. Téléchargez immédiatement après le succès de la tâche ou transférez-les vers un stockage persistant.

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