HiAPI
Blog

Tutoriale

Come usare kling-3.0-omni/image-to-video tramite l'API HiAPI: guida con curl e Python

Nov 3, 2026
扁平液状色块交叠的抽象封面图,墨色底上点缀青瓷绿与石板灰,用于该教程文章。

Cosa fa l'endpoint image-to-video

La generazione video non è una singola chiamata sincrona. Su HiAPI, kling-3.0-omni/image-to-video segue il modello invia prima, poi esegui il polling: invii una richiesta HTTP che descrive il task, l'API accetta la richiesta e restituisce un identificatore del task, poi interroghi quel task finché il video non è stato generato.

Questa struttura a due fasi ha una ragione ben precisa. Renderizzare un video da un'immagine statica richiede tempo, molto più di quanto una tipica richiesta HTTP dovrebbe restare aperta. Se l'endpoint si bloccasse finché il file non è pronto, ogni chiamata incorrerebbe in timeout del gateway e connessioni interrotte. Restituendo immediatamente un handle del task, l'API lascia al tuo codice decidere quanto attendere, ogni quanto controllare e come gestire gli errori.

Un task tipico comprende tre parti logiche:

  • Media di input: l'immagine (o il set di immagini) che fornisce l'ancoraggio alla scena.
  • Impostazioni di generazione: testo del prompt, opzioni legate al movimento, audio e scelta della risoluzione.
  • Ciclo di vita: la risposta di invio contenente l'ID del task, seguita dai controlli di stato che alla fine restituiscono l'URL del video o il motivo del fallimento.

È utile pensare a questo endpoint come a una coda con ricevuta. La chiamata di invio è la consegna del tuo ordine di lavoro; la ricevuta è l'ID del task; la chiamata di polling è la domanda allo sportello se l'ordine è pronto. All'invio non viene restituito alcun video vero e proprio, quindi qualsiasi client tu scriva dovrebbe essere organizzato fin dall'inizio attorno a queste due fasi.

Ne derivano due conseguenze. Primo, la gestione degli errori deve essere separata: un corpo della richiesta rifiutato è un problema al momento dell'invio, risolvibile nel codice; un fallimento di rendering è un problema a livello di task, che nessun numero di retry dello stesso corpo della richiesta risolverà. Secondo, hai bisogno di un posto dove conservare l'ID del task: una riga di log, una riga di database, una variabile nello script, perché è l'unico handle che hai sul lavoro in corso.

Stabilito questo modello mentale, il resto di questa guida è soprattutto lavoro di conduttura: costruire la richiesta, poi attenderla correttamente.

Costruire una richiesta curl funzionante

Inizia con la richiesta minima possibile che funziona, verifica che venga accettata, e solo dopo aggiungi opzioni. I nomi esatti dei campi JSON e i percorsi degli endpoint sono definiti nella pagina di riferimento del modello su HiAPI: consulta quella pagina per lo schema attuale, invece di fidarti di frammenti tratti da articoli di blog (compreso questo).

La chiamata di invio ha una struttura prevedibile. Ti autentichi con la tua API key HiAPI nell'header Authorization, dichiari Content-Type: application/json e invii un corpo JSON. Concettualmente il corpo della richiesta è così:

{
  "model": "kling-3.0-omni/image-to-video",
  "input": {
    "image": "https://your-cdn.example.com/frame-first.jpg",
    "prompt": "A slow dolly-in as morning light crosses the room"
  }
}

La struttura conta più dei valori letterali: l'identificatore del modello, l'oggetto input che trasporta il riferimento all'immagine sorgente e il prompt, e lo spazio per le opzioni di generazione. Prima di scrivere nel codice ogni chiave, verifica rispetto alla documentazione attuale.

La chiamata curl equivalente segue lo schema comune:

curl -sS -X POST "$HIAPI_BASE_URL/v1/video/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json

Tenere il corpo della richiesta in request.json invece che inline rende le iterazioni molto più semplici: modifichi un file, non una voce nella cronologia della shell.

Leggere la risposta

Un invio riuscito è volutamente scarno. Aspettati un identificatore del task e uno stato che indica accettato ma non completato. Analizzalo, stampalo e salvalo:

curl -sS -X POST "$HIAPI_BASE_URL/v1/video/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json | tee submit.json

Controlla prima il codice di stato HTTP. 4xx significa che la richiesta stessa è stata rifiutata: chiave errata, JSON malformato, opzione non supportata, URL dell'immagine non valido. 2xx significa che il task è entrato nel sistema, e da quel momento il segnale utile arriva dallo stato del task, non dal codice di stato HTTP.

Prima di scrivere qualsiasi codice di polling, esegui manualmente un invio. Conferma di riuscire a vedere l'ID del task nell'output. Questo scambio manuale intercetta presto la maggior parte degli errori di configurazione.

Gestire gli input di primo e ultimo frame

L'immagine che invii non è solo un riferimento: vincola il punto di partenza della scena. Questa distinzione determina la maggior parte delle decisioni di progettazione in questa sezione.

Solo primo frame

Quando fornisci una singola immagine, considerala come frame di apertura. Il prompt descrive poi ciò che dovrebbe accadere a partire da quel frame: movimento di camera, azione del soggetto, cambiamenti di luce, ritmo. Poiché il modello deve creare tutto ciò che viene dopo il primo frame, i prompt che descrivono una traiettoria funzionano meglio di quelli che descrivono una scena statica. "Resta immobile, appare bellissimo" non dà al modello nulla da animare; "lei si volta verso la finestra e la luce diventa più calda" gli dà una direzione.

Aggiungere l'ultimo frame

L'input dell'ultimo frame trasforma il task in un problema di interpolazione. Fornisci due punti finali e il modello genera il percorso tra di essi. Questo è molto potente per transizioni controllate, come un prodotto che ruota dalla vista frontale a quella posteriore o un personaggio che passa tra due pose, perché il punto di arrivo è fissato, non immaginato.

Tre note pratiche:

  • Mantieni i due frame compatibili. Inquadrature, prospettiva e proporzioni del soggetto simili rendono l'interpolazione credibile. Composizioni molto diverse costringono il modello a inventare un ponte che potrebbe non esistere.
  • Riscrivi il prompt. Con due frame, il testo dovrebbe descrivere il movimento tra loro, non i punti finali. Indicare uno stile di movimento e una durata è più naturale che narrare lo stato finale che hai già fornito.
  • Aspettati che la struttura della richiesta cambi. L'ultimo frame è un campo di input separato, quindi il tuo corpo della richiesta avrà una chiave in più. Costruisci l'oggetto della richiesta in modo che i campi opzionali possano essere aggiunti senza ristrutturare tutto.

Nel codice, tratta i frame come un piccolo insieme di input denominati, non come una raccolta disordinata di URL:

inputs = {"image": first_frame_url}
if last_frame_url:
    inputs["last_frame"] = last_frame_url

Questa condizione mantiene le chiamate a un frame e a due frame sullo stesso percorso di codice, cosa che diventa importante non appena inizi a generare varianti.

Infine, ospita i frame in un luogo affidabile e raggiungibile. Un URL che scade a metà task fallirà dopo l'invio, e l'errore sembrerà un fallimento di generazione invece che un problema di risorsa.

Configurare audio e risoluzione di output

Audio e risoluzione sono scelte a livello di richiesta, non impostazioni globali. Le decidi per singolo task, il che significa che dovrebbero stare nel costruttore del corpo della richiesta accanto al prompt, non in una costante di configurazione impostata una volta e dimenticata.

Audio

L'opzione audio in genere ti consente di richiedere una colonna sonora insieme al video, oppure di ometterla. Trattala come una decisione di modellazione legata al caso d'uso: una clip breve per i social può trarre vantaggio dall'audio generato, mentre un frammento a cui aggiungerai musica nell'editor probabilmente non dovrebbe portare audio. Richiedere audio che non userai mai spreca tempo di generazione senza alcun beneficio.

Poiché il comportamento dell'audio è una delle aree più soggette a evoluzione, non codificare ipotesi sulla traccia audio o sulla sua relazione con il prompt. Leggi la documentazione attuale del modello, poi verifica l'output con un task reale prima di progettare qualsiasi flusso downstream attorno ad esso.

Risoluzione

Risoluzioni più alte significano più pixel per frame e molta più potenza di calcolo. Un flusso di lavoro pratico è: iterare a risoluzione moderata mentre si mettono a punto prompt e scelta dei frame, poi rigenerare la configurazione vincente alla risoluzione finale. Rigenerare un buon risultato costa molto meno che scoprire alla fine che il tuo miglior risultato è stato prodotto con le impostazioni sbagliate.

Alcune abitudini che vale la pena adottare:

  • Convalida prima dell'invio. Se il modello non supporta la risoluzione che stai passando, vuoi un rifiuto chiaro al momento dell'invio, non un fallimento a livello di task confuso qualche minuto dopo.
  • Salva le impostazioni insieme al task. Quando confronti più risultati, devi sapere con quale risoluzione e quale scelta audio è stato prodotto ciascuno.
  • Non assumere parità tra i frame. Un task a due frame con immagini sorgente grandi può comportarsi diversamente da un task a un frame; testa entrambi i percorsi.

Dove metterli nel codice

Raggruppa le opzioni di generazione in un oggetto dedicato, così l'intento è evidente e puoi sostituire i valori predefiniti in un solo punto:

options = {
    "resolution": "1080p",   # confirm supported values in the model docs
    "audio": True,
}

Aggiungi commenti alle righe che dipendono da valori documentati. Il te del futuro, o un collega, dovrebbe capire subito quali campi sono sicuri e quali vanno verificati sulla pagina di riferimento prima del rilascio. In caso di dubbio, la documentazione del modello su HiAPI è la fonte di verità, non il corpo di una richiesta di esempio.

Eseguire il polling del completamento in Python

Il codice di polling è dove la maggior parte delle integrazioni diventa silenziosamente fragile. L'obiettivo è costruire un piccolo client riutilizzabile che invii il task e lo attenda con limiti ragionevoli, invece di uno script copiaincolla e modificato ogni volta.

Struttura minima del client

import os, time, requests

BASE = os.environ["HIAPI_BASE_URL"]
KEY = os.environ["HIAPI_API_KEY"]
HEADERS = {"Authorization": f"Bearer {KEY}"}

def submit(payload: dict) -> str:
    r = requests.post(f"{BASE}/v1/video/generations",
                      headers=HEADERS, json=payload, timeout=30)
    r.raise_for_status()
    return r.json()["id"]

def wait(task_id: str, interval: float = 5.0, max_wait: float = 600.0) -> dict:
    deadline = time.monotonic() + max_wait
    while time.monotonic() < deadline:
        r = requests.get(f"{BASE}/v1/video/generations/{task_id}",
                         headers=HEADERS, timeout=30)
        r.raise_for_status()
        job = r.json()
        if job["status"] in ("succeeded", "failed"):
            return job
        time.sleep(interval)
    raise TimeoutError(f"task {task_id} not finished in {max_wait}s")

In quel codice, quattro dettagli portano la maggior parte del peso:

  • Timeout espliciti su ogni chiamata HTTP. Una richiesta bloccata è peggiore di una fallita.
  • Tempo massimo di attesa. Un ciclo infinito trasforma un task bloccato in un servizio bloccato.
  • Controllo dello stato terminale. Fermati solo quando lo stato è davvero definitivo; considera tutto il resto ancora in corso, inclusi gli stati che non riconosci.
  • Restituisci l'intero oggetto del task. Il chiamante di solito ha bisogno dell'URL di output e dei dettagli di eventuali errori, non solo di un booleano.

Ritmo del polling

Per i task video, cinque secondi sono un valore predefinito ragionevole. Eseguire il polling ogni 200 ms consuma quota senza alcun beneficio, dato che il rendering richiede comunque diversi minuti. Se invii molti task, metti il ciclo di polling in un worker invece che nel gestore delle richieste, e applica un leggero backoff ai task di lunga durata.

Collegare il tutto

payload = {
    "model": "kling-3.0-omni/image-to-video",
    "input": {"image": first_frame_url, "prompt": prompt},
    "options": options,
}

job = wait(submit(payload))
if job["status"] == "failed":
    raise RuntimeError(job.get("error") or "generation failed")
print(job["output"]["url"])

Poiché submit restituisce una stringa semplice e wait restituisce un dict, puoi testare ciascuna metà in modo indipendente: simula una risposta di invio per testare la logica di polling, e chiama submit da solo per verificare il corpo della richiesta contro l'API reale.

Diagnosticare i guasti comuni

I guasti si concentrano su tre livelli, e identificare il livello è gran parte del lavoro di diagnosi.

Livello 1: rifiuto all'invio

Questi compaiono come risposte 4xx prima che esista qualsiasi task. Cause comuni:

  • Autenticazione: header mancante, token bearer malformato, chiave non esportata nell'ambiente che credi.
  • Struttura del corpo della richiesta: campo scritto male, stringa dove serve un array, livello di annidamento delle opzioni errato.
  • Valori non supportati: risoluzione o combinazione audio che il modello non accetta.
  • Problemi di risorsa: URL di file non raggiungibile, scaduto o non immagine.

Leggi il corpo della risposta di errore. L'API di solito indica il campo problematico, e la correzione più rapida è spesso quella suggerita dal messaggio. Riproduci la chiamata fallita con curl per confermare che il problema è nel corpo della richiesta e non nel codice del client.

Livello 2: problemi di trasporto

La rete sta tra il tuo codice e il ciclo di vita del task. Timeout e reset di connessione sulla chiamata di invio sono ambigui: il task potrebbe essere stato creato o meno. Se riprovi alla cieca, potresti generare task duplicati.

Il modello sicuro è riprovare solo operazioni idempotenti e sicure, e registrare ogni tentativo di invio con la relativa risposta. Se davvero non riesci a capire se l'invio è riuscito, controlla la cronologia dei task prima di inviare di nuovo. Sul fronte del polling, un singolo controllo di stato fallito non equivale a un task fallito: cattura l'eccezione, attendi e chiedi di nuovo.

Livello 3: fallimenti a livello di task

Questi arrivano come stato terminale su un task che era stato accettato. Di solito significano che il contenuto non può essere generato come specificato: i due frame sono troppo diversi per essere interpolati, il prompt non è conciliabile con l'immagine sorgente, o c'è stato un errore interno durante il rendering.

Distinguili chiaramente nel codice, perché il rimedio è diverso. 4xx significa correggi la richiesta. Un task fallito significa cambia gli input o riprova: se lo stesso corpo della richiesta fallisce due volte, smetti di riprovare e cambia qualcosa: semplifica il prompt, scegli frame più vicini, riduci la risoluzione.

Checklist di triage rapido

  1. Lo stato HTTP è 4xx? Correggi il corpo della richiesta o le credenziali; non fare polling.
  2. Invio riuscito ma il task non si completa mai? Il tuo limite di attesa è troppo breve o il task è bloccato: registra l'ID del task e indaga, invece di ciclare all'infinito.
  3. Il task è fallito? Leggi i dettagli dell'errore, poi modifica gli input invece di reinviare identico.
  4. Riuscito ma il video è sbagliato? È un problema di prompt e scelta dei frame, non dell'API.

Rilascio: checklist per la produzione

Prima di esporlo agli utenti, rafforza le parti che falliscono in silenzio.

Configurazione. Conserva base URL e API key in variabili d'ambiente o in un gestore di segreti, mai nel codice sorgente. Se una delle due manca, fallisci rapidamente all'avvio, non alla prima richiesta di un utente.

Timeout e retry. Ogni chiamata in uscita richiede un timeout esplicito. Riprova gli invii solo dove la ripetizione è tollerabile, e considera sicuri i retry del polling, che sono letture di stato idempotenti. Limita il tempo totale di attesa ed esponi al chiamante un timeout chiaro.

L'ID del task è il record. Persisti l'ID del task insieme al corpo della richiesta inviato: frame, prompt, risoluzione, scelta audio e timestamp. Quando qualcuno ti chiederà due settimane dopo perché il video sembra sbagliato, quella riga sarà tutta la risposta.

L'output è temporaneo. Considera temporaneo l'URL del video restituito. Appena un task riesce, scaricalo e archivialo nel tuo storage persistente, e registra la posizione di archiviazione sul task.

Consapevolezza dei costi. La generazione video viene fatturata sull'output generato, quindi risoluzione, durata e numero di generazioni sono leve importanti. Itera a bassa risoluzione, genera la versione finale in modo consapevole e registra le impostazioni, così da poter attribuire la spesa a un caso d'uso invece di scoprirla in fattura.

Osservabilità. Emetti righe di log all'invio e allo stato terminale, con ID del task, latenza e stato. Una semplice metrica di tasso di successo per modello ti dirà quando una modifica al prompt o un aggiustamento delle opzioni inizia a causare fallimenti dei task.

Verifica rispetto alla documentazione, non rispetto agli esempi. Prima di ogni rilascio che tocca audio, risoluzione o input dei frame, conferma i nomi dei campi e i valori accettati sulla pagina di riferimento del modello, perché sono le impostazioni più soggette a cambiare.

Con il client, il ciclo di polling e queste salvaguardie, kling-3.0-omni/image-to-video diventa un componente prevedibile, non uno script su cui speri che continui a funzionare.

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