HiAPI
Blog

Tutoriale

Come usare nano-banana tramite l'API HiAPI: curl, Python e richieste eseguibili

L'intero flusso pratico, dalla prima richiesta al download dell'output

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

Come si presenta una richiesta a nano-banana

Prima di scrivere codice, è utile capire il ciclo di vita di una chiamata a nano-banana su HiAPI. Ogni richiesta segue lo stesso schema: invii un body JSON che descrive ciò che desideri, HiAPI accetta il task e poi ricevi direttamente il risultato già completato oppure un handle del task con cui interrogare lo stato di completamento.

Alcune caratteristiche ne definiscono la forma:

  • Un endpoint, una chiave API. Tutto il traffico di nano-banana passa attraverso l'interfaccia unificata di HiAPI. Ti autentichi con la chiave HiAPI e non entri mai in contatto diretto con l'upstream.
  • JSON in ingresso, JSON in uscita. Il body è JSON. Anche la risposta è JSON e contiene o un risultato inline o un id del task più un campo di stato.
  • Testo obbligatorio, immagine opzionale. Come minimo devi inviare un prompt. Puoi aggiungere immagini di riferimento per guidare soggetto, stile o personaggio.
  • Orientato all'asincrono. La generazione di immagini può richiedere più tempo di un tipico timeout HTTP, quindi l'API supporta task asincroni che puoi interrogare finché non sono completati.

Struttura approssimativa del body:

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

Risposta in modalità asincrona:

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

Quando il task è completato e lo recuperi, status diventa succeeded (o failed) e il payload del risultato contiene l'URL di output. Tieni a mente questo modello a due fasi, perché tutto il resto del tutorial si basa su di esso.

Una richiesta curl eseguibile

Questa è la richiesta minima in grado di generare un'immagine. Presuppone che HIAPI_API_KEY sia già esportata nella tua 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
  }'

Spiegazione campo per campo:

  • model — impostato a nano-banana. HiAPI si basa su di esso per instradare la richiesta.
  • prompt — l'unico campo creativo strettamente obbligatorio. Descrivi con precisione soggetto, composizione e stile; i prompt vaghi sono la causa più comune di output deludenti.
  • async — quando è true, l'API restituisce subito un handle del task. Se preferisci bloccare fino al completamento, impostalo a false, ma preparati a tempi di risposta più lunghi.
  • image_input — array opzionale di URL di immagini di riferimento. Omettilo per la generazione da solo testo.
  • consistency — controllo opzionale di coerenza del personaggio o del soggetto tra immagini; vedi più avanti.

Se non sei sicuro di quali parametri opzionali supporti la versione corrente, consulta prima la pagina di documentazione del modello su HiAPI prima di aggiungere campi: i campi sconosciuti possono essere ignorati o rifiutati a seconda del parametro.

Esegui il comando e controlla il JSON. Supponendo che async sia true, riceverai un oggetto task invece di un'immagine. Salva id; è l'handle per il passo successivo.

Portare la richiesta in Python

La chiamata curl si traduce direttamente. L'obiettivo qui non è solo scrivere uno script usa e getta, ma un wrapper riutilizzabile da inserire in una 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()

Alcuni dettagli lo rendono robusto e non solo funzionante:

  • Timeout esplicito. Passalo sempre; il valore predefinito di "attendi per sempre" trasforma un problema di rete temporaneo in un processo bloccato.
  • Controlla lo stato prima di fare il parsing. Solleva un'eccezione sulle risposte di errore, così una richiesta errata non restituisce silenziosamente un body JSON che poi provi a indicizzare.
  • Ometti i campi opzionali invece di impostarli a vuoto. Anziché inviare "image_input": null, aggiungi la chiave solo quando ha un valore. Questo rende i log più leggibili ed evita l'ambiguità tra null che significa "nessuno" e "non impostato".

Incapsulare tutto in una funzione rende anche semplici i retry: poiché la funzione solleva un'eccezione in caso di errore, il chiamante può avvolgerla in un ciclo di retry senza toccare il codice della richiesta. Quando passi da una singola immagine all'elaborazione in batch, questa separazione ripaga.

Inviare input di testo e immagini

La generazione da testo è il caso semplice. La parte interessante di nano-banana è combinare prompt e immagini di riferimento in un'unica richiesta. Passi image_input come array di URL e il prompt descrive come usarli.

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"],
)

Consigli pratici per le modalità miste:

  • Specifica i ruoli. Se invii più immagini, indica nel prompt quale fornisce il soggetto, quale lo stile e quale lo sfondo. "Combina queste immagini" è vago; "usa il volto dell'immagine uno e l'illuminazione dell'immagine due" non lo è.
  • Usa URL raggiungibili e stabili. L'API deve poter recuperare le immagini. Gli URL firmati che scadono dopo pochi minuti vanno bene per chiamate in tempo reale, ma falliscono se ritenti un task in coda più tardi.
  • Preferisci riferimenti puliti. Un ritratto nitido e ben illuminato produce una somiglianza del personaggio più fedele rispetto a una foto di gruppo affollata. In ambito di condizionamento delle immagini, il principio "spazzatura in ingresso, spazzatura in uscita" è ancora più evidente che con il testo.
  • Itera prima sul prompt, non sull'immagine di riferimento. Se la somiglianza non è corretta, regola prima il modo in cui descrivi il soggetto e solo dopo cambia l'immagine di origine: di solito è la modifica più semplice.

Quando l'immagine di riferimento contiene un personaggio che intendi riutilizzare in più generazioni, entri nel territorio della coerenza del personaggio, cioè la sezione successiva.

Opzioni di coerenza del personaggio

La coerenza del personaggio risponde a un'esigenza precisa: generare più immagini che sembrino avere come protagonista lo stesso personaggio o soggetto. nano-banana la espone come un controllo opzionale, non come un modello separato.

A livello generale, i compromessi sono più o meno questi:

  • Nessun controllo di coerenza. Ideale per immagini una tantum. Veloce e semplice, ma il personaggio può variare se rigenerato. Da usare per sfondi, oggetti e illustrazioni autonome.
  • Coerenza basata su riferimento. Fornisci una o più immagini del personaggio e imposti l'opzione di coerenza, così il modello le usa come ancora. È la scelta corretta per storyboard, fumetti e sequenze di prodotto, in cui l'identità deve mantenersi tra i fotogrammi. Il costo è che devi disporre prima di una buona immagine di riferimento.
  • Coerenza basata su descrizione testuale. Descrivi dettagliatamente il personaggio in ogni prompt — colore dei capelli, abbigliamento, corporatura, tratti distintivi — senza usare riferimenti. Utile quando non hai una foto di origine, ma richiede disciplina: mantieni la descrizione in una costante riutilizzabile, così non varia tra le chiamate.

Un flusso di lavoro pragmatico per una serie:

  1. Genera il primo fotogramma senza coerenza e itera sul prompt finché il personaggio non appare corretto.
  2. Usa quell'output come immagine di riferimento.
  3. Attiva il controllo di coerenza e genera i fotogrammi successivi, mantenendo il riferimento fisso.
  4. Se il personaggio continua a variare, accorcia il prompt e lascia fare più lavoro al riferimento: un prompt lungo che contraddice il riferimento confonde il modello.

La coerenza è una manopola, non una garanzia. Anche in una pipeline ben ottimizzata, prevedi di rivedere gli output e di rigenerare una piccola parte dei fotogrammi.

Interrogare i task asincroni

Quando invii con async: true, ricevi un id del task. L'interrogazione è semplice, ma i dettagli contano più di quanto sembri.

Un poller minimo e corretto:

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")

Il motivo di ogni scelta:

  • Backoff quasi esponenziale con limite massimo. Parti da pochi secondi e moltiplica per 1,5 fino al tetto. Un polling fisso a un secondo per un task da trenta secondi spreca decine di richieste; un polling fisso a trenta secondi aggiunge ritardo inutile ai task veloci.
  • Scadenza rigida. Senza di essa, un task bloccato blocca il tuo processo worker a tempo indeterminato. Meglio fallire in modo evidente.
  • Gestione degli stati terminali. Tratta failed e cancelled come eccezioni, non come stati in cui continuare a interrogare. Un task in stato di errore non avrà mai successo al polling successivo.
  • Esegui una nuova GET a ogni iterazione. Non memorizzare la prima risposta e presumere che si aggiorni.

Per l'elaborazione in batch, invia prima tutti i task, raccogli gli id e poi interrogali in un ciclo: così i tempi di generazione si sovrappongono tra i task invece di essere serializzati.

Scaricare e verificare l'output

Dopo il successo del task, il payload del risultato contiene un URL di output. Il download è una normale GET, ma prima di controllare non devi dare per scontato che quei byte siano un'immagine.

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

Controlli di buon senso che vale la pena fare a ogni download:

  • Content-Type. Se chiedi un'immagine e ricevi application/json o text/html, in realtà hai ricevuto una pagina di errore. Controllare l'header permette di intercettarlo prima di scrivere un file corrotto.
  • Streaming. Scrivi su disco in streaming invece di caricare l'intero body della risposta in memoria; così mantieni stabile la memoria anche con download paralleli.
  • Dimensione non zero. Un file da zero byte significa che qualcosa è andato storto anche se lo stato HTTP è normale.
  • Registra nei log un checksum o il numero di byte. Annotalo insieme all'id del task, così puoi ricollegare un file difettoso a una generazione specifica.

Gli URL di output possono essere temporanei. Se intendi conservare i risultati, scaricali nel tuo storage nella stessa esecuzione, invece di salvare il link per usarlo più tardi.

Punti di errore comuni

La maggior parte dei problemi rientra in poche categorie. Controlla queste prima di supporre che il modello si comporti in modo anomalo.

Autenticazione e routing

  • Un header Authorization mancante o malformato restituisce un errore di autenticazione, non un'immagine. Verifica che l'header sia Bearer <key>, con uno spazio nel mezzo.
  • Inviare un valore model errato instrada la richiesta nel posto sbagliato o fallisce direttamente. Deve essere esattamente nano-banana.

Problemi del body

  • Prompt vaghi. La singola causa più comune di "output scadente". Prima di cambiare qualsiasi altra cosa, aggiungi dettagli su composizione, illuminazione e stile.
  • URL di immagini irraggiungibili. Gli URL di riferimento che restituiscono 404, richiedono autenticazione o puntano a percorsi locali falliscono o vengono ignorati silenziosamente. Recupera tu stesso una volta l'URL per confermare che funzioni.
  • Inviare async: false con un prompt molto lungo e incappare in un timeout del client. Passa all'asincrono e interroga, invece di aumentare il timeout.

Gestione dei task

  • Interrogare un task che non è mai esistito. Un errore di battitura nell'id del task produce un errore di non trovato che sembra un fallimento; registra gli id che invii.
  • Trattare failed come ancora in esecuzione. Esci sempre dal ciclo di polling sugli stati terminali.
  • Nessuna scadenza. Il bug più costoso nei task in batch: una singola immagine bloccata blocca l'intera esecuzione.

Gestione dell'output

  • Non controllare il Content-Type. Come detto sopra, una pagina di errore salvata con estensione .png sembrerà in seguito un'immagine corrotta.
  • Affidarsi a URL di risultato scaduti ore dopo la generazione. Scarica tempestivamente.

Procedendo nell'ordine autenticazione, body, ciclo di vita del task, download, la maggior parte dei guasti si risolve in pochi minuti.

Inizia a creare con artefatti IA persistenti

Ottieni la tua chiave API e chiama modelli di immagini, video e audio tramite un'unica API basata sui task.

Leggi la documentazione API