HiAPI
Blog

Tutoriel

API de génération de vidéo à partir de texte PixVerse V6 : un exemple curl et Python qui fonctionne

Oct 20, 2026
API de génération de vidéo à partir de texte PixVerse V6 : un exemple curl et Python qui fonctionne
Modèle
pixverse-v6

Générer une vidéo sur hiapi avec pixverse-v6/text-to-video revient essentiellement à faire un POST /v1/tasks suivi d'une boucle de polling — exactement le même modèle de tâche asynchrone que celui utilisé par chaque modèle de génération de la plateforme. Cet article vous donne des exemples curl et Python directement copiables, les champs d'entrée que l'endpoint valide réellement, ainsi que le cycle de vie des tâches et les pièges que j'ai notés lors d'une génération réellement exécutée.

Ce qu'il faut avant de commencer

  • Une clé API hiapi. Créez-la dans la console, puis transmettez-la avec l'en-tête Authorization: Bearer sk-<votre key>. Le format de l'en-tête est décrit dans la documentation d'authentification.
  • L'id du modèle est pixverse-v6/text-to-video. Conservez la barre oblique du milieu — elle fait partie de l'id, ce n'est pas un chemin d'URL.
  • curl ne nécessite aucune installation ; l'exemple Python n'utilise que la bibliothèque standard et requests.

PixVerse V6 text-to-video transforme un prompt textuel en un clip vidéo de 1 à 15 secondes, de 360p à 1080p. Il passe par l'interface de tâches asynchrones unifiée : soumettre la tâche → interroger le résultat → télécharger la vidéo avant l'expiration de l'URL de sortie.

Requête minimale fonctionnelle (curl)

Soumettez la tâche à POST /v1/tasks :

curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-<votre key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "pixverse-v6/text-to-video",
    "input": {
      "prompt": "Au soleil du matin, un renard roux traverse la neige fraîche au petit trot, rendu cinématographique, ralenti",
      "aspect_ratio": "16:9",
      "duration": 5,
      "resolution": "720p"
    }
  }'

La réponse fournit un id de tâche :

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

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

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

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

{
  "code": 200,
  "data": {
    "status": "success",
    "storage": "temp",
    "output": [
      { "type": "video", "url": "https://temp.hiapi.ai/....mp4", "expireAt": 1793036664 }
    ]
  },
  "message": "success"
}

Cette URL de sortie est temporaire — elle contient un horodatage expireAt (d'après mes tests, 7 jours après la génération). Téléchargez immédiatement le fichier .mp4 et stockez-le dans votre propre stockage objet ; ne créez pas de lien direct vers cette adresse.

Le même flux (Python)

import time, requests

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

def create_task():
    payload = {
        "model": "pixverse-v6/text-to-video",
        "input": {
            "prompt": "Au soleil du matin, un renard roux traverse la neige fraîche au petit trot, rendu cinématographique, ralenti",
            "aspect_ratio": "16:9",
            "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"Échec de la création : {data}")
    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)          # une instabilité réseau ponctuelle ne doit pas interrompre tout le polling
            continue
        status = task.get("status")
        if status == "success":
            return task["output"][0]["url"]
        if status == "fail":
            raise RuntimeError(f"Échec de la tâche : {task.get('error')}")
        time.sleep(5)              # toujours en file d'attente/rendu — continuez d'interroger avec le même task_id
    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("out.mp4 enregistré")

La boucle de polling ne teste que les deux états finaux success et fail, et continue d'attendre pour tout autre état — ainsi, que le rendu en cours signale handling ou archiving, le code ne se trompe pas. Point essentiel : si une requête de polling échoue elle-même, patientez puis réinterrogez avec le même task_id conservé, ne soumettez pas à nouveau la tâche — une nouvelle soumission relancerait un rendu et facturerait une seconde fois.

Les paramètres réellement validés par pixverse-v6/text-to-video

Voici les champs que l'endpoint accepte réellement. Pour chacun, j'ai confirmé en envoyant une valeur invalide et en relisant son propre message de validation, puis j'ai recoupé avec la documentation du modèle :

Champ Type Valeurs / plage Valeur par défaut Description
prompt string 1–2048 octets UTF-8 (1 caractère chinois = 3 octets, environ 682 caractères) — Obligatoire.
aspect_ratio enum 16:9, 4:3, 1:1, 3:4, 9:16, 2:3, 3:2, 21:9 16:9 Notez qu'il prend en charge 21:9 (ultra-large) et 2:3/3:2.
duration integer 1–15 5 En secondes, doit être un entier, pas une chaîne.
resolution enum 360p, 540p, 720p, 1080p 720p Résolution de sortie.
generate_audio boolean true / false false Générer également musique de fond, effets sonores et dialogues ; désactivé par défaut, activez-le explicitement pour avoir de l'audio.
negative_prompt string 2048 octets UTF-8 maximum — Facultatif ; ce que vous voulez éviter.
seed integer N'importe quel entier — Facultatif ; même seed + même prompt permet de reproduire le même résultat.
style enum anime, 3d_animation, clay, comic, cyberpunk aucune Préréglage de style facultatif ; laissez vide pour conserver un rendu naturel.

Pour utiliser un callback plutôt que le polling, vous pouvez aussi ajouter un objet callback au même niveau que input : url (adresse de callback HTTPS) + when (actuellement uniquement final, c'est-à-dire un callback à l'état final de la tâche).

Bonnes pratiques en production

  • Le schéma est fermé. Tout champ absent du tableau ci-dessus est directement rejeté, avec invalid input: <root>: additional properties '<champ>' not allowed. C'est en fait une bonne chose : un champ mal orthographié ou inventé échoue bruyamment dès la soumission, au lieu d'être silencieusement ignoré.
  • duration est un entier, ce qui diffère de certains modèles vidéo de la plateforme qui acceptent une chaîne ("6"). Envoyer "5" donne invalid input: duration: got string, want integer ; une valeur hors limites indique directement la borne, par exemple duration: maximum: got 999, want 15, duration: minimum: got 0, want 1.
  • Les erreurs ne signalent qu'un seul champ à la fois, l'enveloppe est plate : {"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: aspect_ratio: value must be one of ..."}. Corrigez le champ signalé puis renvoyez ; le prochain champ invalide (s'il y en a) apparaîtra à l'appel suivant.
  • En cas d'échec de la tâche, le HTTP reste 200. L'information d'échec se trouve dans data.status: "fail" du corps de la réponse ; c'est ce champ status qu'il faut évaluer, pas le code de statut HTTP.
  • Polling ou callback ? Le polling est le plus simple pour un script léger ; pour passer à l'échelle sans maintenir de connexion, utilisez callback. Dans les deux cas, dès que vous obtenez output[0].url, téléchargez immédiatement — l'URL contient expireAt et renverra 404 après expiration.

Journal de test : je l'ai vraiment exécuté

Cet article ne se limite pas à des sondes en lecture seule — j'ai parcouru tout le cycle de vie avec une vraie requête, consigné ci-dessous :

  • Création : la requête 720p / 5 secondes / 16:9 ci-dessus a renvoyé immédiatement {"code":200,"data":{"taskId":"tk-hiapi-01M5AM..."},"message":"success"}. Notez que le taskId est une chaîne préfixée par tk-hiapi-, pas un UUID.
  • Polling : le data.status de GET /v1/tasks/<id> est passé successivement par handling → archiving → success, le clip de 5 secondes a pris environ 74 secondes de la création à l'achèvement.
  • Sortie : à l'état final, data.output[0] vaut {"type":"video","url":"https://temp.hiapi.ai/....mp4","expireAt":...,"artifactId":"...","storage":"temp"}. expireAt moins l'heure de création donne exactement 604800 secondes, soit 7 jours.
  • Artefact : un HEAD sur cette URL de sortie renvoie content-type: video/mp4, une taille d'environ 2,2 Mo, confirmant un fichier mp4 valide.

Une correction concernant l'audio mérite d'être notée à part : j'ai sondé plusieurs noms de champs voisins avec des valeurs invalides — generate_audio renvoie got string, want boolean (ce qui indique que c'est un champ booléen valide), tandis que sound et audio renvoient tous additional properties not allowed (inexistants). Autrement dit, l'audio est contrôlé par l'interrupteur generate_audio, par défaut false ; la page de tarification propose d'ailleurs deux niveaux « Sound On / Sound Off ». Lors de ce test, je n'ai pas activé generate_audio, la sortie est donc une vidéo muette.

Je n'ai pas porté d'évaluation subjective sur la qualité visuelle (cette fois, je n'ai vérifié que le contrat de requête, le cycle de vie de la tâche et le fait que le produit est un mp4 valide) ; jugez cette partie vous-même après avoir généré avec vos propres prompts.

Comment le coût est calculé

pixverse-v6/text-to-video est facturé à la seconde (le facteur de facturation est duration), plus la durée est longue plus c'est cher ; une résolution plus élevée, ainsi que l'activation de generate_audio en mode « Sound On », augmentent le prix par seconde. Les tarifs précis à la seconde sont ceux des données en temps réel de la page de tarification ; jetez-y un œil avant toute génération en lot.

Liens utiles

Questions fréquentes

duration est en secondes ou en images ? En secondes — n'importe quel entier entre 1 et 15, par défaut 5.

Peut-on générer une vidéo avec du son ? Oui. Réglez generate_audio sur true, le modèle génère alors musique de fond, effets sonores et dialogues ; la valeur par défaut est false (muet). L'activation de l'audio passe par un niveau de facturation plus élevé, voir la page de tarification.

Quelle est la longueur maximale du prompt ? La limite est de 2048 octets UTF-8. Un caractère chinois compte pour 3 octets, donc environ 682 caractères chinois au maximum.

Quelle différence avec image-to-video ? Cet endpoint (pixverse-v6/text-to-video) part uniquement d'un prompt textuel. Image-to-video est un autre model id, qui reçoit une image de départ et l'anime — pour animer une image existante plutôt que générer à partir de rien, utilisez celui-là.

Pourquoi ma requête renvoie-t-elle « additional properties not allowed » ? Vous avez transmis un champ absent du schéma. L'entrée n'accepte que prompt, aspect_ratio, duration, resolution, generate_audio, negative_prompt, seed et style.

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