HiAPI
Blog

Tutoriel

Créer des applications d'IA multi-modèles sur hiapi avec une API unique et unifiée

Oct 8, 2026
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
  • curl ou 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

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 →

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