HiAPI
Blog

Tutoriel

Tutoriel pour kandinsky-6.0-pro/image-to-video sur HiAPI : curl + Python

Lancer une requête image-vers-vidéo exploitable sur HiAPI : de l'envoi de la première image à l'interrogation asynchrone jusqu'au téléchargement final

Nov 3, 2026
深色底上青绿与石板灰的扁平液态色块相互交叠,构成 kandinsky-6.0-pro 视频教程的封面图。

Aperçu du flux image-vers-vidéo sur HiAPI

Chaque appel image-vers-vidéo sur HiAPI suit le même cycle de vie en trois phases. Le comprendre dès le départ rendra la suite du tutoriel méthodique plutôt que mystérieuse.

Phase 1 : soumettre la tâche

Vous envoyez une requête POST au point de terminaison kandinsky-6.0-pro/image-to-video, en joignant l'image de première image et un court prompt décrivant le mouvement souhaité. La réponse ne renvoie pas un flux d'octets vidéo, mais accepte la tâche et renvoie un identifiant de tâche — toutes les opérations suivantes reposent sur ce handle.

Phase 2 : interroger l'état

La génération vidéo prend du temps, donc le point de terminaison est asynchrone. Vous appelez la route d'état avec l'identifiant de tâche, qui rapporte l'état actuel : en file d'attente, en cours de traitement, réussi ou échoué. Vous répétez la vérification à intervalles réguliers jusqu'à ce que l'état atteigne un état terminal.

Phase 3 : récupérer la sortie

Une fois la tâche signalée comme réussie, la charge utile d'état porte l'emplacement de la vidéo finale. Vous la téléchargez et l'écrivez sur disque. C'est tout le contrat.

Pourquoi cette forme est importante

La séparation entre soumission et interrogation n'est pas fortuite. Cela signifie que votre client HTTP n'a pas besoin de garder la socket ouverte pendant tout le rendu, et que vous pouvez persister l'identifiant de tâche, partir, puis reprendre. En cas d'erreur, le champ d'état vous indique où cela a échoué, ce qui est bien plus utile qu'un timeout opaque.

Retenez ces trois phases : soumettre, interroger, télécharger. Les exemples curl et Python ci-dessous ne sont que deux écritures de la même séquence.

Préparer la première image et les identifiants API

Il n'y a que deux endroits où la plupart des premiers appels échouent : la clé et l'image. Réglez les deux maintenant et le reste du tutoriel se déroulera sans accroc.

Définir la clé API comme variable d'environnement

Ne collez pas la clé dans un fichier source. Exportez-la dans le shell pour que curl et Python puissent y accéder sans rien coder en dur :

export HIAPI_API_KEY="your_api_key_here"

Dans Windows PowerShell, l'équivalent est $env:HIAPI_API_KEY="your_api_key_here". Avant de continuer, vérifiez que le shell peut la lire :

echo "$HIAPI_API_KEY" | head -c 4; echo

Vous devriez voir les premiers caractères de la clé. Si vous ne voyez rien, la variable n'a pas été persistée dans la session shell actuelle — c'est une cause courante des réponses 401 ultérieures.

Choisir une image de première image valide

La première image détermine le sujet, la composition et le format d'image de toute la vidéo. Quelques contraintes pratiques évitent la plupart des rejets :

  • Format : utilisez un format d'image Web standard comme JPEG ou PNG. Évitez les codecs exotiques, les cas limites progressifs ou les profils colorimétriques CMYK.
  • Accessibilité : l'API doit réellement lire ces octets. Si vous passez une URL, assurez-vous qu'elle est publiquement accessible ou correctement authentifiée ; si vous passez des données en ligne, assurez-vous que l'encodage est complet.
  • Dimensions : une image de taille raisonnable se valide plus vite. Les fichiers très volumineux ralentissent la requête et sont souvent la source d'erreurs d'envoi.
  • Contenu : choisissez une image raisonnable comme point de départ du mouvement — sujet net, arrière-plan simple. Le prompt de mouvement décrit comment s'éloigner de cette image, donc une image encombrée produira une vidéo encombrée.

Décider comment référencer l'image

Le corps de la requête vous permet de pointer vers la première image via une URL ou des données encodées en ligne. Choisissez selon l'emplacement de l'image :

  • Si le fichier est hébergé et lisible publiquement, l'URL est l'option la plus concise.
  • Si l'image est locale ou privée, l'encodage en ligne rend la requête autonome, au prix d'une charge utile plus importante.

Dans les deux cas, gardez la même forme de variable pour la référence d'image dans curl et Python, afin que les deux exemples restent directement comparables.

Envoyer la requête avec curl

Voici une requête de soumission complète. Remplacez la référence d'image et le prompt par les vôtres, puis exécutez-la.

curl -sS -X POST "https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "https://your-host.example/first-frame.jpg",
    "prompt": "slow dolly-in on the subject, gentle ambient motion, stable camera",
    "duration": 5,
    "aspect_ratio": "16:9"
  }'

Le rôle de chaque partie

  • URL pointe spécifiquement vers le modèle kandinsky-6.0-pro/image-to-video. Le choix du modèle se fait dans le chemin, pas dans le corps de la requête, donc une faute ici donnera un 404 plutôt qu'un message de validation explicite.
  • Authorization porte le jeton bearer de la variable d'environnement exportée. Rien d'autre n'authentifie l'appel.
  • Content-Type: application/json est important pour tout POST avec corps JSON ; omettez-le et certains proxys corrompront la charge utile avant qu'elle n'atteigne l'API.
  • image est votre référence de première image — ici sous forme d'URL, ou sous forme de chaîne encodée en ligne si l'image est privée.
  • prompt décrit le mouvement. Restez centré sur le mouvement et le comportement de la caméra, plutôt que de répéter ce que l'image contient déjà.
  • duration et aspect_ratio façonnent la sortie. Le format d'image doit correspondre à la première image ; un désaccord provoquera un recadrage ou des bandes noires. La facturation est mesurée sur la sortie générée, donc ces réglages déterminent aussi votre coût.

Lire la réponse

Une soumission réussie renvoie la charge utile de la tâche, pas la vidéo. Capturez-en l'identifiant de tâche — vous le passerez plus tard au point de terminaison d'état :

curl -sS -X POST "https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json | jq -r '.id'

Utiliser un fichier body.json au lieu de -d en ligne permet de garder les longs prompts et les images encodées lisibles. jq est facultatif, mais il transforme la réponse en une seule ligne, directement collable dans l'appel suivant.

Appeler le même point de terminaison en Python

La version Python est la même requête, le même corps, simplement enveloppés dans une fonction pour la réutilisation. Gardez le corps identique à la version curl — c'est précisément l'intérêt de montrer les deux.

import os
import json
import requests

API_BASE = "https://api.hiapi.ai/v1"
ENDPOINT = f"{API_BASE}/kandinsky-6.0-pro/image-to-video"

def submit_video_job(image_ref: str, prompt: str, duration: int = 5, aspect_ratio: str = "16:9") -> str:
    """Submit a first-frame image-to-video job and return the job id."""
    api_key = os.environ.get("HIAPI_API_KEY")
    if not api_key:
        raise RuntimeError("HIAPI_API_KEY is not set in this environment")

    body = {
        "image": image_ref,
        "prompt": prompt,
        "duration": duration,
        "aspect_ratio": aspect_ratio,
    }

    response = requests.post(
        ENDPOINT,
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        },
        json=body,
        timeout=60,
    )

    if response.status_code >= 400:
        raise RuntimeError(f"submit failed: {response.status_code} {response.text}")

    payload = response.json()
    return payload["id"]

if __name__ == "__main__":
    job_id = submit_video_job(
        image_ref="https://your-host.example/first-frame.jpg",
        prompt="slow dolly-in on the subject, gentle ambient motion, stable camera",
    )
    print(json.dumps({"job_id": job_id}, indent=2))

Remarques sur la version Python

  • Échec rapide si la clé manque. Une vérification explicite de l'environnement transforme l'erreur la plus fréquente en une erreur locale claire, plutôt qu'en un 401 distant.
  • json=body vs data=json.dumps(body). Passer json= laisse requests définir le type de contenu pour vous, ce qui garde les en-têtes minimaux. Si vous utilisez plutôt data=, définissez explicitement Content-Type.
  • Timeout généreux. La soumission vidéo implique parfois le transfert de charges utiles d'images volumineuses. Un timeout trop court produit de faux échecs.
  • Lire le corps de la réponse en cas d'erreur. Lever à la fois le code d'état et le corps signifie que le message d'échec vous dit réellement ce que l'API refuse.

Si la première image est un fichier local, lisez-le, encodez-le en base64 et remplacez image_ref dans submit_video_job par cette chaîne. La signature de la fonction reste inchangée, ce qui est précisément la raison pour laquelle les deux styles d'appel partagent la même définition de corps de requête.

Interroger la tâche et télécharger la vidéo

Une tâche soumise n'est pas la vidéo finale. Interrogez jusqu'à ce que la tâche atteigne un état terminal, puis téléchargez.

Boucle d'interrogation

import time
import requests

def wait_for_video(job_id: str, interval: float = 10.0, max_wait: float = 600.0) -> str:
    """Poll the job until it finishes; return the output URL."""
    import os
    api_key = os.environ["HIAPI_API_KEY"]
    url = f"https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video/{job_id}"
    headers = {"Authorization": f"Bearer {api_key}"}

    deadline = time.time() + max_wait
    while time.time() < deadline:
        response = requests.get(url, headers=headers, timeout=30)
        response.raise_for_status()
        payload = response.json()
        status = payload.get("status")

        if status == "succeeded":
            return payload["output"]
        if status == "failed":
            raise RuntimeError(f"job failed: {payload.get('error', 'unknown error')}")

        time.sleep(interval)

    raise TimeoutError(f"job {job_id} did not finish within {max_wait}s")

Trois détails font bien fonctionner cette boucle :

  • Attendre entre les appels, pas compter sur la chance. Un sommeil fixe d'environ dix secondes est un bon point de départ ; ne martelez pas le point de terminaison dans une boucle serrée.
  • Gérer chaque état terminal.succeeded vous donne la sortie, failed vous donne le message d'erreur. Les deux doivent être traités comme des sorties. Une boucle qui ne vérifie que le succès tournerait jusqu'à l'échéance sur une tâche morte.
  • Limiter le temps d'attente.max_wait garantit que le script se termine, au lieu de tourner indéfiniment derrière une tâche bloquée.

Télécharger la vidéo finale

Une fois l'URL de sortie obtenue, écrivez en streaming sur disque, plutôt que de charger tout le fichier en mémoire :

def download_video(output_url: str, path: str = "result.mp4") -> str:
    with requests.get(output_url, stream=True, timeout=120) as response:
        response.raise_for_status()
        with open(path, "wb") as handle:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    handle.write(chunk)
    return path

Assemblez les parties :

job_id = submit_video_job(
    image_ref="https://your-host.example/first-frame.jpg",
    prompt="slow dolly-in on the subject, gentle ambient motion, stable camera",
)
output_url = wait_for_video(job_id)
saved = download_video(output_url, "kandinsky_result.mp4")
print(f"saved to {saved}")

En production, persistez le job_id au moment même où la soumission réussit. Si le processus redémarre, vous pouvez reprendre la boucle d'interrogation avec l'identifiant stocké, sans payer pour une nouvelle soumission.

Résoudre les erreurs courantes de requête et de tâche

La plupart des échecs se classent en trois catégories : la clé, l'image ou l'état de la tâche. Isolez la catégorie avant de modifier quoi que ce soit d'autre.

Clé refusée

Symptôme : une erreur d'authentification ou d'autorisation avant la création de toute tâche.

  • Confirmez que la variable est bien exportée dans le shell actuel : echo "$HIAPI_API_KEY".
  • Vérifiez qu'aucun espace ou saut de ligne n'a été collé avec la clé ; supprimez-les.
  • Vérifiez que l'en-tête est orthographié Authorization, avec une valeur commençant par Bearer suivi d'un espace.
  • Si curl fonctionne mais pas Python, affichez os.environ.get("HIAPI_API_KEY") depuis le même interpréteur — la variable peut exister dans votre shell mais pas dans l'environnement hérité par le script.

Image refusée

Symptôme : une erreur de validation à la soumission, ou une tâche qui échoue immédiatement.

  • Format incorrect ou exotique. Réexportez en JPEG ou PNG standard et réessayez. Les profils CMYK et les codecs exotiques sont des coupables fréquents.
  • URL inaccessible. Ouvrez l'URL de votre image dans une fenêtre de navigation privée ; si vous ne pouvez pas obtenir les octets, l'API non plus.
  • Encodage en ligne corrompu. Si vous encodez en base64 localement, décodez la chaîne une fois et confirmez qu'il s'agit d'une image valide avant de l'envoyer.
  • Format d'image incompatible. Si la vidéo revient recadrée ou avec des bandes noires, alignez aspect_ratio sur la première image, plutôt que de vous battre avec la sortie après coup.

La tâche ne se termine pas ou échoue

Symptôme : l'état reste bloqué sur queued/processing, ou signale failed.

  • Lisez le champ error. Une tâche en échec porte un message. Il indique généralement le champ ou la condition en cause. Ne devinez pas — consignez toute la charge utile d'état.
  • Respectez la conception asynchrone. Attendre quelques secondes avant de vérifier est normal. Une boucle de reprise serrée qui abandonne au bout de deux secondes n'est pas un mécanisme d'état.
  • Limitez le temps d'attente. Si la tâche dépasse une limite raisonnable, considérez-la comme échouée, exposez l'identifiant de tâche et soumettez une nouvelle tâche, plutôt que d'interroger indéfiniment.
  • Comparez avec un appel connu comme valide. Si votre image personnalisée échoue, exécutez le curl exact du début de ce tutoriel avec une petite image publique et un prompt court. S'il réussit, le problème vient de vos entrées ; s'il échoue aussi, le problème vient des identifiants ou de la forme de la requête.

Erreur de téléchargement après succès

Si la tâche signale succeeded mais que le téléchargement échoue, l'URL de sortie a peut-être expiré ou est soumise à un contrôle d'accès. Interrogez à nouveau la tâche pour obtenir un nouvel emplacement de sortie, plutôt que de réessayer celui qui a expiré.

Traitez ces catégories dans l'ordre — la clé, puis l'image, puis l'état de la tâche — chaque erreur vous ramènera rapidement à une cause unique. C'est tout le cycle : soumettre la première image, interroger jusqu'à l'achèvement, télécharger le résultat.

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