HiAPI
Blog

Tutorial

PixVerse V6 Bild-zu-Video-API: direkt ausführbare curl- und Python-Beispiele

Oct 20, 2026
PixVerse V6 Bild-zu-Video-API: direkt ausführbare curl- und Python-Beispiele
Modell
pixverse-v6

Um mit pixverse-v6/image-to-video auf hiapi ein statisches Bild zu animieren, genügt ein einziger POST /v1/tasks-Aufruf plus ein Polling – genau dasselbe asynchrone Aufgabenmuster, das alle generativen Modelle auf der Plattform verwenden. Der Unterschied zum Text-zu-Video-Endpunkt liegt in der Eingabe: Sie müssen ein Startbild (image_url) sowie einen Prompt angeben, der beschreibt, wie es sich bewegen soll. Dieser Leitfaden enthält direkt kopierbare curl- und Python-Beispiele, die Eingabefelder, die dieser Endpunkt tatsächlich validiert, sowie eine Überraschung, über die alle stolpern, die von Text-zu-Video kommen.

Was Sie vorbereiten müssen

  • Einen hiapi-API-Schlüssel. Erstellen Sie ihn in der Konsole und senden Sie ihn über Authorization: Bearer sk-<your-key>. Das Header-Format finden Sie in der Authentifizierungsdokumentation.
  • Die Modell-ID pixverse-v6/image-to-video (der Schrägstrich bleibt erhalten – er gehört zur ID, nicht zum URL-Pfad). Dies ist eine andere Modell-ID als pixverse-v6/text-to-video.
  • Eine öffentlich erreichbare Bild-URL als erstes Frame. Die Plattform ruft das Bild serverseitig ab, daher funktionieren localhost- oder Links zu privaten Buckets nicht – hosten Sie das Startbild an einem Ort, der für die API per GET erreichbar ist.
  • Mit curl müssen Sie nichts installieren. Für das Python-Beispiel benötigen Sie nur die Standardbibliothek plus requests.

PixVerse V6 Bild-zu-Video verwendet Ihr statisches Bild als Startframe und animiert es zu einem Clip von 1–15 Sekunden. Es läuft über die einheitliche asynchrone Aufgaben-API – Sie reichen eine Aufgabe ein, fragen das Ergebnis ab und laden es herunter, bevor die Ausgabe-URL abläuft.

Minimal funktionierende Anfrage (curl)

Reichen Sie die Aufgabe bei POST /v1/tasks ein. image_url und prompt sind beide erforderlich:

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": "Langsamer Push-in; die Person dreht sich zur Kamera und lächelt, sanfte Brise im Haar",
      "duration": 5,
      "resolution": "720p"
    }
  }'

Die Antwort liefert eine Aufgaben-ID zurück:

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

Fragen Sie dann GET /v1/tasks/<taskId> ab, bis data.status gleich success ist:

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

Bei Erfolg befindet sich die URL des fertigen Videos in data.output[0].url:

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

Diese Ausgabe-URL ist temporär – sie trägt einen expireAt-Zeitstempel. Laden Sie die .mp4 sofort herunter und speichern Sie sie in Ihrem eigenen Bucket; verlinken Sie sie nicht direkt per Hotlink.

Derselbe Ablauf in 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": "Langsamer Push-in; die Person dreht sich zur Kamera und lächelt",
            "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")

Die Polling-Schleife prüft explizit auf success und fail und wartet bei jedem anderen Status weiter, sodass sie robust mit allen Zwischenstatus-Labels umgeht, die die Aufgabe während des Renderns meldet. Wenn eine Polling-Anfrage fehlschlägt, schlafen Sie kurz und fragen Sie die gleiche gespeicherte task_id erneut ab, statt neu einzureichen – eine erneute Einreichung verursacht ein zweites Rendering und damit erneut Kosten.

Parameter, die pixverse-v6/image-to-video tatsächlich validiert

Im Folgenden finden Sie die Felder, die der Live-Endpunkt tatsächlich akzeptiert. Ich habe sie einzeln bestätigt, indem ich ungültige Werte gesendet und die Validierungsfehler gelesen habe, und sie anschließend mit der Modelldokumentation abgeglichen:

Feld Typ Wert / Bereich Beschreibung
image_url string eine öffentliche Bild-URL Erforderlich. Erstes Frame. Ein einzelner URL-String – kein Array.
prompt string — Erforderlich. Beschreibt die auf das Frame angewendete Bewegung.
duration integer 1–15, Standard 5 In Sekunden. Muss eine Ganzzahl sein, kein String.
resolution string 360p, 540p, 720p, 1080p Ausgabauflösung.
negative_prompt string — Optional; Inhalte, die vermieden werden sollen.
seed integer — Optional; wiederverwenden für reproduzierbare Ergebnisse.
style string anime, 3d_animation, clay, comic, cyberpunk Optionaler voreingestellter Stil.
generate_audio boolean true / false, Standard false Optional; schaltet generierte Tonspur/Umgebungsgeräusche ein oder aus.

Es gibt hier kein aspect_ratio-Feld. Dies ist der einzige wirkliche Unterschied zu Text-zu-Video-Anfragen, die Sie vielleicht kennen, und es lohnt sich, ihn im Hinterkopf zu behalten (siehe unten).

Hinweise für den Produktivbetrieb

  • Kein aspect_ratio – die Ausgabegeometrie folgt Ihrem Eingabebild. Der Text-zu-Video-Endpunkt akzeptiert ein aspect_ratio-Enum; dieser Endpunkt lehnt es ab. Da Sie das erste Frame liefern, übernimmt der Clip dessen Abmessungen. Wenn Sie einen 9:16-Hochformat-Clip benötigen, schneiden Sie image_url vor dem Einreichen auf 9:16 zu oder füllen Sie es entsprechend auf – es gibt keinen Seitenverhältnis-Regler, der das überschreiben könnte.
  • Die Anfragestruktur ist geschlossen. Jedes Feld außerhalb der obigen Tabelle wird abgelehnt mit dem Fehler invalid input: <root>: additional properties '<field>' not allowed. Tippfehler oder geratene Felder schlagen beim Einreichen laut fehl, statt still ignoriert zu werden. Felder wie first_frame, image, input_image, motion_mode und camera_movement werden alle abgelehnt – das Startbild gehört in image_url, Kamerabewegungen werden im prompt-Text beschrieben.
  • duration ist eine Ganzzahl, anders als bei einigen anderen Videomodellen der Plattform, die Strings ("6") akzeptieren. Das Senden von "5" liefert invalid input: duration: got string, want integer. Werte außerhalb des Bereichs melden die Grenze direkt, z. B. duration: maximum: got 999, want 15.
  • Fehler werden jeweils für ein Feld zurückgegeben, im flachen Envelope-Format: {"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: image_url: missing required field \"image_url\""}. Korrigieren Sie das gemeldete Feld und reichen Sie erneut ein; das nächste ungültige Feld (falls vorhanden) taucht beim nächsten Aufruf auf.
  • Aufgabenfehler liefern trotzdem HTTP 200. Die Fehlerinformation steht im Antworttext als data.status: "fail" – prüfen Sie das Statusfeld, nicht nur den HTTP-Statuscode. Eine Bild-URL, die die Plattform nicht abrufen kann, ist eine häufige Ursache, stellen Sie also zuerst sicher, dass Ihr Startframe öffentlich ist.
  • Abrechnung pro Sekunde, die mit Auflösung und Dauer steigt, sodass ein 1080p/15s-Clip deutlich teurer ist als ein 540p/3s-Clip. Prüfen Sie die aktuellen Tarife auf der Preisseite, bevor Sie in großem Umfang generieren.

Was ich verifiziert habe (und was nicht)

Ich habe den Eingabevertrag ermittelt, indem ich absichtlich ungültige Werte an den Live-Endpunkt /v1/tasks von pixverse-v6/image-to-video gesendet und die Validierungsantworten der API selbst gelesen habe – eine reine Lese-Schema-Sondierung ohne Callback und ohne Rendering-Kosten. Konkrete Erkenntnisse, die es sich zu merken lohnt:

  • image_url ist das erforderliche Feld für das erste Frame und ein einzelner String, kein Array. Das Senden einer Zahl liefert image_url: got number, want string; das Weglassen liefert missing required field "image_url". Naheliegende Alternativfelder (first_frame, image, input_image) werden alle als unbekannte Eigenschaften abgelehnt.
  • Dieser Endpunkt lehnt aspect_ratio ab (additional properties 'aspect_ratio' not allowed), obwohl es bei pixverse-v6/text-to-video gültig ist. Das ist der konkrete Grund, warum Bild-zu-Video-Anfragen anders aussehen als Text-zu-Video-Anfragen: Die Ausgabeform wird durch Ihr Frame bestimmt, nicht durch einen Parameter.
  • generate_audio ist ein echter boolescher Schalter (Standard false) – das Senden eines Strings liefert generate_audio: got string, want boolean. Wenn Sie Ton wünschen, setzen Sie ihn auf true; er wird nicht zwangsweise ein- oder ausgeschaltet.

Bei dieser Sondierung habe ich keine Clips gerendert, daher macht dieser Leitfaden keine Aussagen über die Bewegungsqualität oder darüber, wie treu das Modell das Eingabeframe beibehält – er dokumentiert lediglich den exakten Anfragevertrag, den dieser Endpunkt durchsetzt.

Verwandte Inhalte

Häufig gestellte Fragen

Warum gibt es bei Bild-zu-Video kein aspect_ratio?Weil der Clip mit Ihrem image_url-Frame beginnt und dessen Abmessungen übernimmt. Um das Ausgabeverhältnis zu steuern, ändern Sie die Größe des Eingabebilds oder schneiden Sie es vor dem Einreichen zu – der Endpunkt lehnt aspect_ratio als unbekanntes Feld ab.

Akzeptiert image_url mehrere Bilder?Nein. Bei diesem Endpunkt ist es ein einzelner URL-String; das Übergeben eines Arrays führt zu einem Validierungsfehler. Es legt ausschließlich das Startframe fest.

Ist die Einheit von duration Sekunden oder Frames?Sekunden – eine beliebige Ganzzahl von 1 bis 15, Standard 5. Muss eine Ganzzahl sein; der String "5" wird abgelehnt.

Worin unterscheidet sich das vom Text-zu-Video-Endpunkt?pixverse-v6/image-to-video animiert das von Ihnen bereitgestellte Frame (image_url + prompt, kein aspect_ratio). pixverse-v6/text-to-video ist eine separate Modell-ID, generiert nur aus dem Prompt und akzeptiert tatsächlich aspect_ratio. Verwenden Sie Bild-zu-Video, wenn Sie bereits das exakte statische Startbild haben, das Sie in Bewegung setzen möchten.

Warum schlägt meine Anfrage mit „additional properties not allowed“ fehl?Sie haben ein Feld gesendet, das im Schema nicht existiert. Die Eingabe akzeptiert nur image_url, prompt, duration, resolution, negative_prompt, seed, style und generate_audio.

Ähnliche Artikel

Alle Artikel ansehen →

Starte mit persistenten KI-Artefakten

Hol dir deinen API-Key und rufe Bild-, Video- und Audiomodelle über eine einzige task-basierte API auf.

API-Dokumentation lesen