HiAPI
Blog

Tutoriale

Come usare flux-2-klein-9b/image-to-image con l'API hiapi: curl, Python e richieste eseguibili

Jul 30, 2026
Come usare flux-2-klein-9b/image-to-image con l'API hiapi: curl, Python e richieste eseguibili

Questo tutorial mostra come chiamare la modalità image-to-image di flux-2-klein-9b tramite l'API unificata delle attività di hiapi: la struttura esatta della richiesta, comandi curl eseguibili, uno script Python pronto da integrare nel backend e una particolarità dello schema che incontrerai se stai migrando da altri modelli della piattaforma.

Al termine avrai uno script che riceve l'URL di una singola immagine di riferimento e un prompt di modifica, e restituisce l'URL di un'immagine generata scaricabile.

Cosa ti serve

  • Un account hiapi e una chiave API. Puoi ottenerla dalla console hiapi.
  • Esattamente un' immagine di riferimento ospitata su un URL HTTPS pubblico. A differenza di alcuni modelli image-to-image della piattaforma che accettano più immagini di riferimento, l'array image_urls di flux-2-klein-9b è limitato a un elemento: dettagli più sotto.
  • Un prompt testuale che descrive la modifica o la trasformazione.

Tutte le richieste vanno all'endpoint unificato delle attività, con la chiave nell'intestazione Authorization:

POST https://api.hiapi.ai/v1/tasks
Authorization: Bearer sk-<your-key>
Content-Type: application/json

Struttura della richiesta (e un campo che facilmente inganna)

L'id del modello è flux-2-klein-9b/image-to-image: passalo così com'è, esattamente come elencato in /v1/models. Il suo schema input è rigoroso:

Campo Tipo Obbligatorio Descrizione
prompt string Sì L'istruzione di modifica.
image_urls string[] Sì Esattamente un URL di immagine di riferimento: sia minItems sia maxItems valgono 1.
aspect_ratio string No Uno tra: 1:1, 4:3, 3:4, 16:9, 9:16.
seed integer No Fissalo per riprodurre i risultati.
output_format string No Uno tra: jpeg, png, webp.

Prima di scrivere qualsiasi codice, vale la pena sapere due cose:

  1. image_urls accetta un solo URL, non un intervallo.Se hai costruito un codice di invio condiviso attorno a un modello che accetta da 1 a 14 immagini di riferimento, qui l'invio di due URL verrà rifiutato: maxItems: got 2, want 1. Questo modello esegue la modifica di una singola immagine, non la fusione di più riferimenti.
  2. I campi aggiuntivi vengono rifiutati, non ignorati.resolution, size, n, strength, image_size e num_images, comuni in altri modelli di immagini della piattaforma, qui restituiscono tutti 400 INVALID_REQUEST: additional properties '<field>' not allowed. Non riutilizzare il payload di un altro modello senza aver controllato prima lo schema di questo.

Esempio minimo eseguibile: curl

Crea un'attività:

curl -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-<your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flux-2-klein-9b/image-to-image",
    "input": {
      "prompt": "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
      "image_urls": ["https://your-cdn.example.com/subject.png"],
      "aspect_ratio": "3:4",
      "output_format": "png"
    }
  }'

In caso di invio riuscito viene restituito un id attività in data:

{"code": 200, "data": {"taskId": "tk-hiapi-..."}}

Esegui il polling per ottenere il risultato:

curl https://api.hiapi.ai/v1/tasks/tk-hiapi-... \
  -H "Authorization: Bearer sk-<your-key>"

Durante l'esecuzione data.status passa in sequenza per queued → handling → archiving. Al termine diventa "success" e l'immagine compare in data.output:

{
  "code": 200,
  "data": {
    "status": "success",
    "output": [{"url": "https://.../result.png", "expireAt": "..."}]
  }
}

L'URL di output è di breve durata (nota expireAt). Scarica subito i byte e archiviali tu stesso: non fare hotlink né rendere persistente l'URL originale.

Python per la produzione: invio, polling, download

import time
import requests

API_BASE = "https://api.hiapi.ai/v1/tasks"
API_KEY = "sk-<your-key>"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}


def edit_image(prompt: str, image_url: str,
               aspect_ratio: str = "1:1", output_format: str = "png") -> bytes:
    # 1. Crea l'attività: image_urls di questo modello accetta esattamente un URL
    resp = requests.post(API_BASE, headers=HEADERS, json={
        "model": "flux-2-klein-9b/image-to-image",
        "input": {
            "prompt": prompt,
            "image_urls": [image_url],
            "aspect_ratio": aspect_ratio,
            "output_format": output_format,
        },
    }, timeout=60)
    body = resp.json()
    if resp.status_code != 200 or not (body.get("data") or {}).get("taskId"):
        raise RuntimeError(f"create failed [{resp.status_code}]: {body}")
    task_id = body["data"]["taskId"]

    # 2. Esegui il polling fino allo stato terminale
    deadline = time.time() + 600
    while time.time() < deadline:
        task = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS,
                            timeout=30).json().get("data") or {}
        if task.get("status") == "success":
            outputs = task.get("output") or []
            if not outputs or not outputs[0].get("url"):
                raise RuntimeError(f"task {task_id} succeeded but returned no output URL")
            # 3. Scarica subito: l'URL scade
            img = requests.get(outputs[0]["url"], timeout=120)
            img.raise_for_status()
            return img.content
        if task.get("status") == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
        time.sleep(5)
    raise TimeoutError(f"task {task_id} still running after 600s")


if __name__ == "__main__":
    png = edit_image(
        "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
        "https://your-cdn.example.com/subject.png",
        aspect_ratio="3:4",
    )
    with open("edited.png", "wb") as f:
        f.write(png)
    print(f"saved edited.png ({len(png)} bytes)")

Modifiche riproducibili con seed

Passa un intero seed e il modello lo riutilizzerà per quella generazione: utile quando vuoi fare A/B test sulla formulazione del prompt mantenendo tutto il resto invariato:

{
  "model": "flux-2-klein-9b/image-to-image",
  "input": {
    "prompt": "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
    "image_urls": ["https://your-cdn.example.com/subject.png"],
    "seed": 42
  }
}

Se lo ometti, il modello sceglie un seed casuale a ogni chiamata, ed è ciò che si vuole nel normale traffico di produzione: quando ti servono output con un aspetto diverso, cambia il prompt, non il seed.

Usare i callback invece del polling

Per script e code di worker il polling va bene. Per un backend Web, registra un callback al momento della creazione dell'attività e lascia che hiapi ti invii lo stato terminale: l'oggetto callback si trova al livello superiore della richiesta, allo stesso livello di model:

{
  "model": "flux-2-klein-9b/image-to-image",
  "input": { "...": "..." },
  "callback": {
    "url": "https://your-app.example.com/hooks/hiapi",
    "when": "final"
  }
}

Con "when": "final" riceverai esattamente una POST quando l'attività raggiunge lo stato di successo o di fallimento. Due regole per il gestore:

  • Rendilo idempotente.Fai in modo che la logica di elaborazione sia indicizzata per id attività, così un webhook riconsegnato non venga elaborato due volte. Memorizza l'id attività al momento dell'invio e considera il callback come "vai a recuperare e finalizza", non come unica fonte di verità.
  • Mantieni un percorso di riconciliazione.Se il tuo endpoint non è disponibile quando scatta il callback, scansiona periodicamente gli id irrisolti ed esegui GET /v1/tasks/<id> per recuperare ciò che è stato perso.

Se il callback sembra non arrivare mai, controlla uno per uno perché il callback delle attività hiapi non viene attivato: l'articolo copre le cause di guasto più comuni in ordine di probabilità.

Gestione degli errori: tre scenari di guasto chiave

401 permission_denied alla creazione: la tua chiave non può usare questo modello. Corpo della risposta:

{"error": {"code": "permission_denied", "message": "...", "request_id": "...", "type": "hiapi_error"}}

Se ti capita, controlla la chiave API non valida oppure verifica i permessi del modello della chiave nella console.

400 INVALID_REQUEST alla creazione: il tuo input non corrisponde allo schema di questo modello. Il messaggio indica direttamente il campo non conforme (ad esempio <root>: additional properties 'resolution' not allowed, oppure image_urls: maxItems: got 2, want 1). Sono errori permanenti: ritentare con lo stesso payload fallirà sempre; correggi il campo e invia di nuovo.

L'attività restituisce status: "fail": la creazione è riuscita ma la generazione è fallita (URL di riferimento non recuperabile dal backend, policy sui contenuti, capacità a monte). error.code e error.message dell'attività indicano quale dei tre. A differenza del 400, alcuni di questi sono transitori e vale la pena ritentare una volta con backoff.

Altra cosa da sapere: una GET su un id attività inesistente o scaduto restituisce 404 task not found: nelle scansioni di riconciliazione va considerato uno stato terminale, non "ancora in attesa".

Dove andare ora

Domande frequenti

Posso passare più immagini di riferimento a flux-2-klein-9b image-to-image? No. minItems e maxItems di image_urls sono entrambi impostati a 1: esattamente un URL, né più né meno. Se ti serve la fusione di più riferimenti, cerca modelli il cui schema documenta un intervallo più ampio, come seedream-5.0-lite.

Perché ricevo "additional properties 'resolution' not allowed"? Lo schema input dell'API delle attività è specifico per modello. flux-2-klein-9b/image-to-image non accetta affatto i campi resolution o size: le dimensioni di output seguono l'immagine di input e aspect_ratio. Rimuovi qualsiasi campo di dimensione proveniente dal payload di altri modelli.

Posso inviare l'immagine di riferimento in base64 invece che come URL? Lo schema di questo modello accetta image_urls, un array di URL. Ospita l'immagine su un URL HTTPS accessibile (qualsiasi CDN, bucket di object storage o URL pre-firmato va bene) e passa quell'URL.

Cosa controlla esattamente seed? Con lo stesso seed, lo stesso prompt e la stessa immagine di riferimento si riproduce la stessa generazione, utile per A/B test controllati sui prompt. Nel traffico normale omettilo, così ogni chiamata ottiene il proprio seed casuale.

Lo stesso codice vale per flux-2-klein-9b text-to-image? L'endpoint e il flusso sono gli stessi, ma text-to-image ha un proprio schema input (senza image_urls). Sostituisci l'id del modello con flux-2-klein-9b/text-to-image e rimuovi l'immagine di riferimento, poi verifica con una chiamata di test, perché i campi obbligatori cambiano in base alla modalità.

Quanto costa ogni immagine? Il prezzo è calcolato in base all'utilizzo per ogni generazione. Controlla le tariffe aggiornate nella pagina dei prezzi.

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