HiAPI
Blog

Tutoriel

API image-vers-texte Happyhorse : exemples curl et Python prêts à l'emploi (hiapi)

Jun 27, 2026
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ête Authorization: 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 taskId peut 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 taskId dans 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, de 3 à 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 →

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