Tutoriel
Créer des applications d'IA multi-modèles sur hiapi avec une API unique et unifiée
- Modèle
- seedance-2.5
Ce que vous allez construire
La plupart des équipes finissent par appeler trois ou quatre fournisseurs d'IA différents pour un seul produit : un modèle texte-image pour les vignettes, un modèle texte-vidéo pour les courtes vidéos sociales, et peut-être un modèle TTS pour la narration. Chaque fournisseur a son propre SDK, son propre schéma d'authentification, sa propre logique de polling. hiapi regroupe tout cela en un seul modèle d'endpoint : POST /v1/tasks pour créer une tâche, GET /v1/tasks/:id pour interroger une tâche, applicable à chaque modèle de génération de la plateforme.
Ce tutoriel démontre ce modèle d'intégration avec trois modèles réels et actuellement en ligne — z-image (texte-image), qwen-image-2.0 (texte-image, schéma différent) et seedance-2.5 (texte-vidéo) — pour vous montrer où l'API est parfaitement identique et où seule l'objet input de chaque modèle diffère.
Prérequis
- Une clé API hiapi obtenue depuis la console
curlou tout client HTTP capable d'envoyer du JSON- Aucun SDK nécessaire — chaque exemple ci-dessous est un appel HTTP pur
Chaque requête nécessite un en-tête :
Authorization: Bearer <your-api-key>
Lorsque la clé est invalide ou absente, une erreur de type 401 est renvoyée avant d'atteindre le moindre modèle :
{
"error": {
"code": "permission_denied",
"message": "invalid api key",
"request_id": "req_...",
"type": "hiapi_error"
}
}
Il n'existe pas de mode sans clé ni anonyme — chaque appel à /v1/tasks doit porter un bearer token valide. Pour la rotation et la portée des clés, consultez Authentification.
Exemple minimal fonctionnel
1. Créer une tâche
La structure de l'appel de création est toujours la même : un identifiant model et un objet input propre au modèle. Voici la création d'une tâche image avec z-image :
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"
}
}'
La réponse revient immédiatement — la génération est asynchrone :
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "queued"
}
}
Maintenant, suivez le même schéma avec qwen-image-2.0. Même endpoint, même structure externe — seul le input diffère (ce modèle reçoit une chaîne size, et non un 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"
}
}'
Créez ensuite une tâche vidéo avec seedance-2.5/text-to-video — toujours la même structure externe, avec désormais un duration et une énumération aspect_ratio aux valeurs plus larges :
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"
}
}'
C'est là le point central : la structure externe de la tâche ne change jamais, seul le input varie. Chaque modèle sur hiapi documente son propre schéma input sur sa page de modèle — consultez toujours le schéma du modèle spécifique avant de construire une nouvelle intégration, car des champs comme aspect_ratio, size et duration ne sont pas interchangeables d'un modèle à l'autre.
2. Interroger le résultat
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-xxxxxxxxxxxx \
-H "Authorization: Bearer $HIAPI_API_KEY"
status passe successivement par queued → handling → archiving, puis atteint l'état terminal success ou fail. En cas de succès, data.output est un tableau — la plupart des modèles image/vidéo renvoient une entrée, mais une même tâche peut renvoyer plusieurs types de sortie (par exemple un modèle vidéo qui renvoie à la fois video et thumbnail) :
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "success",
"output": [
{ "type": "image", "url": "https://...", "expireAt": "2026-10-09T00:00:00Z" }
]
}
}
expireAt est important — les URL de sortie sont temporaires par défaut. Téléchargez le contenu binaire dès que la tâche est terminée (ou réhébergez-le sur votre propre stockage). Si vous avez besoin d'une conservation plus longue par la plateforme, définissez storage: "persistent" à la création ou effectuez une mise à niveau après coup — consultez Stockage.
En cas d'échec, vous recevez un objet error au lieu de output :
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "fail",
"error": { "code": "...", "message": "..." }
}
}
La boucle de polling Python minimale ci-dessous fonctionne de la même manière pour tous les modèles ci-dessus :
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
Schémas de production
Utiliser des callbacks au lieu du polling en production
Le polling convient aux scripts ou aux outils CLI, mais à toute échelle de trafic réelle, les callbacks sont préférables : passez un objet callback à la création de la tâche et hiapi enverra le résultat terminal en POST à votre endpoint, au lieu de vous faire répéter GET /v1/tasks/:id en boucle.
{
"model": "z-image",
"input": { "prompt": "...", "aspect_ratio": "1:1" },
"callback": { "url": "https://yourapp.com/webhooks/hiapi", "when": "final" }
}
when: "final" signifie que vous ne recevrez qu'un seul POST, déclenché lors de l'état terminal (success ou fail) — sans le bruit des états intermédiaires handling/archiving. Si vous avez configuré un callback mais que vous ne recevez rien, vérifiez que votre endpoint est accessible publiquement et renvoie rapidement un 2xx ; pour la cause la plus fréquente de perte silencieuse de callback, consultez cet article connexe.
Réessais idempotents
Les perturbations réseau sont inévitables. Si vous réessayez un appel POST /v1/tasks après un timeout sans protection, vous risquez de créer (et de payer) des tâches en double. Envoyez un en-tête Idempotency-Key (n'importe quelle chaîne de moins de 255 octets, généralement un UUID généré par requête logique) : les réessais avec la même clé renvoient la tâche d'origine au lieu d'en créer une nouvelle :
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"}}'
Gérer les erreurs de validation par modèle
Comme chaque modèle possède son propre schéma input, le mode d'échec le plus courant est un 400 sur un champ — un champ valide pour un modèle mais invalide pour un autre, par exemple en envoyant aspect_ratio à un modèle qui attend size :
{
"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'"
}
Si vous construisez un routeur multi-modèles (où l'utilisateur ou votre propre logique choisit le modèle à l'exécution), maintenez un petit registre de constructeurs d'entrée par modèle plutôt qu'une structure de payload partagée :
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)}
Ainsi, les particularités de chaque modèle sont centralisées en un seul endroit plutôt que dispersées à chaque point d'appel. Un taskId inexistant ou appartenant à un autre compte renvoie un 404 ordinaire :
{ "code": 404, "message": "task not found", "data": null }
Quant aux 503 temporaires, ils doivent être réessayés avec un backoff exponentiel plutôt qu'exposés directement à l'utilisateur — les limites de débit et de capacité sont documentées dans Limites de débit.
Lectures complémentaires
- Référence de l'API de tâches asynchrones — le contrat complet
create/detail - Authentification — portée et rotation des clés API
- Stockage — conserver les sorties au-delà de l'expiration par défaut
- Pourquoi votre callback de tâche pourrait ne pas se déclencher
- Parcourir tous les modèles — le schéma
inputpropre à chaque modèle figure sur sa page - Tarification — les coûts sont calculés par modèle et par sortie, et non selon un tarif unique par requête
Questions fréquentes
Chaque modèle nécessite-t-il un SDK ou une clé API différente ?
Non. Une seule clé API, un seul modèle d'endpoint (/v1/tasks), pour chaque modèle de la plateforme. Seul l'objet input dans le corps de la requête change.
Pourquoi la réponse ne contient-elle pas encore de output juste après la création de la tâche ?
La génération est asynchrone. L'appel de création ne renvoie que taskId et status: "queued" — vous devez interroger GET /v1/tasks/:id (ou utiliser un callback) jusqu'à ce que status atteigne success ou fail.
Peut-on appeler l'API sans authentification pour tester rapidement ?
Non — chaque appel à /v1/tasks nécessite une clé Authorization: Bearer valide. Il n'existe pas de mode sans clé ni de démonstration pour les appels de production ; générez d'abord une clé depuis la console.
Que se passe-t-il si l'on interroge après l'expiration de l'URL de sortie ?
L'enregistrement de la tâche lui-même est conservé, mais le output[].url des tâches non persistantes ne sera plus résolvable après expireAt. Téléchargez le fichier immédiatement en cas de succès, ou définissez storage: "persistent" à la création si une conservation à long terme est nécessaire.
Comment savoir quels champs un modèle accepte ?
Consultez la page de ce modèle sous Modèles — chacune documente son schéma input exact. Ne supposez pas que aspect_ratio, size ou duration sont interchangeables entre modèles ; comme montré ci-dessus, z-image et qwen-image-2.0 divergent déjà sur la façon d'exprimer la forme de l'image.
Articles similaires
Voir tous les articles →-
Tutoriel
Comment appeler hailuo-2.3-fast/image-to-video via l'API HiAPI : curl et Python en pratique
La première requête exploitable du palier image-vers-vidéo rapide et économique — de l'ID du modèle au téléchargement du MP4
-
Tutoriel
Comment appeler hailuo-2.3/text-to-video via l'API HiAPI : curl, Python et exemples de requêtes exécutables
-
Tutoriel
Utiliser Kandinsky 6.0 Pro texte-vers-vidéo sur HiAPI : tutoriel curl et Python
Démonstration pratique axée sur la requête : construire l'appel du prompt au clip vidéo, soumettre la requête, interroger l'état d'achèvement et télécharger le résultat