Tutorial
Mehrmodell-AI-Anwendungen mit einer einzigen einheitlichen API auf hiapi erstellen
- Modell
- seedance-2.5
Was du baust
Die meisten Teams rufen für ein Produkt am Ende drei oder vier verschiedene KI-Anbieter auf: ein Text-zu-Bild-Modell für Thumbnails, ein Text-zu-Video-Modell für kurze Social-Videos und vielleicht noch ein TTS-Modell für Voice-over. Jeder Anbieter hat sein eigenes SDK, sein eigenes Authentifizierungsschema und seine eigene Polling-Logik. hiapi fasst all das in einem einzigen Endpunktmuster zusammen: POST /v1/tasks erstellt eine Aufgabe, GET /v1/tasks/:id fragt die Aufgabe ab – und das gilt für jedes generative Modell der Plattform.
Dieses Tutorial demonstriert dieses Integrationsmuster an drei realen und derzeit live verfügbaren Modellen – z-image (Text zu Bild), qwen-image-2.0 (Text zu Bild, anderes Schema) und seedance-2.5 (Text zu Video) – damit du siehst, wo die API vollständig identisch ist und wo sich nur das modellspezifische input-Objekt unterscheidet.
Voraussetzungen
- Ein hiapi API key aus der Konsole
curloder einen beliebigen HTTP-Client, der JSON senden kann- Kein SDK erforderlich – jedes Beispiel unten ist ein reiner HTTP-Aufruf
Jede Anfrage benötigt einen Request-Header:
Authorization: Bearer <your-api-key>
Bei ungültigem oder fehlendem Key wird ein Fehler der 401-Klasse zurückgegeben, bevor irgendein Modell erreicht wird:
{
"error": {
"code": "permission_denied",
"message": "invalid api key",
"request_id": "req_...",
"type": "hiapi_error"
}
}
Es gibt keinen schlüssellosen oder anonymen Modus – jeder /v1/tasks-Aufruf muss ein gültiges Bearer-Token mitführen. Zu Key-Rotation und Geltungsbereichen siehe Authentifizierung.
Minimales lauffähiges Beispiel
1. Aufgabe erstellen
Die Struktur des Erstellungsaufrufs ist immer gleich: eine model-ID und ein modellspezifisches input-Objekt. So erstellst du mit z-image eine Bildaufgabe:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "z-image",
"input": {
"prompt": "a minimalist workspace, soft morning light, isometric illustration",
"aspect_ratio": "16:9"
}
}'
Die Antwort kommt sofort zurück – die Generierung erfolgt asynchron:
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "queued"
}
}
Nun dasselbe Muster mit qwen-image-2.0. Derselbe Endpunkt, dieselbe äußere Struktur – nur das input unterscheidet sich (dieses Modell erwartet einen size-String statt aspect_ratio):
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-image-2.0",
"input": {
"prompt": "a minimalist workspace, soft morning light, isometric illustration",
"size": "2048*2048"
}
}'
Erstelle mit seedance-2.5/text-to-video eine Videoaufgabe – weiterhin dieselbe äußere Struktur, jetzt mit duration und einer breiteren aspect_ratio-Enumeration:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.5/text-to-video",
"input": {
"prompt": "a paper airplane gliding through a sunlit office, slow motion",
"duration": 5,
"aspect_ratio": "16:9"
}
}'
Das ist der Kernpunkt: Die äußere Struktur der Aufgabe ändert sich nie, nur das input ändert sich. Jedes Modell auf hiapi dokumentiert sein eigenes input-Schema auf seiner Modellseite – sieh dir vor dem Bau einer neuen Integration immer das Schema des konkreten Modells an, denn Felder wie aspect_ratio, size und duration sind zwischen Modellen nicht austauschbar.
2. Ergebnis abfragen
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-xxxxxxxxxxxx \
-H "Authorization: Bearer $HIAPI_API_KEY"
status durchläuft queued → handling → archiving und endet schließlich in einem der Endzustände success oder fail. Bei Erfolg ist data.output ein Array – die meisten Bild-/Videomodelle geben einen Eintrag zurück, aber eine einzelne Aufgabe kann mehrere Ausgabetypen liefern (zum Beispiel gibt ein Videomodell gleichzeitig video und thumbnail zurück):
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "success",
"output": [
{ "type": "image", "url": "https://...", "expireAt": "2026-10-09T00:00:00Z" }
]
}
}
expireAt ist wichtig – Ausgabe-URLs sind standardmäßig temporär. Lade die Bytes herunter, sobald die Aufgabe abgeschlossen ist (oder lade sie in deinen eigenen Speicher hoch). Wenn du möchtest, dass die Plattform Dateien länger aufbewahrt, setze bei der Erstellung storage: "persistent" oder führe ein nachträgliches Upgrade durch – siehe Speicher.
Im Fehlerfall erhältst du ein error-Objekt statt output:
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "fail",
"error": { "code": "...", "message": "..." }
}
}
Die folgende minimale Python-Polling-Schleife funktioniert für alle oben genannten Modelle gleichermaßen:
import time
import requests
def run_task(model: str, input_payload: dict, api_key: str) -> list[dict]:
headers = {"Authorization": f"Bearer {api_key}"}
create = requests.post(
"https://api.hiapi.ai/v1/tasks",
headers=headers,
json={"model": model, "input": input_payload},
).json()
task_id = create["data"]["taskId"]
time.sleep(3) # initial wait before first poll
while True:
poll = requests.get(
f"https://api.hiapi.ai/v1/tasks/{task_id}", headers=headers
).json()
status = poll["data"]["status"]
if status == "success":
return poll["data"]["output"]
if status == "fail":
raise RuntimeError(poll["data"]["error"])
time.sleep(4) # 3-5s between polls; use 5-10s for video models
Produktionsmuster
Callbacks statt Polling in der Produktion verwenden
Polling ist für Skripte oder CLI-Tools in Ordnung, aber bei jedem realen Traffic-Volumen sind Callbacks vorzuziehen: Übergib beim Erstellen der Aufgabe ein callback-Objekt, und hiapi sendet das Endergebnis per POST an deinen Endpunkt, statt dass du in einer Schleife wiederholt GET /v1/tasks/:id aufrufst.
{
"model": "z-image",
"input": { "prompt": "...", "aspect_ratio": "1:1" },
"callback": { "url": "https://yourapp.com/webhooks/hiapi", "when": "final" }
}
when: "final" bedeutet, dass du nur einen einzigen POST erhältst – ausgelöst im Endzustand (success oder fail), ohne das Rauschen der Zwischenzustände handling/archiving. Wenn du einen Callback konfiguriert hast und nichts ankommt, prüfe, ob dein Endpunkt öffentlich erreichbar ist und schnell mit 2xx antwortet; zum häufigsten Grund für still verlorene Callbacks siehe diesen verwandten Artikel.
Idempotente Wiederholungsversuche
Netzwerkschwankungen sind unvermeidlich. Wenn du nach einem Timeout den POST /v1/tasks-Aufruf ohne Schutz erneut versuchst, könntest du doppelte Aufgaben erstellen (und bezahlen). Sende einen Idempotency-Key-Header (eine beliebige Zeichenkette mit höchstens 255 Byte, üblicherweise eine pro logischer Anfrage generierte UUID); Wiederholungsversuche mit demselben Key geben die ursprüngliche Aufgabe zurück, statt eine neue zu erstellen:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Idempotency-Key: 8f14e45f-ceea-467e-bd5f-...-9a2c" \
-H "Content-Type: application/json" \
-d '{"model": "z-image", "input": {"prompt": "...", "aspect_ratio": "1:1"}}'
Validierungsfehler modellspezifisch behandeln
Da jedes Modell sein eigenes input-Schema hat, ist das häufigste Fehlermuster ein 400 für ein bestimmtes Feld – ein Feld, das für ein Modell gültig, für ein anderes ungültig ist, etwa wenn aspect_ratio an ein Modell gesendet wird, das size erwartet:
{
"code": 400,
"data": null,
"error_code": "INVALID_REQUEST",
"message": "invalid input: aspect_ratio: value must be one of '1:1', '4:3', '3:4', '16:9', '9:16'"
}
Wenn du einen Mehrmodell-Router baust (bei dem Benutzer oder deine eigene Logik zur Laufzeit das Modell wählen), pflege eine kleine Registry von modellspezifischen Input-Buildern statt einer gemeinsamen Payload-Struktur:
MODEL_INPUT_BUILDERS = {
"z-image": lambda prompt, **kw: {
"prompt": prompt, "aspect_ratio": kw.get("aspect_ratio", "1:1")
},
"qwen-image-2.0": lambda prompt, **kw: {
"prompt": prompt, "size": kw.get("size", "2048*2048")
},
"seedance-2.5/text-to-video": lambda prompt, **kw: {
"prompt": prompt,
"duration": kw.get("duration", 5),
"aspect_ratio": kw.get("aspect_ratio", "16:9"),
},
}
def build_task_payload(model: str, prompt: str, **kw) -> dict:
return {"model": model, "input": MODEL_INPUT_BUILDERS[model](prompt, **kw)}
So sind die Besonderheiten jedes Modells an einer Stelle gebündelt statt an jedem Aufrufpunkt verstreut. Bei nicht vorhandenen oder kontofremden taskId wird ein gewöhnliches 404 zurückgegeben:
{ "code": 404, "message": "task not found", "data": null }
Und ein temporäres 503 sollte mit exponentiellem Backoff erneut versucht werden, statt es direkt an den Benutzer weiterzugeben – Raten- und Kapazitätsgrenzen sind in Ratenbegrenzungen dokumentiert.
Weiterführende Literatur
- Referenz zur asynchronen Task-API – der vollständige
create/detail-Vertrag - Authentifizierung – Geltungsbereiche und Rotation von API keys
- Speicher – Ausgaben über die standardmäßige Ablaufzeit hinaus aufbewahren
- Warum dein Task-Callback möglicherweise nicht ausgelöst wird
- Alle Modelle durchsuchen – das jeweilige
input-Schema jedes Modells steht auf seiner Seite - Preise – Kosten werden pro Modell und Ausgabe berechnet, nicht einheitlich pro Anfrage
Häufige Fragen
Braucht jedes Modell ein eigenes SDK oder einen eigenen API key?
Nein. Ein API key, ein Endpunktmuster (/v1/tasks) – gilt für jedes Modell der Plattform. Nur das input-Objekt im Request-Body ändert sich.
Warum enthält die Antwort direkt nach dem Erstellen der Aufgabe noch kein output?
Die Generierung ist asynchron. Der Erstellungsaufruf gibt nur taskId und status: "queued" zurück – du musst GET /v1/tasks/:id abfragen (oder einen Callback verwenden), bis status den Wert success oder fail erreicht.
Kann ich die API für schnelle Tests ohne Authentifizierung aufrufen?
Nein – jeder /v1/tasks-Aufruf benötigt einen gültigen Authorization: Bearer-Key. Für Produktionsaufrufe gibt es keinen schlüssellosen oder Demo-Modus; erzeuge zuerst einen Key in der Konsole.
Was passiert, wenn ich nach Ablauf der Ausgabe-URL abfrage?
Der Task-Datensatz selbst bleibt erhalten, aber die output[].url nicht-persistenter Aufgaben ist nach expireAt nicht mehr auflösbar. Lade die Dateien bei Erfolg sofort herunter oder setze bei der Erstellung storage: "persistent", wenn du sie langfristig aufbewahren möchtest.
Wie finde ich heraus, welche Felder ein Modell akzeptiert?
Sieh dir die Seite des jeweiligen Modells unter Modelle an – jedes dokumentiert sein exaktes input-Schema. Gehe nicht davon aus, dass aspect_ratio, size oder duration zwischen Modellen universell einsetzbar sind; wie oben gezeigt, unterscheiden sich z-image und qwen-image-2.0 bereits darin, wie sie das Bildformat ausdrücken.
Ähnliche Artikel
Alle Artikel ansehen →-
Tutorial
So rufen Sie hailuo-2.3-fast/image-to-video über die HiAPI API auf: curl und Python in der Praxis
Die erste lauffähige Anfrage für die schnelle, kostengünstige Bild-zu-Video-Stufe – von der Modell-ID bis zum heruntergeladenen MP4
-
Tutorial
So rufen Sie hailuo-2.3/text-to-video über die HiAPI API auf: curl, Python und lauffähige Anfragebeispiele
-
Tutorial
Kandinsky 6.0 Pro Text-zu-Video auf HiAPI: curl- und Python-Tutorial
Eine anfrageorientierte praktische Demonstration: Aufbau des Aufrufs vom Prompt zum Videoclip, Absenden der Anfrage, Abfragen des Fertigstellungsstatus und Herunterladen des Ergebnisses