HiAPI
Blog

Tutoriel

Comment utiliser nano-banana via l'API HiAPI : curl, Python et requêtes exécutables

Le parcours complet, de la première requête au téléchargement du résultat

Oct 31, 2026
博客封面图:错位叠放的骨白色纸张,炭灰边缘,夹一张锈红色纸片,扁平投影。

À quoi ressemble une requête nano-banana

Avant d'écrire du code, il est utile de comprendre le cycle de vie d'un appel nano-banana sur HiAPI. Chaque requête suit le même schéma : vous envoyez un corps JSON décrivant ce que vous voulez, HiAPI accepte la tâche, puis vous récupérez soit directement le résultat finalisé, soit un identifiant de tâche avec lequel vous interrogez l'état d'avancement jusqu'à la fin.

Quelques caractéristiques définissent sa forme :

  • Un point d'accès, une clé API. Tout le trafic nano-banana passe par l'interface unifiée HiAPI. Vous vous authentifiez avec votre clé HiAPI et ne touchez jamais directement au fournisseur en amont.
  • Du JSON en entrée, du JSON en sortie. Le corps de la requête est du JSON. La réponse est également du JSON, contenant soit un résultat intégré, soit un id de tâche accompagné d'un champ de statut.
  • Texte obligatoire, image facultative. Au minimum, vous devez envoyer un prompt. Vous pouvez ajouter des images de référence pour orienter le sujet, le style ou le personnage.
  • Adapté à l'asynchrone. La génération d'images peut dépasser le délai d'expiration HTTP habituel ; l'API prend donc en charge les tâches asynchrones, que vous pouvez interroger jusqu'à leur achèvement.

Structure approximative du corps de la requête :

{
  "model": "nano-banana",
  "prompt": "a description of the image you want",
  "image_input": ["https://.../reference.jpg"],
  "consistency": "character",
  "async": true
}

Réponse en mode asynchrone :

{
  "id": "job_abc123",
  "status": "queued",
  "created_at": "..."
}

Lorsque la tâche est terminée et que vous la récupérez, status devient succeeded (ou failed), et la charge utile du résultat contient l'URL de sortie. Gardez ce modèle en deux phases à l'esprit, car tout le reste de ce tutoriel s'appuie dessus.

Une requête curl exécutable

Voici la requête minimale capable de générer une image. Elle suppose que HIAPI_API_KEY est déjà exportée dans votre shell :

curl -X POST https://api.hiapi.ai/v1/images/generations \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nano-banana",
    "prompt": "a cozy reading nook by a rain-streaked window, warm lamp light, watercolor style",
    "async": true
  }'

Explication champ par champ :

  • model — défini sur nano-banana. HiAPI s'en sert pour acheminer la requête.
  • prompt — le seul champ de création strictement obligatoire. Décrivez précisément le sujet, la composition et le style ; les prompts vagues sont la cause la plus fréquente de résultats décevants.
  • async — avec true, l'API renvoie immédiatement un identifiant de tâche. Si vous préférez bloquer jusqu'à l'achèvement, mettez false, mais attendez-vous à des temps de réponse plus longs.
  • image_input — tableau facultatif d'URL d'images de référence. Omettez-le pour une génération purement textuelle.
  • consistency — contrôle facultatif de la cohérence du personnage ou du sujet entre les images ; voir ci-dessous pour les détails.

Si vous ne savez pas quels paramètres facultatifs la version actuelle prend en charge, consultez d'abord la page de documentation du modèle sur HiAPI avant d'ajouter des champs — les champs inconnus peuvent être ignorés ou rejetés selon le paramètre.

Exécutez la commande et examinez le JSON. En supposant que async vaut true, vous récupérez un objet tâche plutôt qu'une image. Conservez l'id ; c'est l'identifiant dont vous aurez besoin à l'étape suivante.

Porter la requête en Python

L'appel curl se traduit directement. L'objectif ici n'est pas seulement d'écrire un script jetable, mais un wrapper réutilisable que vous pouvez intégrer à un pipeline.

import os
import time
import requests

BASE = "https://api.hiapi.ai"
HEADERS = {
    "Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}",
    "Content-Type": "application/json",
}

def create_image(prompt, image_input=None, consistency=None, async_job=True):
    body = {"model": "nano-banana", "prompt": prompt, "async": async_job}
    if image_input:
        body["image_input"] = image_input
    if consistency:
        body["consistency"] = consistency

    resp = requests.post(
        f"{BASE}/v1/images/generations",
        headers=HEADERS,
        json=body,
        timeout=60,
    )
    if resp.status_code >= 400:
        raise RuntimeError(f"create failed: {resp.status_code} {resp.text}")
    return resp.json()

Voici ce qui le rend robuste et pas seulement fonctionnel :

  • Un timeout explicite. Passez-en toujours un ; la valeur par défaut « attendre indéfiniment » transforme un problème réseau passager en processus bloqué.
  • Vérifier le statut avant de parser. Levez une exception sur les réponses d'erreur, afin qu'une mauvaise requête ne renvoie pas silencieusement un corps JSON que vous indexeriez ensuite.
  • Omettre les champs facultatifs plutôt que de les mettre à vide. Plutôt que d'envoyer "image_input": null, ajoutez la clé uniquement lorsqu'elle a une valeur. Cela rend les logs plus lisibles et évite l'ambiguïté entre null signifiant « aucun » et « non défini ».

L'encapsulation dans une fonction facilite aussi les nouvelles tentatives : comme la fonction lève une exception en cas d'échec, l'appelant peut l'envelopper dans une boucle de retry sans toucher au code de la requête. Cette séparation paie dès que vous passez d'une image unique au traitement par lots.

Envoyer des entrées texte et image

La génération texte-vers-image est le cas simple. La partie intéressante de nano-banana consiste à combiner un prompt et des images de référence dans une seule requête. Vous passez image_input comme tableau d'URL et le prompt décrit comment les traiter.

result = create_image(
    prompt="the person in the first image standing in a Tokyo alley at night, neon reflections, cinematic",
    image_input=["https://example.com/portrait.jpg"],
)

Conseils pratiques pour le mode multimodal :

  • Précisez les rôles. Si vous envoyez plusieurs images, indiquez dans le prompt laquelle fournit le sujet, laquelle fournit le style et laquelle fournit l'arrière-plan. « Combinez ces images » est vague ; « utilisez le visage de l'image un et l'éclairage de l'image deux » ne l'est pas.
  • Utilisez des URL accessibles et stables. L'API doit pouvoir récupérer les images. Une URL signée qui expire après quelques minutes convient à un appel en temps réel, mais échouera si vous réessayez une tâche mise en file plus tard.
  • Privilégiez des références propres. Un portrait net et bien éclairé produit une ressemblance plus fidèle qu'une photo de groupe chargée. Le principe « garbage in, garbage out » est encore plus visible sur le conditionnement par image que sur le texte.
  • Itérez d'abord sur le prompt, pas sur l'image de référence. Si la ressemblance ne convient pas, ajustez d'abord la façon dont vous décrivez le sujet, puis changez l'image source — c'est généralement le changement le plus simple.

Lorsque l'image de référence contient un personnage que vous comptez réutiliser dans plusieurs générations, vous entrez dans le domaine de la cohérence de personnage, c'est-à-dire la section suivante.

Options de cohérence de personnage

La cohérence de personnage répond à un besoin précis : générer plusieurs images qui semblent mettre en scène le même personnage ou sujet. nano-banana l'expose comme un contrôle facultatif, et non comme un modèle distinct.

En résumé, les compromis sont les suivants :

  • Aucun contrôle de cohérence. Idéal pour les images uniques. Rapide et simple, mais le personnage peut dériver si vous régénérez. À utiliser pour les arrière-plans, les objets et les illustrations autonomes.
  • Cohérence basée sur la référence. Vous fournissez une ou plusieurs images du personnage et activez l'option de cohérence pour que le modèle s'y ancre. C'est le bon choix pour les storyboards, les bandes dessinées et les séquences produit, où l'identité doit rester stable d'une image à l'autre. Le prix à payer : vous devez d'abord disposer d'une bonne image de référence.
  • Cohérence par description textuelle. Vous décrivez le personnage en détail dans chaque prompt — couleur de cheveux, vêtements, carrure, traits distinctifs — sans utiliser de référence. Utile lorsque vous n'avez pas de photo source, mais cela exige de la discipline : placez la description dans une constante réutilisable pour qu'elle ne dérive pas entre les appels.

Un workflow pragmatique pour une série :

  1. Générez la première image sans cohérence, en itérant sur le prompt jusqu'à ce que le personnage soit correct.
  2. Utilisez cette sortie comme image de référence.
  3. Activez le contrôle de cohérence et générez les images suivantes en gardant la référence fixe.
  4. Si le personnage dérive encore, raccourcissez le prompt pour laisser la référence faire davantage de travail — un long prompt qui contredit la référence embrouille le modèle.

La cohérence est un réglage, pas une garantie. Même dans un pipeline bien ajusté, prévoyez de vérifier les résultats et de régénérer une petite partie des images.

Interroger les tâches asynchrones

Lorsque vous soumettez avec async: true, vous récupérez un id de tâche. L'interrogation est simple, mais les détails comptent plus qu'il n'y paraît.

Un poller minimal et correct :

def wait_for_job(job_id, timeout=300, initial_delay=2, max_delay=15):
    deadline = time.time() + timeout
    delay = initial_delay
    while time.time() < deadline:
        resp = requests.get(
            f"{BASE}/v1/images/generations/{job_id}",
            headers=HEADERS,
            timeout=30,
        )
        resp.raise_for_status()
        data = resp.json()

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

        time.sleep(delay)
        delay = min(delay * 1.5, max_delay)

    raise TimeoutError(f"job {job_id} did not finish within {timeout}s")

Justification de chaque choix :

  • Backoff quasi exponentiel plafonné. Commencez à quelques secondes, multipliez par 1,5 jusqu'au plafond. Interroger toutes les secondes une tâche de trente secondes gaspille des dizaines de requêtes ; interroger toutes les trente secondes ajoute une latence inutile aux tâches rapides.
  • Échéance stricte. Sans elle, une tâche bloquée immobilise indéfiniment votre worker. Mieux vaut échouer bruyamment.
  • Traitement des états terminaux. Traitez failed et cancelled comme des exceptions, pas comme des états sur lesquels continuer à interroger. Une tâche en état d'échec ne réussira jamais au prochain sondage.
  • Un nouveau GET à chaque itération. Ne mettez pas la première réponse en cache en supposant qu'elle se mettra à jour.

Pour le traitement par lots, soumettez d'abord toutes les tâches, collectez les ids, puis interrogez-les en boucle — cela permet aux temps de génération de se chevaucher d'une tâche à l'autre au lieu de se sérialiser.

Télécharger et vérifier la sortie

Une fois la tâche réussie, la charge utile du résultat contient une URL de sortie. Le téléchargement est un simple GET, mais avant de vérifier vous ne devriez pas supposer que les octets sont bien une image.

def download_image(url, path):
    with requests.get(url, stream=True, timeout=60) as r:
        r.raise_for_status()
        content_type = r.headers.get("Content-Type", "")
        if not content_type.startswith("image/"):
            raise RuntimeError(f"unexpected content type: {content_type}")
        with open(path, "wb") as f:
            for chunk in r.iter_content(chunk_size=8192):
                f.write(chunk)
    return path

Vérifications de bon sens qui valent la peine pour chaque téléchargement :

  • Content-Type. Si vous demandez une image et obtenez application/json ou text/html, vous avez en réalité reçu une page d'erreur. Vérifier l'en-tête permet de détecter cela avant d'écrire un fichier corrompu.
  • Streaming. Écrivez sur disque en flux plutôt que de charger tout le corps de la réponse en mémoire ; cela stabilise la mémoire lorsque vous téléchargez en parallèle.
  • Taille non nulle. Un fichier de zéro octet indique un problème, même si le statut HTTP est normal.
  • Journalisez une somme de contrôle ou le nombre d'octets. Consignez-les avec l'id de tâche pour pouvoir relier un fichier défectueux à une génération précise.

Les URL de sortie peuvent être temporaires. Si vous comptez conserver les résultats, téléchargez-les vers votre propre stockage au cours de la même exécution, plutôt que d'enregistrer le lien pour plus tard.

Points de défaillance courants

La plupart des problèmes se rangent dans quelques catégories. Vérifiez celles-ci avant de supposer que le modèle se comporte anormalement.

Authentification et routage

  • Un en-tête Authorization manquant ou mal formé renvoie une erreur d'authentification, pas une image. Vérifiez que l'en-tête se lit Bearer <key>, avec un espace au milieu.
  • Envoyer une mauvaise valeur de model route vers le mauvais endroit ou échoue directement. Elle doit être exactement nano-banana.

Problèmes de corps de requête

  • Prompts vagues. La cause unique la plus fréquente de « mauvaise sortie ». Avant de changer quoi que ce soit d'autre, ajoutez des détails de composition, d'éclairage et de style.
  • URL d'image inaccessibles. Une URL de référence qui renvoie 404, exige une authentification ou pointe vers un chemin local échouera ou sera silencieusement ignorée. Récupérez d'abord l'URL vous-même pour confirmer qu'elle fonctionne.
  • Envoyer async: false avec un long prompt et atteindre le timeout client. Passez en asynchrone et interrogez, plutôt que d'augmenter le timeout.

Traitement des tâches

  • Interroger une tâche qui n'a jamais existé. Une faute de frappe dans l'id de tâche produit une erreur de type non trouvé qui ressemble à un échec ; journalisez l'id que vous avez soumis.
  • Traiter failed comme si la tâche tournait encore. Quittez toujours la boucle d'interrogation sur un état terminal.
  • Aucune échéance. Le bug le plus coûteux dans le traitement par lots : une image bloquée paralyse toute l'exécution.

Traitement de la sortie

  • Ne pas vérifier le Content-Type. Comme indiqué plus haut, une page d'erreur enregistrée avec l'extension .png ressemblera plus tard à une image corrompue.
  • Dépendre d'une URL de résultat expirée plusieurs heures après la génération. Téléchargez sans tarder.

En diagnostiquant dans l'ordre : authentification, corps de requête, cycle de vie de la tâche, téléchargement, la plupart des pannes se résolvent en quelques minutes.

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