Tutoriel
API image-vers-texte Happyhorse : exemples curl et Python prêts à l'emploi (hiapi)
Vous voulez transformer une image fixe en une courte vidéo fluide directement depuis votre code. Le modèle happyhorse-1.1/image-to-video sur hiapi fait exactement cela : vous fournissez une URL d'image et un prompt de mouvement facultatif, et en quelques secondes vous récupérez un MP4 en 720p ou 1080p. Ce guide est le chemin le plus court pour y arriver — de vrais corps de requête, de vrais messages d'erreur, en curl et en Python — pour que vous puissiez mettre en production dès aujourd'hui sans vous arracher les cheveux sur les noms de champs.
Ce dont vous avez besoin avant de commencer
- Une clé API hiapi. Obtenez-la depuis la console hiapi — elle ressemble à
sk-...et se place dans l'en-têteAuthorization: Bearer .... - Une URL d'image accessible (HTTPS public). Les fichiers locaux ne fonctionnent pas ; le modèle ira récupérer cette URL lui-même.
- Une intention de mouvement exprimée en texte (facultatif mais recommandé). Plus les verbes sont clairs, meilleur est l'effet de mouvement.
Le modèle est facturé à la seconde de vidéo, les tarifs sont listés sur la page de tarification hiapi ; le reste du catalogue est sur hiapi models — jetez un œil aux deux avant d'augmenter les volumes.
La requête minimale qui fonctionne (curl)
Chaque vidéo sur hiapi passe par le point de terminaison asynchrone /v1/tasks : vous POST d'abord une requête de création, récupérez un taskId, puis soit vous interrogez GET /v1/tasks/{taskId}, soit vous recevez un callback à la fin.
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": "happyhorse-1.1/image-to-video",
"input": {
"image_urls": ["https://example.com/your-still.jpg"],
"prompt": "the cat slowly turns its head toward the camera and blinks",
"resolution": "1080p",
"duration": 5
}
}'
Une réponse réussie ressemble à ceci :
{
"code": 200,
"data": { "taskId": "tk-hiapi-01KW3D..." },
"message": "success"
}
Interrogez maintenant le résultat (toutes les 5 secondes suffisent — la plupart des clips se terminent en deux minutes) :
curl "https://api.hiapi.ai/v1/tasks/tk-hiapi-01KW3D..." \
-H "Authorization: Bearer sk-your-key"
Pendant l'exécution, vous obtiendrez "status": "handling". Une fois terminé, vous obtiendrez :
{
"code": 200,
"data": {
"status": "success",
"model": "happyhorse-1.1/image-to-video",
"output": [
{
"type": "video",
"url": "https://temp.hiapi.ai/.../01KW....mp4",
"expireAt": 1783131245
}
],
"taskId": "tk-hiapi-01KW3D..."
}
}
Cette url est ce que vous devez télécharger — et vous devriez le faire immédiatement, car expireAt est un horodatage Unix qui indique quand le stockage temporaire supprimera le fichier.
Python : interroger jusqu'au succès
Voici un script complet et exécutable — renseignez votre clé et l'URL de l'image et c'est parti.
import os
import time
import requests
API = "https://api.hiapi.ai/v1/tasks"
TOKEN = os.environ["HIAPI_KEY"] # sk-...
def create_video(image_url: str, prompt: str, duration: int = 5,
resolution: str = "1080p") -> str:
"""Submit an image-to-video task. Returns the taskId."""
r = requests.post(
API,
headers={"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json"},
json={
"model": "happyhorse-1.1/image-to-video",
"input": {
"image_urls": [image_url], # exactly one URL
"prompt": prompt, # optional
"resolution": resolution, # "720p" or "1080p"
"duration": duration, # int, 3..15
},
},
timeout=60,
)
body = r.json()
if body.get("code") != 200:
raise RuntimeError(f"create failed: {body}")
return body["data"]["taskId"]
def wait_for_video(task_id: str, timeout_s: int = 600,
poll_s: int = 5) -> str:
"""Block until the task succeeds, fails, or times out. Returns the MP4 URL."""
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(
f"{API}/{task_id}",
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=30,
)
task = r.json().get("data") or {}
status = task.get("status")
if status == "success":
return task["output"][0]["url"]
if status == "fail":
raise RuntimeError(f"task failed: {task.get('error')}")
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=120) 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 = create_video(
image_url="https://example.com/your-still.jpg",
prompt="the dancer spins once and ends in a pose",
duration=5,
resolution="1080p",
)
print("submitted:", task_id)
mp4_url = wait_for_video(task_id)
download(mp4_url, "out.mp4")
print("saved out.mp4")
L'ensemble fait environ 50 lignes et constitue toute la surface d'API prête pour la production — pas de SDK, pas de file d'attente en arrière-plan, pas de protocole de streaming. Lancez-le avec HIAPI_KEY=sk-... python video.py.
En production : utiliser les callbacks au lieu de l'interrogation
L'interrogation convient aux tâches ponctuelles et aux tests. Pour la charge serveur, vous voulez que la plateforme vous rappelle lorsque le clip est prêt — cela élimine la boucle, passe à l'échelle sans travail supplémentaire, et le flux de requêtes retourne immédiatement.
Ajoutez un objet callback au niveau racine du corps de la requête (pas à l'intérieur de input) :
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/image-to-video",
"input": {
"image_urls": ["https://example.com/still.jpg"],
"prompt": "slow zoom out from the subject",
"resolution": "1080p",
"duration": 6
},
"callback": {
"url": "https://your-app.example.com/webhooks/hiapi",
"when": "final"
}
}'
Lorsque la tâche atteint un état terminal, hiapi POST vers votre URL l'objet de tâche que vous auriez pu interroger (status, output[].url, error, etc.). when: "final" signifie que vous ne recevez un callback que pour un succès ou un échec — pas de bruit de progression.
Quelques règles qui vous épargneront de vraies heures de débogage :
- Rendez votre webhook idempotent. Le même
taskIdpeut arriver deux fois (les retentatives réseau existent vraiment). Vérifiez d'abord dans votre base de données si cette tâche a déjà été traitée. - Réhébergez le MP4 dans votre gestionnaire. L'URL fournie par hiapi pointe vers un stockage temporaire avec un champ
expireAt; streamez-la dans votre propre bucket dès la première réception, sinon vous la perdrez. - Accusez réception rapidement. Retournez vite un
2xx, puis faites le gros du travail de manière asynchrone. Les webhooks lents risquent des retentatives. - Utilisez l'interrogation comme filet de sécurité. Emballez le
taskIddans une tâche différée et interrogez à nouveau 10 minutes plus tard au cas où le webhook serait perdu. Si cela vous arrive régulièrement, consultez les raisons pour lesquelles un callback de tâche hiapi peut ne pas se déclencher pour connaître les causes courantes.
Les messages d'erreur que vous rencontrerez
Voici les chaînes exactes renvoyées par l'API ; les faire correspondre dans votre code est plus fiable que de deviner.
| Situation | HTTP | Forme du corps de réponse |
|---|---|---|
| Clé incorrecte ou révoquée | 401 | {"error":{"code":"permission_denied","message":"This API key cannot use the selected model...","type":"hiapi_error"}} |
| Champ obligatoire manquant | 400 | {"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: image_urls: missing required field \"image_urls\""} |
resolution invalide |
400 | ... resolution: value must be one of '720p', '1080p' |
| Durée hors limites | 400 | ... duration: minimum: got 2, want 3 / duration: maximum: got 30, want 15 |
Longueur de image_urls incorrecte |
400 | ... image_urls: maxItems: got 2, want 1 (ou minItems: got 0, want 1) |
Champ inconnu à la racine ou dans input |
400 | ... additional properties 'identity_preserving' not allowed |
| La tâche elle-même échoue après soumission | 200 (GET de la tâche) avec data.status == "fail" et data.error.{code,message} |
Les erreurs d'authentification et de validation signifient que vous ne devez pas réessayer ; toutes les autres (5xx de la plateforme, timeouts réseau pendant l'interrogation, task fail transitoire avec un code réessayable) se prêtent à une nouvelle tentative avec un léger backoff.
Régler la sortie
Quatre paramètres ont réellement un impact — tout le reste (seed, aspect_ratio, fps, cfg_scale, negative_prompt) est actuellement rejeté comme champ inconnu. Restez dans cet ensemble :
image_urls— une URL HTTPS. Le ratio d'aspect et la composition de la sortie suivent cette image.prompt— une courte description du mouvement ("she smiles and tilts her head"). Les prompts cinématographiques, axés sur les verbes, surpassent les listes d'adjectifs.resolution—"720p"pour le prototypage et les vignettes,"1080p"pour la livraison.duration— un entier en secondes, de3à15. Les clips plus longs coûtent plus de temps et d'argent ; choisissez la durée la plus courte qui raconte l'histoire.
Pour les projets nécessitant une image de début et de fin, consultez happyhorse-1.1 reference-to-video. Pour la génération purement textuelle sans image source, happyhorse-1.1 text-to-video est l'article complémentaire. Parcourez le reste dans le catalogue hiapi models ou dans la section recettes du blog.
FAQ
Puis-je passer deux images pour contrôler la première et la dernière frame ?
Pas sur happyhorse-1.1/image-to-video — image_urls accepte au plus un élément (maxItems: 1). Si vous en avez besoin, utilisez happyhorse-1.1/reference-to-video, ou construisez avec happyhorse-1.1 reference-to-video.
Quelle est la résolution du MP4 que je récupère réellement ?
La sortie suit le ratio d'aspect de votre image d'entrée et le flag resolution que vous définissez. "1080p" donne le plus élevé des deux ; "720p" est plus rapide et moins cher pour les tests.
Pourquoi ma clé fonctionne-t-elle avec d'autres modèles mais renvoie-t-elle un 401 permission_denied ?
Certains modèles sont restreints par clé. Consultez la page du modèle dans la console hiapi — si happyhorse-1.1/image-to-video n'est pas dans votre liste d'autorisation, générez une nouvelle clé ou contactez le support avec le request_id de l'erreur.
Y a-t-il une réponse en streaming/SSE ? Non. Chaque modèle vidéo utilise le schéma de tâche asynchrone décrit dans cet article. Choisissez l'interrogation ou le callback selon que vous contrôlez ou non le serveur.
Où voir le coût réel de chaque clip ?
Sur la page de tarification hiapi. Le coût est calculé à la seconde de vidéo, donc le duration que vous transmettez détermine directement la dépense — gardez les clips de développement/test à duration: 3 pour économiser votre budget.
L'URL de sortie ne fonctionne plus après un jour.
C'est le comportement attendu — cette URL pointe vers un stockage temporaire et la réponse contient un horodatage Unix expireAt. Téléchargez toujours le MP4 au moment où la tâche réussit (ou streamez-le via votre propre CDN).
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