HiAPI
Blog

Tutoriel

API PixVerse V6 image-vers-vidéo : exemples curl et Python directement exécutables

Oct 20, 2026
API PixVerse V6 image-vers-vidéo : exemples curl et Python directement exécutables
Modèle
pixverse-v6

Pour animer une image statique avec pixverse-v6/image-to-video sur hiapi, il suffit d'un appel POST /v1/tasks et d'une interrogation répétée (polling) — exactement le même schéma de tâches asynchrones que celui utilisé par tous les modèles de génération de la plateforme. Sa différence avec le point de terminaison texte-vers-vidéo réside dans les entrées : vous fournissez une image de départ (image_url) ainsi qu'un prompt décrivant le mouvement souhaité. Ce guide fournit des exemples curl et Python directement copiables, les champs d'entrée réellement validés par ce point de terminaison, et un piège inattendu qui surprend ceux qui viennent du texte-vers-vidéo.

Ce dont vous avez besoin

  • Une clé API hiapi. Créez-la dans la console et envoyez-la via Authorization: Bearer sk-<your-key>. Le format de l'en-tête est décrit dans la documentation Authentification.
  • L'ID de modèle pixverse-v6/image-to-video (conservez la barre oblique — elle fait partie de l'ID, ce n'est pas un chemin d'URL). Il s'agit d'un ID de modèle différent de pixverse-v6/text-to-video.
  • Une URL d'image accessible publiquement comme première image. La plateforme récupère cette image côté serveur, donc les liens localhost ou ceux d'un bucket privé ne fonctionneront pas — hébergez l'image de départ à un emplacement accessible en GET par l'API.
  • Avec curl, rien à installer. Pour l'exemple Python, il suffit de la bibliothèque standard et de requests.

PixVerse V6 image-vers-vidéo prend l'image statique que vous fournissez comme image de départ et l'anime en un clip de 1 à 15 secondes. Il fonctionne via l'API de tâches asynchrones unifiée — vous soumettez la tâche, interrogez le résultat, puis téléchargez l'URL de sortie avant son expiration.

Requête minimale fonctionnelle (curl)

Soumettez la tâche à POST /v1/tasks. image_url et prompt sont tous deux obligatoires :

curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-<your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "pixverse-v6/image-to-video",
    "input": {
      "image_url": "https://cdn.example.com/your-first-frame.jpg",
      "prompt": "Slow push-in; the subject turns toward the camera and smiles, soft breeze in the hair",
      "duration": 5,
      "resolution": "720p"
    }
  }'

La réponse renvoie un ID de tâche :

{ "code": 200, "data": { "taskId": "a1b2c3d4-...." } }

Interrogez ensuite GET /v1/tasks/<taskId> jusqu'à ce que data.status vaille success :

curl -s https://api.hiapi.ai/v1/tasks/a1b2c3d4-.... \
  -H "Authorization: Bearer sk-<your-key>"

En cas de succès, l'URL de la vidéo terminée se trouve dans data.output[0].url :

{
  "code": 200,
  "data": {
    "status": "success",
    "output": [{ "url": "https://temp.hiapi.ai/....mp4", "expireAt": 1760000000 }]
  }
}

Cette URL de sortie est temporaire — elle porte un horodatage expireAt. Téléchargez immédiatement le fichier .mp4 et enregistrez-le dans votre propre bucket ; ne créez pas de lien direct (hotlink) vers cette URL.

Le même flux en Python

import time, requests

BASE = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": "Bearer sk-<your-key>", "Content-Type": "application/json"}

def create_task():
    payload = {
        "model": "pixverse-v6/image-to-video",
        "input": {
            "image_url": "https://cdn.example.com/your-first-frame.jpg",
            "prompt": "Slow push-in; the subject turns toward the camera and smiles",
            "duration": 5,
            "resolution": "720p",
        },
    }
    r = requests.post(BASE, json=payload, headers=HEADERS, timeout=60)
    r.raise_for_status()
    data = r.json()
    task_id = (data.get("data") or {}).get("taskId")
    if not task_id:
        raise RuntimeError(f"create failed: {data}")  # 400s echo the exact bad field
    return task_id

def wait_for(task_id, timeout_s=600):
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        try:
            r = requests.get(f"{BASE}/{task_id}", headers=HEADERS, timeout=30)
            task = (r.json().get("data") or {})
        except requests.RequestException:
            time.sleep(5)          # one transient network blip should not abort the whole poll
            continue
        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(5)              # still pending — keep the SAME task_id, re-poll
    raise TimeoutError(task_id)

task_id = create_task()
url = wait_for(task_id)
video = requests.get(url, timeout=120).content
open("out.mp4", "wb").write(video)
print("saved out.mp4")

La boucle de polling vérifie explicitement success et fail, et continue d'attendre pour tout autre statut ; elle gère donc robustement les libellés d'état intermédiaires que la tâche peut rapporter pendant le rendu. Si une requête de polling échoue, patientez puis réinterrogez le même task_id sauvegardé, plutôt que de resoumettre — une resoumission entraînerait des frais de rendu supplémentaires.

Paramètres réellement validés par pixverse-v6/image-to-video

Voici les champs réellement acceptés par le point de terminaison en production. Je les ai confirmés un par un en envoyant des valeurs invalides et en lisant les erreurs de validation, puis j'ai recoupé avec la documentation du modèle :

Champ Type Valeurs / plage Description
image_url string une URL d'image publique Obligatoire. Première image. Une seule chaîne d'URL — pas un tableau.
prompt string — Obligatoire. Décrit le mouvement appliqué à cette image.
duration integer 1–15, par défaut 5 En secondes. Doit être un entier, pas une chaîne.
resolution string 360p, 540p, 720p, 1080p Résolution de sortie.
negative_prompt string — Facultatif ; contenu à éviter.
seed integer — Facultatif ; à réutiliser pour des résultats reproductibles.
style string anime, 3d_animation, clay, comic, cyberpunk Style prédéfini facultatif.
generate_audio boolean true / false, par défaut false Facultatif ; active/désactive la piste audio/ambiante générée.

Il n'y a pas de champ aspect_ratio ici. C'est la seule véritable différence avec les requêtes texte-vers-vidéo que vous avez pu voir, et il faut la garder en tête (voir ci-dessous).

Points à surveiller en production

  • Pas de aspect_ratio — la géométrie de sortie suit votre image d'entrée. Le point de terminaison texte-vers-vidéo accepte une énumération aspect_ratio ; celui-ci la rejette. Comme vous fournissez la première image, le clip hérite des dimensions de cette image. Si vous avez besoin d'un clip vertical 9:16, recadrez ou complétez votre image_url en 9:16 avant la soumission — aucun réglage de ratio ne peut le remplacer.
  • La structure de la requête est fermée. Tout champ hors du tableau ci-dessus est rejeté avec l'erreur invalid input: <root>: additional properties '<field>' not allowed. Une faute de frappe ou un champ deviné échoue bruyamment à la soumission plutôt que d'être ignoré silencieusement. Des champs comme first_frame, image, input_image, motion_mode et camera_movement sont tous rejetés — l'image de départ se place dans image_url, et le mouvement de caméra s'écrit dans le texte du prompt.
  • duration est un entier, contrairement à certains autres modèles vidéo de la plateforme qui acceptent une chaîne ("6"). Envoyer "5" renvoie invalid input: duration: got string, want integer. Les valeurs hors plage rapportent directement la borne, par exemple duration: maximum: got 999, want 15.
  • Les erreurs renvoient un champ à la fois, dans une enveloppe plate : {"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: image_url: missing required field \"image_url\""}. Corrigez le champ signalé et resoumettez ; le champ invalide suivant (s'il y en a un) apparaîtra à l'appel suivant.
  • Un échec de tâche renvoie quand même HTTP 200. L'échec se trouve dans le corps de la réponse sous la forme data.status: "fail" — vérifiez le champ de statut, pas seulement le code HTTP. Une URL d'image que la plateforme ne peut pas récupérer est une cause fréquente, donc vérifiez d'abord que votre image de départ est publique.
  • Facturation à la seconde, qui augmente avec la résolution et la durée ; un clip 1080p/15s coûte donc bien plus qu'un clip 540p/3s. Consultez les tarifs actuels sur la page des prix avant de lancer des générations en lot.

Ce que j'ai vérifié (et ce que je n'ai pas vérifié)

J'ai établi le contrat d'entrée en envoyant des valeurs délibérément invalides au point de terminaison /v1/tasks de production de pixverse-v6/image-to-video et en lisant les réponses de validation de l'API elle-même — une sonde de schéma en lecture seule, sans callback ni frais de rendu. Découvertes notables :

  • image_url est le champ obligatoire de la première image, et c'est une chaîne unique, pas un tableau. Envoyer un nombre renvoie image_url: got number, want string ; l'omettre renvoie missing required field "image_url". Les champs alternatifs devinables (first_frame, image, input_image) sont tous rejetés comme propriétés inconnues.
  • Ce point de terminaison rejette aspect_ratio (additional properties 'aspect_ratio' not allowed), même s'il est valide sur pixverse-v6/text-to-video. C'est la raison concrète pour laquelle une requête image-vers-vidéo diffère d'une requête texte-vers-vidéo : la forme de sortie est déterminée par votre image, pas par un paramètre.
  • generate_audio est un véritable interrupteur booléen (par défaut false) — envoyer une chaîne renvoie generate_audio: got string, want boolean. Si vous voulez du son, mettez-le à true ; il n'est ni forcé à l'activation ni à la désactivation.

Je n'ai pas rendu de clip lors de cette sonde, donc ce guide ne se prononce pas sur la qualité du mouvement ni sur la fidélité du modèle à l'image d'entrée — il documente uniquement le contrat de requête exact imposé par ce point de terminaison.

Contenu associé

Questions fréquentes

Pourquoi image-vers-vidéo n'a-t-il pas de aspect_ratio ?Parce que le clip commence par votre image image_url et hérite de ses dimensions. Pour contrôler le ratio de sortie, redimensionnez ou recadrez l'image d'entrée avant la soumission — ce point de terminaison rejette aspect_ratio comme champ inconnu.

image_url accepte-t-il plusieurs images ?Non. Sur ce point de terminaison, c'est une chaîne d'URL unique ; passer un tableau provoque une erreur de validation. Elle ne définit que l'image de départ.

duration est-il en secondes ou en images ?En secondes — un entier de 1 à 15, par défaut 5. Il doit être un entier ; la chaîne "5" est rejetée.

En quoi cela diffère-t-il du point de terminaison texte-vers-vidéo ?pixverse-v6/image-to-video anime l'image que vous fournissez (image_url + prompt, sans aspect_ratio). pixverse-v6/text-to-video est un ID de modèle distinct, génère uniquement à partir du prompt et accepte bien aspect_ratio. Utilisez image-vers-vidéo lorsque vous disposez déjà de l'image fixe exacte dont vous voulez animer l'ouverture.

Pourquoi ma requête échoue-t-elle avec « additional properties not allowed » ?Vous avez envoyé un champ absent du schéma. Les entrées acceptent uniquement image_url, prompt, duration, resolution, negative_prompt, seed, style et generate_audio.

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