Tutoriel
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 :
- Télécharger (ou héberger) 1 à 9 images de référence vers des URL HTTPS accessibles par l'API.
- Envoyer en POST à
https://api.hiapi.ai/v1/tasksune tâche qui référence ces URL. - Une fois la tâche à l'état
success, lire l'adresse MP4 finale dansdata.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 formeAuthorization: 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_imagesous forme de tableau. Passer directement une chaîne renvoiereference_image: got string, want array. durationdoit être un entier compris entre 3 et 15 inclus.999et1renvoient tous deux un message d'erreur de borne explicite.- Il n'existe pas de
seed,negative_prompt,fps,cfg_scalenistepsau 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
successetfail— 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 :
- Idempotence par
taskId.successetfailsont tous deux terminaux ; les nouvelles tentatives de HiAPI (ou les doublons produits par votre propre couche de retry) redélivreront le mêmetaskId. Dédupliquez avant de déclencher tout effet de bord (copie S3, message Slack, événement de facturation). - 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 étatprocessingdepuis 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êteAuthorizationmanquant, 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_REQUESTavec chemin de champ — le message d'erreur vous indique précisément ce qui a échoué, par exemplereference_image: maxItems: got 10, want 9ouaspect_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 renvoiestatus: "fail", accompagné deerror.codeeterror.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 →-
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
-
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