HiAPI
Blog

Tutorial

So verwendest du die Kling 2.5 Turbo Image-to-Video API (curl + Python)

Eine lauffähige Übung zur Videoerzeugung aus dem ersten Frame: Task erstellen, Task pollen, MP4 herunterladen – inklusive vollständiger Auflistung des strikten Eingabe-Schemas.

Oct 21, 2026
So verwendest du die Kling 2.5 Turbo Image-to-Video API (curl + Python)

TL;DR

  • kling-2.5-turbo/image-to-video verwandelt ein Erstbild in einen 5- oder 10-sekündigen Videoclip; übergibst du eine zweite Bild-URL, wird der letzte Frame festgelegt. Das Modell läuft auf der asynchronen Task-API von HiAPI – zuerst POST /v1/tasks, um einen Task zu erstellen, dann GET /v1/tasks/{taskId} pollen, um das generierte MP4 abzurufen.
  • Die Ausgabe kostet $0.06 pro Sekunde: $0.30 für einen 5-Sekunden-Clip, $0.60 für einen 10-Sekunden-Clip. Abgerechnet wird pro abgeschlossener Ausgabesekunde; Tasks, die wegen ungültiger Eingaben fehlschlagen, erzeugen keine Ausgabe und werden nicht berechnet.
  • Das Eingabe-Schema ist strikt und geschlossen. Erforderlich: image_urls (Array, 1–2 Einträge – beachte den Plural), prompt, duration (nur 5 oder 10). Optional: aspect_ratio, negative_prompt, cfg_scale. Die Singularform image_url wird direkt abgelehnt.
  • Das Modell ist bereits auf HiAPI verfügbar: Modellseite öffnen, um es zuerst im Browser auszuprobieren und danach die unten beschriebene API anzubinden.

Was du baust und was du vorbereiten musst

Nach dem Lesen dieses Artikels hast du einen funktionierenden Aufruf Erstbild → Video: Bild und Bewegungs-Prompt übermitteln, Task pollen, MP4 herunterladen. Zunächst brauchst du zwei Dinge:

  1. Einen API-Key aus dem HiAPI-Dashboard – jeder Request muss ihn als Authorization: Bearer sk-... mitsenden. Falls du noch keinen erstellt hast, sieh dir die Authentifizierungsdokumentation an.
  2. Eine öffentlich erreichbare URL für das Erstbild. Der Erstellungsaufruf prüft nur die Struktur der Eingabe; das Bild wird erst zur Laufzeit des Tasks abgerufen, daher erzeugt eine localhost- oder private URL einen Task, der anschließend fehlschlägt (Details unten).

Da es sich um Image-to-Video handelt, lautet die Modell-ID kling-2.5-turbo/image-to-video. Wenn du Videos nur anhand eines Prompts generieren möchtest, gibt es einen separaten Text-to-Video-Endpunkt.

Minimales lauffähiges Beispiel (curl)

Task erstellen. Der Modellname steht auf der obersten Ebene; alles, was das Modell liest, steht in 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": "Die Parfümflasche rotiert langsam auf dem Marmorständer, Licht glitzert über das Glas, Blütenblätter schweben herab, langsame Umkreisung",
      "image_urls": ["https://static.hiapi.ai/gallery/2026/10/4852c542c4447593.jpg"],
      "duration": 5,
      "aspect_ratio": "9:16"
    }
  }'

Bei erfolgreicher Erstellung wird eine Task-ID zurückgegeben – kein Video:

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

Dann den Detail-Endpunkt pollen, bis status einen Endzustand erreicht:

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

Während der Task läuft, siehst du Nicht-Endzustände; nach Abschluss findest du die MP4-URL unter 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"
}

Diese output[0].url ist ein temporärer Link – lade sie sofort herunter und verlinke sie nicht direkt in deiner Anwendung (siehe Hinweise für den Produktivbetrieb).

Minimales lauffähiges Beispiel (Python)

Kein SDK nötig – die HiAPI-Task-API ist eine reine HTTP-Schnittstelle, daher reicht die Standardbibliothek:

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 Einträge; der 2. = letzter Frame
            "duration": duration,        # nur 5 oder 10
        },
    }
    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",
    "langsame Umkreisung des Produkts, Licht glitzert über das Glas, Blütenblätter schweben herab",
    duration=5,
)
mp4_url = wait(task_id)

# Sofort herunterladen – die URL ist nur kurz gültig.
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")

Hinweise für den Produktivbetrieb

Pollen oder Callback verwenden. Alle 5 Sekunden zu pollen (wie oben gezeigt) ist der einfachste Weg und der, den die Beispiele nutzen. Wenn du Ergebnisse lieber pushen lassen möchtest, akzeptiert der Erstellungsaufruf ein callback-Objekt auf oberster Ebene – {"url": "https://you/webhook", "when": "final"} – und HiAPI sendet einen POST an deinen Endpunkt, sobald der Task einen Endzustand erreicht. Den genauen Webhook-Vertrag findest du in der Dokumentation zum Erstellen von Tasks; für die meisten Einzelclip-Tasks reicht das Pollen des Task-Detail-Endpunkts.

Verzweige immer anhand von data.status, niemals anhand des HTTP-Statuscodes des Erstellungsaufrufs. Ein 200 beim Erstellen bedeutet akzeptiert, nicht gerendert. Der Clip existiert erst wirklich, wenn der Detail-Endpunkt status: "success" zurückgibt.

Behandle Ausgabe-URLs als temporäre Links. storage ist temp, die URL läuft ab, daher solltest du das MP4 sofort nach erfolgreichem Task herunterladen (oder in deinen eigenen Bucket kopieren). Gib HiAPI-URLs niemals direkt an Endnutzer weiter.

Kostenkontrolle. Du zahlst pro abgeschlossener Ausgabesekunde – $0.06/Sekunde – also $0.30 für einen 5-Sekunden-Clip und $0.60 für einen 10-Sekunden-Clip. Rendere während der Iteration an Prompts und Erstbild zunächst mit 5 Sekunden und führe erst, wenn du zufrieden bist, erneut mit 10 Sekunden aus. Die aktuellen Preise findest du auf der Preisseite.

Praxiserprobte Notizen und Fallstricke

Alles in diesem Artikel stammt aus echten Requests an den Live-Endpunkt (das Schema wurde am 2026-10-21 erneut verifiziert) und nicht nur aus Parametertabellen:

  • Das Feld heißt image_urls (Plural), und das Schema ist geschlossen. Das scheinbar naheliegende image_url schlägt vor der Task-Erstellung fehl:
    { "code": 400, "error_code": "INVALID_REQUEST",
      "message": "invalid input: image_urls: missing required field \"image_urls\"; <root>: additional properties 'image_url' not allowed" }
    
    Jedes nicht erkannte Feld (mode, Tippfehler usw.) wird auf dieselbe Weise abgelehnt – es gibt keine zusätzlichen Felder, die stillschweigend ignoriert werden.
  • duration ist eine Enum, kein Bereich. Nur 5 und 10 werden akzeptiert; 7 gibt duration: value must be one of 5, 10 zurück.
  • image_urls enthält 1 oder 2 Einträge. Eine URL ist der erste Frame; eine zweite URL legt den letzten Frame fest, um einen kontrollierten Übergang zu erzielen. Drei URLs ergeben maxItems: got 3, want 2; ein leeres Array ergibt minItems: got 0, want 1.
  • Optionale Felder, die tatsächlich existieren: aspect_ratio akzeptiert nur 16:9, 9:16, 1:1, 4:3, 3:4, 21:9; cfg_scale hat als Obergrenze 1 (die Übergabe von 99 ergibt cfg_scale: maximum: got 99, want 1); negative_prompt muss ein String sein. Wenn du aspect_ratio weglässt, folgt das Modell deinem Erstbild.
  • Der Erstellungsaufruf validiert nur die Struktur, nicht die Erreichbarkeit – das ist die größte Stolperfalle. Eine syntaktisch gültige, aber nicht erreichbare Bild-URL führt zu 200 und einer taskId, und der Task wechselt anschließend asynchron in den Fehlerzustand:
    { "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." } } }
    
    Dieser Task hat kein output erzeugt und wurde nicht berechnet – Fehler verursachen keine Kosten –, aber wenn du nur die Erstellungsantwort prüfst, gehst du fälschlich davon aus, dass er erfolgreich war. Genau deshalb muss die Polling-Schleife anhand von data.status verzweigen.

Verwandte Inhalte

Häufige Fragen

Wie lang kann der Clip sein? 5 oder 10 Sekunden – das sind die einzigen beiden duration-Werte, die das Schema akzeptiert.

Kann ich das Ende steuern? Ja. Lege zwei URLs in image_urls ab: Die erste ist das Startbild, die zweite der letzte Frame. Das Modell interpoliert die Bewegung dazwischen.

Warum gibt das Erstellen 200 zurück, aber der Task zeigt fail? Der Erstellungsaufruf prüft nur, ob dein JSON dem Schema entspricht. Das Bild wird erst zur Laufzeit des Tasks abgerufen, daher erzeugen nicht erreichbare oder private URLs einen Task und lassen ihn anschließend fehlschlagen. Lies data.status immer vom Detail-Endpunkt und stelle sicher, dass die Erstbild-URL öffentlich erreichbar ist.

Werden fehlgeschlagene Tasks berechnet? Nein. Abgerechnet wird pro abgeschlossener Ausgabesekunde, daher erzeugen Tasks, die wegen ungültiger Eingaben fehlschlagen, kein output und werden nicht berechnet.

Gibt es eine Kontrolle des Seitenverhältnisses? aspect_ratio ist optional und akzeptiert 16:9, 9:16, 1:1, 4:3, 3:4 oder 21:9. Lässt du es weg, folgt das Modell deinem Erstbild.

Ä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