Tutoriale
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_urlsdi 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:
image_urlsaccetta 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.- I campi aggiuntivi vengono rifiutati, non ignorati.
resolution,size,n,strength,image_sizeenum_images, comuni in altri modelli di immagini della piattaforma, qui restituiscono tutti400 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
- Pagina del modello flux-2-klein-9b/image-to-image: area di prova e prezzo per immagine.
- Prezzi hiapi: tariffe attuali per tutti i modelli di immagini.
- Tutorial image-to-image di seedream-5.0-lite: la stessa API delle attività, con uno schema che accetta fino a 14 immagini di riferimento invece di 1; un ottimo esempio del perché i payload non si possono trasferire da un modello all'altro.
- Documentazione dell'API asincrona delle attività hiapi: riferimento completo per creazione, polling e callback.
- Documentazione sull'autenticazione hiapi: configurazione della chiave API e formato delle intestazioni.
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.
Articoli correlati
Vedi tutti gli articoli →-
Tutoriale
Come chiamare hailuo-2.3-fast/image-to-video tramite l'API HiAPI: pratica con curl e Python
La prima richiesta funzionante per il livello image-to-video veloce ed economico — dall'ID del modello al download di un MP4
-
Tutoriale
Come chiamare hailuo-2.3/text-to-video tramite l'API HiAPI: curl, Python ed esempi di richiesta eseguibili
-
Tutoriale
Utilizzare Kandinsky 6.0 Pro su HiAPI per la generazione di video da testo: tutorial con curl e Python
Dimostrazione pratica guidata dalla richiesta: costruire la chiamata da prompt a clip video, inviare la richiesta, monitorare lo stato di completamento e scaricare il risultato