HiAPI
Blog

Tutoriel

Comment appeler l'API Imagen 4 via HiAPI : exemples curl et Python

Oct 31, 2026
博客封面图:淡灰紫色块与墨黑色块以一条笔直硬边相接,交界处一道细紫缝,构图简洁平静。

Ce que l'endpoint Imagen 4 vous apporte

Si vous avez déjà un prompt et que vous voulez obtenir une image sans créer de compte de facturation, sans mettre en place de file d'attente et sans créer de bucket chez un fournisseur que vous n'avez jamais utilisé, le modèle Google-Imagen4 sur HiAPI est un point de départ raisonnable. Vous envoyez un prompt textuel à un seul endpoint HTTP, vous vous authentifiez avec une clé API, et vous récupérez une référence vers l'image générée (ou une tâche interrogeable). Tout le reste — hébergement du modèle, capacité, nouvelles tentatives côté fournisseur — est abstrait.

Les appels texte-vers-image via cet endpoint couvrent les trois choses qui vous importeront réellement en usage :

  • Du prompt à l'image. Le prompt est l'entrée principale. Selon la façon dont vous configurez la requête, le modèle renvoie une ou plusieurs images.
  • Contrôle de la forme de sortie. Vous pouvez spécifier le ratio d'aspect (carré, paysage, portrait) au moment de la requête, plutôt que de recadrer après coup. C'est plus important qu'il n'y paraît : recadrer une image générée fait perdre la composition que le modèle avait déjà planifiée.
  • Exécution asynchrone. Les générations longues sont traitées sous forme de tâches. Vous soumettez, interrogez l'état, puis récupérez le résultat. Les appels courts peuvent revenir rapidement, mais vous devriez écrire votre client comme si chaque appel était asynchrone, car c'est le flux qui tient la charge en production.

Le reste de ce tutoriel construit la même requête de trois façons : un appel curl brut à coller directement dans le terminal, un client Python équivalent, puis les différentes parties dont vous aurez besoin pour gérer le ratio d'aspect, l'interrogation, la persistance et les erreurs. Si vous voulez générer une image avec une commande shell d'une ligne, c'est adapté. Si vous avez besoin d'un contrôle fin sur des masques d'inpainting ou une boucle d'entraînement, ce n'est peut-être pas le bon outil.

Deux prérequis avant de commencer : vous disposez d'une clé API HiAPI, et votre environnement d'exécution peut émettre des requêtes HTTPS sortantes. Rien d'autre n'est requis — l'installation d'un SDK n'est pas obligatoire, même si l'exemple Python ci-dessous utilise requests.

Lancer votre premier appel avec curl

Le moyen le plus rapide de vérifier que la clé fonctionne est un appel curl. L'endpoint reçoit un corps JSON, donc il vous faut deux en-têtes : Content-Type pour le corps, Authorization pour la clé. Lisez la clé depuis une variable d'environnement plutôt que de la coller directement dans la commande, afin qu'elle ne reste pas dans l'historique de votre shell.

export HIAPI_API_KEY="your-key-here"

curl -sS -X POST "https://api.hiapi.ai/v1/images/generations" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -d '{
    "model": "google-imagen-4",
    "prompt": "a weathered lighthouse on a basalt cliff, morning fog, wide shot",
    "n": 1,
    "aspect_ratio": "16:9"
  }'

Quelques points méritent attention dans ce corps de requête :

  • model sélectionne Google-Imagen4. Écrivez-le explicitement même si le service a une valeur par défaut — cela garde votre code lisible un an plus tard.
  • prompt est la seule entrée créative obligatoire. Rédigez-le comme vous décririez une prise de vue à un photographe : sujet, environnement, lumière, cadrage.
  • n contrôle le nombre d'images souhaitées. Commencez par une seule pendant que vous ajustez la structure de réponse ; augmentez une fois que vous faites confiance à votre pipeline.
  • aspect_ratio est détaillé plus loin. L'omettre revient à la valeur par défaut du service.

Une réponse réussie renvoie un objet JSON. La partie qui vous intéresse est la liste d'entrées d'images ; chaque entrée porte une référence téléchargeable ou une URL que vous pouvez transmettre à votre frontend. Si vous avez demandé plusieurs images, vous recevez plusieurs entrées, dans l'ordre de la requête. Si la génération a été mise en file d'attente parce qu'elle est assez longue, la réponse vous donne un identifiant et un état interrogeable — ce flux est décrit dans la section dédiée ci-dessous.

Si vous recevez un 4xx, vérifiez d'abord les deux en-têtes : un préfixe Bearer manquant, ou un caractère blanc en fin de clé, sont les causes de la plupart des échecs au premier appel. Pour déboguer, utilisez -i pour voir la ligne de statut et le corps d'erreur.

Passer la même requête en Python

Dans un script, encapsulez l'appel dans une petite fonction, afin que le reste du code n'ait jamais à toucher au HTTP brut. La fonction doit recevoir le prompt et les paramètres optionnels, construire le corps de requête, l'envoyer et renvoyer le JSON analysé.

import os
import requests

HIAPI_BASE = "https://api.hiapi.ai/v1"


def generate_image(prompt: str, aspect_ratio: str = "1:1", n: int = 1) -> dict:
    response = requests.post(
        f"{HIAPI_BASE}/images/generations",
        headers={
            "Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}",
            "Content-Type": "application/json",
        },
        json={
            "model": "google-imagen-4",
            "prompt": prompt,
            "n": n,
            "aspect_ratio": aspect_ratio,
        },
        timeout=120,
    )
    response.raise_for_status()
    return response.json()


if __name__ == "__main__":
    result = generate_image(
        "a weathered lighthouse on a basalt cliff, morning fog, wide shot",
        aspect_ratio="16:9",
    )
    print(result)

Lorsque vous adaptez ce code, trois choix de conception méritent d'être conservés :

Un timeout explicite.requests n'a pas de timeout par défaut, ce qui signifie qu'une connexion bloquée peut suspendre un worker indéfiniment. Choisissez une valeur supérieure à votre temps de génération le plus long attendu, et faites-la échouer explicitement.

Appelez raise_for_status() avant d'analyser.Les réponses d'erreur ne partagent pas toujours le même schéma que les réponses de succès. Vérifiez le statut HTTP avant de toucher au corps de la réponse.

Une fonction petite et paramétrée.Lorsque vous ajouterez l'interrogation et le téléchargement, cette fonction restera le seul point d'entrée pour la soumission, et la logique de cycle de vie restera à côté d'elle au lieu de s'y emmêler.

Le résultat analysé contient les mêmes informations que la sortie curl. Pour une réponse synchrone, parcourez les entrées d'images et extrayez les URL. Pour une réponse asynchrone, extrayez l'identifiant de tâche et passez-le à la fonction d'interrogation. Votre code doit brancher selon la forme reçue, plutôt que de supposer qu'il n'y en a qu'une.

Contrôler le ratio d'aspect et la sortie

Le ratio d'aspect est le paramètre que vous ajustez le plus souvent, et il vaut la peine de le régler correctement plutôt que de recadrer après coup. Si vous demandez une image carrée puis en retouchez une bande 16:9, vous perdez le haut et le bas de la composition voulue par le modèle. Demander 16:9 dès le départ permet au modèle de planifier la prise de vue pour ce cadre.

Transmettez-le dans le corps de la requête avec le prompt :

result = generate_image(
    "a sprawling night market, neon signs, steam from food stalls",
    aspect_ratio="16:9",
    n=2,
)

Les ratios paysage servent aux bannières hero, aux vignettes de cartes larges et à la photographie produit ; les ratios portrait servent aux cartes mobile-first, aux affiches et aux portraits de personnages ; le carré sert aux avatars et aux posts sociaux dont le recadrage est imprévisible. Décidez en fonction de l'endroit où l'image finira, et non de son apparence isolée.

Ce qui change dans la réponse lorsque vous modifiez le ratio : les dimensions en pixels des images renvoyées. Le nombre d'entrées, leur ordre et le flux de téléchargement restent identiques, donc votre code en aval n'a pas besoin de brancher selon le ratio. Si votre interface réserve de l'espace avant le chargement des images, lisez les dimensions dans la réponse et appliquez-les à l'élément, plutôt que de coder les dimensions en dur.

Deux autres réglages de sortie méritent d'être connus :

  • Nombre d'images (n).Plus d'images par requête signifie plus de choix, mais aussi plus de tokens dépensés sur un prompt potentiellement erroné. Pendant l'itération sur la formulation du prompt, ne demandez qu'une image ; une fois le prompt stabilisé, demandez-en par lot.
  • Formulation du prompt pour le cadrage.Le ratio détermine le cadre ; le prompt détermine la composition à l'intérieur du cadre. Des termes comme « wide shot », « close-up » et « centered » agissent réellement avec le paramètre de ratio.

La facturation de cet endpoint suit la génération d'images elle-même — consultez le panneau tarifaire du site pour les tarifs en vigueur, et ne codez rien en dur dans votre modèle de coûts.

Gérer les tâches asynchrones et l'interrogation

Lorsqu'une génération est assez longue pour être mise en file d'attente, l'endpoint répond avant que l'image n'existe. Vous récupérez un handle de tâche, et votre travail consiste à le vérifier jusqu'à ce qu'il atteigne un état terminal. Écrivez ce flux même si les appels d'aujourd'hui reviennent vite — la capacité, la complexité du prompt et le nombre d'images affectent la latence, et un client conçu pour le synchrone s'effondrera à la première requête lente.

Le cycle de vie se déroule en trois étapes :

  1. Soumettre.Envoyez la requête de génération comme d'habitude. Si la réponse est une tâche, extrayez son identifiant.
  2. Interroger.Appelez l'endpoint d'état de tâche avec cet identifiant à intervalle fixe.
  3. Récupérer.Une fois l'état signalé comme réussi, lisez la référence d'image dans la charge utile du résultat.
import time


def wait_for_job(job_id: str, interval: float = 2.0, max_attempts: int = 60) -> dict:
    for attempt in range(max_attempts):
        response = requests.get(
            f"{HIAPI_BASE}/images/jobs/{job_id}",
            headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
            timeout=30,
        )
        response.raise_for_status()
        payload = response.json()

        status = payload.get("status")
        if status == "succeeded":
            return payload
        if status in {"failed", "cancelled"}:
            raise RuntimeError(f"job {job_id} ended with status {status}")

        time.sleep(interval)

    raise TimeoutError(f"job {job_id} did not finish in time")

Les détails clés qui rendent ce code sûr en production :

  • Limiter les tentatives, pas de boucle infinie.Une tâche qui ne se terminera jamais doit se manifester par une erreur de timeout alertable, plutôt qu'un worker suspendu indéfiniment. max_attempts multiplié par interval est votre budget de temps réel — fixez-le d'après votre temps de génération réel le plus long.
  • Backoff sous charge.Un intervalle fixe de deux secondes convient à l'usage interactif. Pour des charges par lots, utilisez un backoff progressif ou ajoutez du jitter, afin que les nouvelles tentatives de nombreux workers ne se synchronisent pas sur la même seconde.
  • Distinguer l'échec de l'inconnu.Seuls failed et cancelled sont des échecs terminaux. Traitez tout autre état non réussi comme « toujours en cours » et interrogez à nouveau ; ajouter une branche qui lève immédiatement sur les états non reconnus posera problème quand le service ajoutera de nouveaux états.
  • Réessayer la soumission en cas d'erreur réseau, pas l'interrogation.Si l'appel de soumission lui-même expire, une nouvelle tentative peut créer des tâches en double. Si l'API prend en charge les clés d'idempotence, donnez-en une à chaque soumission ; sinon, dédupliquez de votre côté avec un hachage du prompt plus un horodatage.

Une fois qu'une tâche signale un succès, arrêtez d'interroger. Persister le résultat et conserver le handle de tâche pour l'audit coûte moins cher que de continuer à demander un état que vous connaissez déjà.

Télécharger et stocker les images

La réponse de génération vous donne des références, pas des octets d'image. Dans la plupart des cas, chaque entrée d'image porte une URL récupérable par une simple requête GET. Traitez cette URL comme une adresse temporaire : téléchargez-la rapidement, validez-la, et stockez les octets là où vous avez réellement le contrôle.

import pathlib
import uuid


def download_images(payload: dict, out_dir: str = "images") -> list[pathlib.Path]:
    target = pathlib.Path(out_dir)
    target.mkdir(parents=True, exist_ok=True)

    saved = []
    for entry in payload.get("data", []):
        url = entry["url"]
        resp = requests.get(url, timeout=60)
        resp.raise_for_status()

        content_type = resp.headers.get("Content-Type", "")
        if not content_type.startswith("image/"):
            raise ValueError(f"unexpected content type: {content_type}")

        path = target / f"{uuid.uuid4().hex}.png"
        path.write_bytes(resp.content)
        saved.append(path)

    return saved

Avant de considérer une tâche comme terminée, faites quelques vérifications :

  • Type de contenu et taille.Dans certains environnements, une redirection vers une page d'erreur renvoie un 200 et du HTML. Avant d'écrire dans le stockage permanent, vérifiez que Content-Type commence par image/ et que la taille du corps de réponse n'est pas négligeable.
  • Nommage.Générez un nouveau nom à chaque téléchargement, plutôt que de le dériver du prompt. Les prompts se répètent, entrent en conflit et contiennent des caractères refusés par le système de fichiers.
  • Stocker le prompt avec le fichier.Enregistrez le prompt, les paramètres et l'identifiant de tâche dans votre propre base de données ou un fichier de métadonnées annexe. Six mois plus tard, l'image seule ne vous dira pas quelle requête l'a produite.
  • Déplacer vers le stockage permanent, pas copier.Écrivez d'abord dans un emplacement temporaire, validez, puis déplacez vers votre bucket ou votre stockage adossé au CDN. Un fichier à moitié écrit dans le stockage permanent est pire qu'une tâche échouée.

Si votre pipeline produit de nombreuses images, séparez l'étape de téléchargement de l'étape de génération. La génération est la partie coûteuse et limitée en débit ; les nouvelles tentatives de téléchargement sont peu coûteuses et ne devraient jamais vous obliger à régénérer.

Erreurs courantes et liste de vérification de débogage

La plupart des échecs du premier jour se rangent dans quelques catégories. Vérifiez-les dans l'ordre suivant — chaque vérification est plus rapide que la suivante.

Échecs d'authentification (401, 403)

Clé erronée, manquante ou mal formée. Vérifiez que la valeur de l'en-tête est exactement Bearer suivi de la clé, sans saut de ligne issu d'un copier-coller, et confirmez que la variable d'environnement est bien exportée dans le shell ou le processus qui exécute le code. Une clé renouvelée dans le dashboard invalide immédiatement l'ancienne — si un autre service vient de se mettre à échouer, demandez si quelqu'un a renouvelé la clé.

Paramètres rejetés (400, 422)

Le corps de requête ne correspond pas au schéma. Causes fréquentes : identifiant model mal orthographié, format de chaîne aspect_ratio non accepté par l'endpoint, ou n hors de la plage prise en charge. Imprimez le corps de la réponse d'erreur — il indique généralement le champ fautif. Comparez votre JSON à l'exemple curl fonctionnel, champ par champ.

Timeouts

Deux types de timeouts différents sont faciles à confondre. Un timeout côté client signifie que votre appel HTTP a abandonné ; la tâche peut toujours être en cours. Une tâche qui n'atteint jamais un état terminal signifie que le budget d'interrogation est trop petit, ou que la requête a réellement échoué. Réglez le timeout client au-dessus de votre temps de génération le plus long attendu, puis réglez le budget d'interrogation encore plus haut. Lorsqu'un timeout client se déclenche pendant la soumission, une nouvelle tentative aveugle peut dupliquer le travail — vérifiez s'il existe déjà une tâche avant de resoumettre.

Téléchargements qui semblent réussir mais qui échouent

Un statut 200 avec un corps HTML, ou un fichier de zéro octet, signifie généralement que l'URL de l'image a expiré, ou que la récupération s'est faite sans les droits d'accès appropriés. Téléchargez immédiatement après le succès de la tâche, et validez le type de contenu et la taille comme indiqué ci-dessus.

Limites de débit (429)

Reculez et réessayez, plutôt que de resserrer la boucle. Un backoff exponentiel avec jitter est la réponse standard ; limitez le nombre de tentatives et exposez les échecs persistants à votre supervision.

Une habitude de débogage à adopter

Journalisez quatre choses pour chaque requête : l'identifiant du modèle, le corps complet de la requête sans la clé, l'identifiant de tâche (ou le statut de réponse pour un appel synchrone) et la durée. Lorsqu'un problème survient en production, ces quatre champs permettent généralement de déterminer en moins d'une minute si le problème vient de votre client, de vos paramètres ou de la file d'attente.

Commencez par l'appel curl, récupérez une première image, puis déplacez la logique dans un client Python et ajoutez l'interrogation et la persistance autour. Ce parcours garde chaque échec circonscrit et attribuable.

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