Documentazione
Generazione di immagini
Due endpoint: da testo a immagine e modifica o trasformazione di immagini. Gli URL dei risultati restituiti da entrambi sono permanenti: Kunavo archivia ogni immagine sulla propria CDN.
Kunavo offre /v1/images/generations (da testo a immagine) e /v1/images/edits (da immagine a immagine o modifica). Per impostazione predefinita, entrambi funzionano in modo sincrono: la chiamata rimane in attesa finché l'elaborazione upstream non termina, quindi restituisce gli URL dei risultati. In alternativa, puoi usare la modalità asincrona con "async": true o un webhook_url.
Da testo a immagine
Endpoint: POST /v1/images/generations. Usa il metodo images.generate dell'SDK OpenAI oppure invia direttamente una richiesta curl.
img = client.images.generate(
model="nano-banana-pro",
prompt="a red origami crane on a white desk, soft window light",
size="1024x1024",
)
print(img.data[0].url)Parametri comuni
| Parametro | Tipo | Note |
|---|---|---|
model | string | Qualsiasi slug di modello per immagini da /v1/models. |
prompt | string | Prompt testuale. Sempre obbligatorio. |
size | string | Formato OpenAI, ad esempio "1024x1024". Convertito nel rapporto d'aspetto upstream. |
quality | standard|hd | Quando supportato (gpt-image). |
resolution | 1K|2K|4K | Livello di risoluzione per i modelli a tariffe differenziate: vedi Resolution tiers qui sotto. Il costo dipende dal livello. |
n | int | Numero di immagini. La maggior parte dei modelli ne restituisce sempre 1. |
input | object | Opzione di ripiego: vedi Raw input qui sotto. |
files.kunavo.com: Kunavo archivia ogni immagine sulla propria CDN, quindi, a differenza degli URL upstream grezzi, non scadono mai. Se un’immagine non può essere archiviata, il suo URL è quello temporaneo dell’upstream, su un altro host: scarica quell’immagine e ospitala nuovamente.Livelli di risoluzione
Diversi modelli di immagini applicano prezzi a fasce in base alla risoluzione di output. Passa resolution per scegliere un livello; se lo ometti, viene usato quello predefinito. Il costo viene calcolato in base al livello effettivamente prodotto.
| Modello (e relativa variante -edit) | Livelli — prezzo per ciascuno | Predefinito |
|---|---|---|
| nano-banana-2 | 1K $0.0469 · 2K $0.0707 · 4K $0.1057 | 1K |
| nano-banana-pro | 1K / 2K $0.067 · 4K $0.12 | 2K |
| gpt-image-2 | 1K / 2K / 4K — $0.0633 (fisso) | 1K |
Anche la famiglia veo-3 prevede tariffe differenziate per risoluzione: i prezzi per ciascun livello sono indicati nella pagina /models di ogni modello.
resolution su 4K con una dimensione quadrata, la richiesta viene automaticamente ridotta a 2K, con addebito corrispondente.Modifica o trasformazione di immagini
Endpoint: POST /v1/images/edits. Per le modifiche a una singola immagine, passa l'immagine sorgente come URL (HTTPS o URI data: in base64) tramite image; per i riferimenti a più immagini usa image_urls.
# Image edit / image-to-image: pass the source image URL
import requests
resp = requests.post(
"https://api.kunavo.com/v1/images/edits",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "gpt-image-2-edit",
"prompt": "recolor the crane to bright blue, keep composition",
"image": "https://your-cdn.com/source.png",
},
)
print(resp.json()["data"][0]["url"])Riferimenti a più immagini
Alcuni modelli (Nano Banana Edit, GPT-Image-2 Edit) accettano più immagini di riferimento.
# nano-banana-edit accepts multiple reference images
resp = requests.post(
"https://api.kunavo.com/v1/images/edits",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "nano-banana-edit",
"prompt": "merge subject from img1 with background from img2",
"image_urls": [
"https://your-cdn.com/subject.png",
"https://your-cdn.com/background.png",
],
},
)Modelli di modifica supportati
nano-banana-edit— modifica con Google Nano Bananagpt-image-2-edit— da immagine a immagine con OpenAI
Fornire un'immagine sorgente
La modifica delle immagini e la generazione di video da immagini richiedono entrambe un'immagine sorgente accessibile al modello. Hai tre possibilità: scegli quella più adatta alla tua applicazione.
- Hai già un URL pubblico: passalo direttamente come
image(oppureimage_urlper i video). Fatto. - Hai un file locale: caricalo una volta tramite
POST /v1/files, quindi riutilizza l'URL permanente restituito. - Hai dati binari grezzi nel codice: includili in linea come URI
data:in base64; Kunavo li ospita automaticamente.
Opzione A — carica tramite POST /v1/files
Invia multipart/form-data con un campo file (oppure JSON { "file": "data:..." }). Riceverai un URL files.kunavo.com permanente. Sono accettati file fino a 25 MB.
# Upload a local file → get a permanent Kunavo URL
import requests
with open("source.png", "rb") as f:
up = requests.post(
"https://api.kunavo.com/v1/files",
headers={"Authorization": f"Bearer {API_KEY}"},
files={"file": f},
).json()
# up["url"] is now a permanent https://files.kunavo.com/... URL
edit = requests.post(
"https://api.kunavo.com/v1/images/edits",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "gpt-image-2-edit",
"prompt": "recolor the crane to bright blue",
"image": up["url"],
},
).json()
print(edit["data"][0]["url"])Opzione B — incorporamento in base64
Puoi saltare del tutto il caricamento separato: inserisci un URI data: nel campo image o image_url. Kunavo lo decodifica, lo ospita sulla propria CDN e passa al modello l'URL ospitato.
# Or skip the upload step — pass base64 inline, we host it for you.
import base64, requests
with open("source.png", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
resp = requests.post(
"https://api.kunavo.com/v1/images/edits",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "gpt-image-2-edit",
"prompt": "recolor the crane to bright blue",
"image": f"data:image/png;base64,{b64}",
},
)POST /v1/video/generations: image_url accetta un URL https o un URI data: in base64 per i modelli di generazione di video da immagini, come Veo.Struttura della risposta
Entrambi gli endpoint restituiscono la risposta standard images di OpenAI:
{
"created": 1779360636,
"data": [
{ "url": "https://files.kunavo.com/image/.../result.png" }
]
}Latenza e timeout
La generazione di immagini richiede in genere da 5 a 60 secondi, a seconda del modello. Nella modalità sincrona predefinita, Kunavo mantiene aperta la connessione e interroga il servizio upstream per un massimo di 540 secondi prima di restituire un errore 504. Imposta di conseguenza il timeout lato client oppure usa l'API asincrona per inviare la richiesta in pochi secondi e controllarne lo stato secondo i tuoi tempi:
- Nano Banana / Nano Banana 2: in genere meno di 10s.
- Nano Banana Pro / GPT-Image-2: 15–60s.
- Riferimenti multipli + livello premium: fino a circa 2 minuti.
API asincrona (invio + polling o webhook)
Per impostazione predefinita, entrambi gli endpoint per le immagini sono sincroni: la chiamata mantiene aperta la connessione fino al completamento del rendering upstream, quindi restituisce la risposta standard images di OpenAI. Per processi in batch, livelli premium lenti o chiamate serverless che non possono mantenere una richiesta aperta a lungo, aggiungi "async": true (o un webhook_url) a /v1/images/generations o /v1/images/edits.
La risposta all'invio viene restituita subito con un ID img_*. Interroga GET /v1/images/{id} finché status non diventa completed o failed, oppure registra un webhook e salta del tutto il polling.
# Async: add "async": true (or a webhook_url) to either image endpoint.
# The call returns instantly with an img_ id instead of holding the connection.
import requests, time
API = "https://api.kunavo.com/v1"
H = {"Authorization": f"Bearer {API_KEY}"}
submit = requests.post(
f"{API}/images/generations",
headers=H,
json={"model": "gpt-image-2", "prompt": "a red panda", "async": True},
).json()
task_id = submit["id"] # "img_abc..."
status = submit["status"] # "queued"
# Poll GET /v1/images/{id} until terminal — or skip polling and use a webhook.
while status not in ("completed", "failed"):
time.sleep(3)
r = requests.get(f"{API}/images/{task_id}", headers=H).json()
status = r["status"]
if status == "completed":
print(r["output"]["url"])
else:
print("failed:", r["error"])Oggetto attività
| Campo | Tipo | Note |
|---|---|---|
id | string | ID attività img_* — passalo a GET /v1/ |
status | string | queued | in_progress | completed | failed. |
output | object|null | Al completamento: { url, urls, archived }. archived è false quando un’immagine non è stata archiviata e il suo URL è quello temporaneo dell’upstream. |
error | object|null | In caso di errore: { code, message }. |
expires_at | int|null | Secondi Unix, 30 giorni dopo l’invio: l’attività può essere interrogata almeno fino ad allora. Gli URL dei suoi risultati non scadono. |
GET /v1/images/{id} è limitato al proprietario: una richiesta con un ID non tuo restituisce 404, indistinguibile da un ID mai assegnato. Frequenza di polling consigliata: ogni 3 s, con backoff esponenziale fino a circa 30 s.Webhook
Passa webhook_url (https) all'invio e Kunavo invierà via POST un evento firmato image.completed / image.failed al termine dell'attività: stesso formato e firma dei webhook per video e musica:
X-Kunavo-Webhook-Id— ID evento stabile, identico a ogni nuovo tentativo (usalo per deduplicare).X-Kunavo-Webhook-Timestamp— secondi Unix in cui è stata calcolata la firma.X-Kunavo-Webhook-Signature—sha256=HMAC-SHA256 su{timestamp}.{rawBody}, con la chiave segreta del tuo webhook.
Opzione di ripiego per input grezzo
Per i parametri specifici del modello che Kunavo non mappa (seed, prompt negativo, formato di output), passa input. Viene inviato al servizio upstream così com’è, al posto dell’input che il nostro adattatore genererebbe.
Fa eccezione il campo che seleziona il livello di prezzo di un modello: resolution, oppure rendering_speed per Ideogram. Per un modello con livelli, viene sempre inviato come uno dei livelli del modello e ti viene addebitato il costo di quel livello. Quando input lo omette o fornisce un valore che non corrisponde a nessuno dei livelli del modello, viene inviato e addebitato il livello predefinito. Lo stesso campo alla radice della richiesta non viene letto quando è impostato input.
# Raw upstream 'input': sent as written, in place of what our adapter builds.
# "resolution" sets the billed tier: 2K here, the default tier when left out.
resp = client.images.generate(
model="nano-banana-2",
prompt="", # ignored when input is set
extra_body={"input": {
"prompt": "a futuristic cyberpunk skyline at dusk",
"aspect_ratio": "16:9",
"resolution": "2K",
"output_format": "png",
}},
)input, salti la convalida di Kunavo. Se il servizio upstream rifiuta i tuoi campi, riceverai da noi un errore 422: consulta la documentazione sugli errori e la documentazione upstream del modello.