HiAPI
Blog

Tutoriel

Comment utiliser l'API image-vers-vidéo Kling 2.5 Turbo (curl + Python)

Un exercice pratique de génération vidéo à partir d'une première image : créer une tâche, interroger la tâche, télécharger le MP4 — avec le schéma d'entrée strict complet.

Oct 21, 2026
Comment utiliser l'API image-vers-vidéo Kling 2.5 Turbo (curl + Python)

TL;DR

  • kling-2.5-turbo/image-to-video transforme une image de première frame en un clip vidéo de 5 ou 10 secondes ; en passant une seconde URL d'image, vous pouvez fixer la dernière frame. Le modèle fonctionne sur l'API de tâches asynchrone de HiAPI — d'abord POST /v1/tasks pour créer une tâche, puis interrogez GET /v1/tasks/{taskId} pour récupérer le MP4 généré.
  • Le coût de sortie est de 0,06 $ par seconde : 0,30 $ pour un clip de 5 secondes, 0,60 $ pour un clip de 10 secondes. La facturation se fait par seconde de sortie produite ; une tâche qui échoue à cause d'une entrée invalide ne produit aucune sortie et n'est pas facturée.
  • Le schéma d'entrée est strict et fermé. Obligatoires : image_urls (tableau, 1 à 2 éléments — attention au pluriel), prompt, duration (uniquement 5 ou 10). Facultatifs : aspect_ratio, negative_prompt, cfg_scale. La forme singulière image_url est rejetée directement.
  • Le modèle est disponible sur HiAPI : ouvrir la page du modèle pour l'essayer d'abord dans le navigateur, puis intégrez l'API ci-dessous.

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

À la fin de cet article, vous disposerez d'un appel première frame → vidéo fonctionnel : soumettre une image et un prompt de mouvement, interroger la tâche, télécharger le MP4. Deux choses sont à préparer au préalable :

  1. Obtenir une clé API depuis le tableau de bord HiAPI — chaque requête doit être envoyée avec Authorization: Bearer sk-.... Si vous n'en avez pas encore créé, consultez la documentation d'authentification.
  2. Une URL d'image de première frame accessible publiquement. L'appel de création ne vérifie que la structure de l'entrée ; l'image est en réalité récupérée au moment de l'exécution de la tâche, donc une URL localhost ou privée crée une tâche qui échouera ensuite (voir détails ci-dessous).

Il s'agit d'image-vers-vidéo, donc l'identifiant du modèle est kling-2.5-turbo/image-to-video. Si vous souhaitez seulement générer une vidéo à partir d'un prompt, il existe un point de terminaison texte-vers-vidéo distinct.

Exemple minimal fonctionnel (curl)

Créer une tâche. Le nom du modèle se place au niveau supérieur ; tout ce que lit le modèle va dans input :

curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kling-2.5-turbo/image-to-video",
    "input": {
      "prompt": "The perfume bottle rotates slowly on the marble stand, light glints across the glass, petals drift down, slow orbit",
      "image_urls": ["https://static.hiapi.ai/gallery/2026/10/4852c542c4447593.jpg"],
      "duration": 5,
      "aspect_ratio": "9:16"
    }
  }'

Une création réussie renvoie un identifiant de tâche — pas la vidéo :

{ "code": 200, "data": { "taskId": "tk-hiapi-01M5CW..." }, "message": "success" }

Interrogez ensuite le point de terminaison de détail jusqu'à ce que status atteigne un état terminal :

curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-01M5CW... \
  -H "Authorization: Bearer sk-YOUR_KEY"

Pendant l'exécution de la tâche, vous verrez des états non terminaux ; une fois terminée, l'URL du MP4 est disponible sous data.output[0].url :

{
  "code": 200,
  "data": {
    "status": "success",
    "model": "kling-2.5-turbo/image-to-video",
    "storage": "temp",
    "output": [{ "url": "https://temp.hiapi.ai/kling-i2v/tk-hiapi-01M5CW.mp4?expireAt=1761350400" }]
  },
  "message": "success"
}

Cette output[0].url est un lien temporaire — téléchargez-la immédiatement au lieu de la référencer par hotlink directement dans votre application (voir Remarques pour la production).

Exemple minimal fonctionnel (Python)

Aucun SDK nécessaire — l'API de tâches HiAPI est une interface HTTP pure, la bibliothèque standard suffit donc :

import time
import requests

API = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": "Bearer sk-YOUR_KEY", "Content-Type": "application/json"}


def create(image_url: str, prompt: str, duration: int = 5) -> str:
    payload = {
        "model": "kling-2.5-turbo/image-to-video",
        "input": {
            "prompt": prompt,
            "image_urls": [image_url],   # 1–2 items; 2nd = last frame
            "duration": duration,        # 5 or 10 only
        },
    }
    r = requests.post(API, json=payload, headers=HEADERS, timeout=60)
    body = r.json()
    task_id = (body.get("data") or {}).get("taskId")
    if not task_id:
        raise RuntimeError(f"create failed: {body}")
    return task_id


def wait(task_id: str, timeout_s: int = 600, interval: int = 5) -> str:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        r = requests.get(f"{API}/{task_id}", headers=HEADERS, timeout=30)
        task = r.json().get("data") or {}
        status = task.get("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(interval)
    raise TimeoutError(f"task {task_id} did not finish in {timeout_s}s")


task_id = create(
    "https://static.hiapi.ai/gallery/2026/10/4852c542c4447593.jpg",
    "slow orbit around the product, light glints across the glass, petals drift down",
    duration=5,
)
mp4_url = wait(task_id)

# Download immediately — the URL is short-lived.
with requests.get(mp4_url, stream=True, timeout=120) as resp:
    resp.raise_for_status()
    with open("clip.mp4", "wb") as f:
        for chunk in resp.iter_content(1 << 16):
            f.write(chunk)
print("saved clip.mp4")

Remarques pour la production

Interrogation ou callback. Interroger toutes les 5 secondes (comme montré ci-dessus) est la voie la plus simple, et c'est la méthode utilisée par l'exemple. Si vous préférez recevoir les résultats par push, l'appel de création accepte un objet callback de niveau supérieur — {"url": "https://you/webhook", "when": "final"} — et HiAPI enverra un POST à votre point de terminaison lorsque la tâche atteindra un état terminal. Le contrat exact du webhook est décrit dans la documentation de création de tâche ; pour la plupart des tâches à clip unique, interroger le point de terminaison de détail de tâche suffit.

Branchez toujours sur data.status, jamais sur le code de statut HTTP de l'appel de création. Un 200 renvoyé par la création signifie acceptée, pas rendue. Le clip n'existe réellement que lorsque le point de terminaison de détail renvoie status: "success".

Considérez l'URL de sortie comme un lien temporaire. storage vaut temp, l'URL expire, il faut donc télécharger le MP4 dès que la tâche réussit (ou le copier dans votre propre bucket). Ne fournissez pas directement les URL HiAPI aux utilisateurs finaux.

Maîtrise des coûts. Vous payez par seconde de sortie produite — 0,06 $/seconde — donc 0,30 $ pour un clip de 5 secondes et 0,60 $ pour un clip de 10 secondes. Pendant l'itération sur les prompts et la première frame, rendez d'abord en 5 secondes, puis relancez en 10 secondes une fois satisfait. Les tarifs actuels figurent sur la page de tarification.

Notes de test réelles et points d'attention

Tout le contenu de cet article provient de requêtes réelles sur le point de terminaison en production (schéma revérifié le 2026-10-21), et pas seulement d'un tableau de paramètres :

  • Le champ est image_urls (pluriel), et le schéma est fermé. Envoyer le naturel image_url échoue avant la création de la tâche :
    { "code": 400, "error_code": "INVALID_REQUEST",
      "message": "invalid input: image_urls: missing required field \"image_urls\"; <root>: additional properties 'image_url' not allowed" }
    
    Tout champ non reconnu (mode, faute de frappe, etc.) est rejeté de la même manière — il n'existe pas de champs supplémentaires silencieusement ignorés.
  • duration est une énumération, pas une plage. Seuls 5 et 10 sont acceptés ; 7 renvoie duration: value must be one of 5, 10.
  • image_urls contient 1 ou 2 éléments. Une URL est la première frame ; la seconde URL fixe la dernière frame pour une transition maîtrisée. Trois URL renvoient maxItems: got 3, want 2 ; un tableau vide renvoie minItems: got 0, want 1.
  • Champs facultatifs réellement présents : aspect_ratio n'accepte que 16:9, 9:16, 1:1, 4:3, 3:4, 21:9 ; cfg_scale est plafonné à 1 (envoyer 99 renvoie cfg_scale: maximum: got 99, want 1) ; negative_prompt doit être une chaîne. Omettre aspect_ratio laisse le modèle suivre votre première frame.
  • L'appel de création ne valide que la structure, pas l'accessibilité — c'est le piège le plus courant. Une URL d'image syntaxiquement valide mais inaccessible renvoie 200 et un taskId, puis la tâche passe de manière asynchrone à l'état d'échec :
    { "data": { "status": "fail",
      "error": { "code": "INVALID_REQUEST",
        "message": "The image URL is not readable. Please provide a valid public HTTP(S) URL or an accessible file path and try again." } } }
    
    Cette tâche n'a produit aucune output et n'est pas facturée — un échec ne génère pas de frais — mais si vous ne vérifiez que la réponse de création, vous croirez à tort qu'elle a réussi. C'est précisément pourquoi la boucle d'interrogation doit se baser sur data.status.

Contenus associés

FAQ

Quelle peut être la durée du clip ? 5 ou 10 secondes — ce sont les deux seules valeurs de duration acceptées par le schéma.

Puis-je contrôler la fin ? Oui. Placez deux URL dans image_urls : la première est la frame d'ouverture, la seconde la dernière frame. Le modèle interpole le mouvement entre les deux.

Pourquoi la création renvoie-t-elle 200 alors que la tâche affiche fail ? L'appel de création ne vérifie que la conformité de votre JSON au schéma. L'image n'est récupérée qu'au moment de l'exécution de la tâche, donc une URL inaccessible ou privée crée la tâche puis la fait échouer. Lisez toujours data.status depuis le point de terminaison de détail, et assurez-vous que l'URL de la première frame est accessible publiquement.

Les tâches échouées sont-elles facturées ? Non. La facturation se fait par seconde de sortie produite, donc une tâche qui échoue à cause d'une entrée invalide ne produit aucune output et n'est pas facturée.

Y a-t-il un contrôle du ratio d'aspect ? aspect_ratio est facultatif et accepte 16:9, 9:16, 1:1, 4:3, 3:4 ou 21:9. Laissez vide pour suivre votre première frame.

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