Tutoriale
Come chiamare veo-3.1-fast/image-to-video tramite l'API HiAPI (curl + Python)
Una dimostrazione di richiesta eseguibile: input del primo fotogramma, audio nativo, polling asincrono e download dell'output
Cosa costruisce questo tutorial e cosa serve preparare prima
Questo tutorial mostra un'integrazione HiAPI completa ed eseguibile utilizzando veo-3.1-fast/image-to-video: invii un'immagine del primo fotogramma e un prompt, ricevi un'attività asincrona, esegui il polling finché non è completata, quindi scarichi il video generato con il suo audio nativo.
Al termine del tutorial avrai due implementazioni equivalenti: un comando curl eseguibile nel terminale e un client Python che puoi inserire in uno script o in un servizio, entrambi che accedono allo stesso endpoint HiAPI.
Prerequisiti
Prima di iniziare, prepara quanto segue:
- Una chiave API HiAPI. Non salvarla nel controllo del codice sorgente. Negli esempi seguenti viene letta da una variabile d'ambiente, non incollata direttamente nel codice.
- L'URL di base di HiAPI. Usa l'indirizzo mostrato nella tua dashboard HiAPI o nel riferimento API; salvalo in una variabile così da configurarlo una sola volta.
- Un'immagine del primo fotogramma. Un URL accessibile pubblicamente è l'input più semplice. Usa un'immagine reale di cui hai i diritti, in un formato comune come JPEG o PNG.
- Un client HTTP. Per questa dimostrazione,
curlè sufficiente. Per la versione Python serve solorequests, nessun SDK del fornitore. - Una directory locale in cui memorizzare il video scaricato.
Configura l'ambiente
Esporta la chiave e l'URL di base in modo che sia la shell sia gli esempi Python possano riutilizzarli:
export HIAPI_API_KEY="your-api-key"
export HIAPI_BASE_URL="https://api.hiapi.ai"
export FIRST_FRAME_URL="https://example.com/your-first-frame.jpg"
Il percorso esatto dell'endpoint e i nomi dei parametri di veo-3.1-fast/image-to-video sono documentati nel riferimento API HiAPI accanto alla voce del modello: consulta lì l'elenco canonico dei campi invece di affidarti a frammenti copiati altrove. Questo tutorial si concentra sulla struttura della richiesta e sul flusso di lavoro, che restano invariati anche se i dettagli dei campi vanno verificati nella documentazione.
Vale la pena tenere a mente fin da subito un punto: questo è un modello basato su attività, non sincrono. Non ottieni il video direttamente nella risposta di invio. Ottieni un identificatore di attività e il video arriva in seguito.
La struttura di una richiesta image-to-video
Prima di scrivere qualsiasi codice, è utile capire cosa si invia effettivamente e cosa viene restituito. La generazione image-to-video è più pesante della generazione testuale, quindi l'API la modella come attività in background.
Input: prompt e primo fotogramma
Una richiesta veo-3.1-fast/image-to-video ha due parti concettuali:
- Un prompt testuale che descrive il movimento, il comportamento della camera e l'intenzione della scena, cioè cosa dovrebbe accadere dopo il primo fotogramma.
- Un'immagine di riferimento che ancora il primo fotogramma dell'output. È questa la differenza tra image-to-video e text-to-video: il modello prosegue dalla tua immagine invece di creare da zero un fotogramma iniziale.
Il nome stesso del modello suggerisce il compromesso: la variante fast è ottimizzata per una turnaround più rapida nelle attività image-to-video, il che la rende ideale per l'iterazione e per le pipeline in cui la latenza conta più della massima fedeltà.
L'output generato include audio nativo, quindi il file finale è un video completo con sonoro, non una clip muta a cui aggiungere separatamente una colonna sonora.
Ciclo di vita dell'attività
Poiché la generazione richiede tempo, l'interazione si articola in tre fasi:
- Invio. Esegui un
POSTdel prompt e dell'immagine all'endpoint del modello. La risposta è una conferma leggera contenente un identificatore di attività, non il video. - Polling. Usi quell'identificatore per eseguire ripetutamente una
GETsullo stato dell'attività finché non riporta il completamento (o un errore). - Recupero. Una volta completata, la risposta dell'attività punta al video generato, che scarichi e memorizzi.
Mantenere separate queste fasi nel codice è la decisione strutturale più importante. Se le mescoli, retry, timeout e gestione degli errori diventano confusi.
Com'è un invio ben formato
Indipendentemente dalla serializzazione esatta, prevedi una richiesta che contenga:
- L'identificatore del modello
veo-3.1-fast/image-to-video. - Il testo del tuo prompt.
- Il riferimento all'immagine del primo fotogramma.
- L'autenticazione tramite la chiave API nell'header della richiesta.
Tutto il resto (parametri opzionali, valori predefiniti, opzioni di risoluzione dell'output) appartiene al riferimento API HiAPI di quel modello. Leggilo una volta, inserisci i nomi dei campi confermati in costanti e non spargerli nel codebase.
Considera la risposta di invio come una ricevuta. Registrala, salva l'ID dell'attività e passa al polling.
Avviare la chiamata con curl
Inviemmo un'attività. Il comando seguente invia al modello endpoint su HiAPI un corpo JSON contenente il prompt e l'immagine del primo fotogramma, autenticandosi con la chiave presa dall'ambiente.
curl -sS -X POST "$HIAPI_BASE_URL/v1/videos/generations" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "veo-3.1-fast/image-to-video",
"prompt": "Carrellata lenta in avanti lungo un sentiero nel bosco illuminato dal sole, foglie che fluttuano, atmosfera con canto di uccelli",
"image": "'"$FIRST_FRAME_URL"'"
}'
Alcune note su questo comando:
- Il percorso dell'endpoint e il nome del campo
imagevanno verificati nel riferimento API HiAPI diveo-3.1-fast/image-to-video; se differiscono, sostituiscili con i valori canonici. La struttura qui (modello, prompt, immagine) è ciò che conta. - Il prompt conta davvero. Descrivi esplicitamente il movimento e lo spostamento della camera ("carrellata lenta in avanti", "foglie che fluttuano") e menziona l'atmosfera sonora che desideri, perché il modello genera sia le immagini sia l'audio nativo.
-sSmantiene l'output silenzioso ma mostra comunque gli errori, che è proprio ciò che vuoi in una dimostrazione da terminale.- L'immagine viene passata come stringa URL. Se preferisci caricare i byte, consulta le forme di input supportate nel riferimento invece di indovinare.
Leggere la risposta di invio
Un invio riuscito restituisce un oggetto JSON compatto con l'identificatore dell'attività e lo stato, all'incirca così:
{
"id": "job_abc123",
"status": "queued"
}
I nomi dei campi sono solo esempi; verificali con la risposta reale. L'importante è che ora hai un ID da usare per il polling e uno stato che non è quello di un video completato.
Cattura quell'ID nella shell per semplificare i passaggi successivi:
JOB_ID="job_abc123"
Se ricevi un errore di autenticazione, la tua chiave è mancante o malformata. Un errore di validazione di solito significa che il corpo della richiesta non corrisponde allo schema documentato: controlla prima nomi e tipi dei campi. Una risposta di limite di frequenza indica che stai inviando troppo spesso; fai un backoff invece di riprovare in un ciclo serrato.
La stessa richiesta in Python
Di seguito l'invio equivalente in Python. Usa solo requests, legge la configurazione dalle variabili d'ambiente e separa la fase di invio dal polling, così puoi riutilizzare ogni parte.
import os
import time
import requests
API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = os.environ["HIAPI_BASE_URL"].rstrip("/")
FIRST_FRAME_URL = os.environ["FIRST_FRAME_URL"]
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
MODEL = "veo-3.1-fast/image-to-video"
def submit_job(prompt: str, image_url: str) -> str:
payload = {
"model": MODEL,
"prompt": prompt,
"image": image_url,
}
resp = requests.post(
f"{BASE_URL}/v1/videos/generations",
headers=HEADERS,
json=payload,
timeout=60,
)
resp.raise_for_status()
data = resp.json()
job_id = data["id"]
print(f"attività inviata: {job_id} (status={data.get('status')})")
return job_id
if __name__ == "__main__":
job_id = submit_job(
prompt="Carrellata lenta in avanti lungo un sentiero nel bosco illuminato dal sole, "
"foglie che fluttuano, atmosfera con canto di uccelli",
image_url=FIRST_FRAME_URL,
)
Perché questa struttura
- Configurazione in costanti. URL di base, header, percorso dell'endpoint e nome del modello sono definiti una sola volta. Quando il riferimento API ti dice che un nome di campo è diverso, cambi una sola riga.
timeoutesplicito. Un invio che resta bloccato per sempre è peggio di un fallimento rapido. Per un piccolo corpo JSON, sessanta secondi sono abbondanti.raise_for_status()espone subito i fallimenti. Su 4xx e 5xx ottieni un'eccezione invece di interpretare silenziosamente un corpo di errore come un'attività.- La funzione restituisce solo l'ID dell'attività. Il chiamante non ha bisogno di sapere com'è il payload di invio; gli serve solo qualcosa per il polling.
Se la tua applicazione è un servizio web, chiama submit_job nel gestore della richiesta e passa l'ID restituito a un processo in background. Non bloccare la richiesta HTTP mentre il video viene renderizzato.
Eseguire il polling dell'attività asincrona finché il video non è pronto
Ora l'attività esiste ed è in coda. Il polling è il modo con cui scopri quando è completata.
Il ciclo di polling di base
def poll_job(job_id: str, timeout_s: int = 900, initial_delay: float = 5.0) -> dict:
url = f"{BASE_URL}/v1/videos/generations/{job_id}"
delay = initial_delay
deadline = time.time() + timeout_s
while time.time() < deadline:
resp = requests.get(url, headers=HEADERS, timeout=30)
resp.raise_for_status()
data = resp.json()
status = data.get("status")
print(f"status={status}")
if status == "succeeded":
return data
if status in ("failed", "cancelled"):
raise RuntimeError(f"l'attività {job_id} è terminata con status={status}: {data}")
time.sleep(delay)
delay = min(delay * 1.5, 30.0) # backoff con limite a 30s
raise TimeoutError(f"l'attività {job_id} non è terminata entro {timeout_s}s")
Perché esiste ogni parte
- Polling, non webhook. L'integrazione più semplice e affidabile consiste nel chiedere lo stato a intervalli regolari. I webhook sono migliori su larga scala, ma richiedono un endpoint pubblico; il polling funziona ovunque.
- Backoff esponenziale con limite. Le prime interrogazioni sono economiche e frequenti; gli intervalli si allargano in seguito. Il limite impedisce che gli intervalli diventino ingestibili su attività lunghe. Parti da circa cinque secondi e cresci gradualmente.
- Stati terminali espliciti. Il successo è uno stato; errore e annullamento sono altri stati. Tratta tutto il resto come "continua ad attendere", ma non attendere mai all'infinito.
- Scadenza temporale reale. Rispetto al testo, la generazione video è lenta. Scegli un timeout totale generoso adeguato alla pazienza del tuo prodotto e fallisci in modo pulito se viene superato, invece di ciclare per sempre.
- Log degli stati. Stampa o registra ogni transizione di stato. Quando qualcosa va storto, è la prima cosa che desideri avere.
Polling dalla shell
Per un controllo manuale rapido durante il debug, una singola chiamata curl è sufficiente:
curl -sS "$HIAPI_BASE_URL/v1/videos/generations/$JOB_ID" \
-H "Authorization: Bearer $HIAPI_API_KEY"
Rieseguilo ogni pochi secondi, oppure avvolgilo in un piccolo ciclo while finché lo stato non passa a uno stato terminale. Verifica i nomi esatti dei campi e dei percorsi nel riferimento API HiAPI: il ciclo di vita è stabile, ma i dettagli concreti sono la fonte di verità.
Una volta che poll_job restituisce, la risposta dovrebbe contenere un riferimento al video completato. È ciò che scaricherai subito dopo.
Scaricare e verificare l'output
L'attività è completata e la risposta punta al file generato. Scaricarlo è una normale GET HTTP, ma fai bene due cose: scrivilo su disco in streaming e verifica ciò che hai ottenuto.
Recuperare il video
import os
from urllib.parse import urlparse
def download_video(job: dict, out_dir: str = "outputs") -> str:
video_url = job["output"]["url"] # conferma il percorso del campo nel riferimento API
os.makedirs(out_dir, exist_ok=True)
parsed = urlparse(video_url)
ext = os.path.splitext(parsed.path)[1] or ".mp4"
dest = os.path.join(out_dir, f"{job['id']}{ext}")
with requests.get(video_url, stream=True, timeout=120) as r:
r.raise_for_status()
with open(dest, "wb") as f:
for chunk in r.iter_content(chunk_size=1 << 16):
f.write(chunk)
size = os.path.getsize(dest)
if size == 0:
raise IOError(f"il file scaricato è vuoto: {dest}")
print(f"salvato {dest} ({size} byte)")
return dest
Note:
- Streaming a blocchi. I file video sono grandi; caricarli in memoria con
r.contentè uno spreco e può fallire su host con risorse limitate. - Mantieni l'estensione in modo che i lettori e gli strumenti a valle riconoscano il formato del contenitore.
- Assegna il nome in base all'ID dell'attività. È univoco, tracciabile e evita conflitti quando esegui batch.
- Rifiuta i file vuoti. Un download di zero byte è un download fallito, anche se il codice di stato indica 200.
Se l'URL del video è firmato o a tempo limitato, scaricalo subito dopo il completamento dell'attività invece di conservare l'URL per un uso successivo.
Verifica che sia un video reale con audio
Che il file esista non significa che sia utilizzabile. Controllalo:
- Riproducilo localmente con un lettore multimediale e verifica immagini e suono.
veo-3.1-fast/image-to-videogenera audio nativo, quindi un risultato senza suono è un segnale d'allarme da indagare prima che arrivi agli utenti. - Controlla il contenitore con
ffprobe, se hai ffmpeg installato:
ffprobe -hide_banner outputs/job_abc123.mp4
Cerca nell'output un flusso video e un flusso audio, oltre a una durata ragionevole. È anche il modo più rapido per individuare file troncati.
- Controllo di base sul primo fotogramma. Dovrebbe somigliare alla tua immagine di input. In caso contrario, prima di dare la colpa al modello, sospetta dell'URL dell'immagine o del campo usato per passarla.
Una volta verificato il download, salva il file in uno storage persistente e registra anche l'ID dell'attività. Questa mappatura rende possibili debug successivi, riesecuzioni e riconciliazione della fatturazione.
Insidie e controlli prima di scalare
La maggior parte dei problemi di integrazione con le API video asincrone non dipende dal modello. Sono problemi di pipeline. Ecco cosa controllare per primo.
Input immagine
- L'immagine è davvero accessibile? Un URL che funziona nel tuo browser può richiedere autenticazione, bloccare hotlink o scadere. Preferisci URL stabili e leggibili pubblicamente.
- È un formato supportato? A meno che il riferimento API non dica diversamente, attieniti a formati comuni come JPEG e PNG.
- Il contenuto è corretto? Il primo fotogramma ancora l'intera clip. Un'immagine di bassa qualità o fuori tema produce un video deludente che nessuna regolazione del prompt può riparare.
Struttura della richiesta
- Nomi e tipi dei campi. Verificali nel riferimento API HiAPI di
veo-3.1-fast/image-to-video. I frammenti copiati da altre piattaforme falliscono: l'endpoint è di HiAPI e i nomi dei campi sono sensibili alle maiuscole. - Content-Type. Invia
Content-Type: application/jsone un corpo JSON reale, non dati di form. - Identificatore del modello. Usa la stringa completa
veo-3.1-fast/image-to-video; nomi troncati restituiscono errori apparentemente non correlati.
Timeout e retry
- Timeout separati per ogni fase. Invio, richieste di polling e download del file meritano ciascuno il proprio. Un unico timeout globale o è troppo stretto per il download o troppo largo per i controlli di stato.
- Riprova in sicurezza. L'invio può essere ritentato su errori di rete transitori e risposte 5xx, ma solo se puoi determinare se l'attività è stata effettivamente creata; altrimenti rischi doppia fatturazione e doppia generazione. Polling e download sono idempotenti e possono essere ritentati liberamente.
- Fai backoff in caso di limite di frequenza. Rispetta la risposta e rallenta invece di martellare l'endpoint.
Comportamento del polling
- Non eseguire mai il polling in un ciclo serrato. Consuma quota e sembra un abuso.
- Prevedi sempre una condizione di terminazione. Una scadenza che genera un'eccezione è meglio di un'attività bloccata in memoria.
- Registra la marca temporale di ogni transizione di stato. Ti servirà per ricavare la latenza.
Igiene operativa
- Mantieni le chiavi API lato server. Non inviarle mai al browser né incorporarle in app mobili.
- Memorizza l'ID dell'attività insieme a ogni artefatto. È la chiave che collega log, file e record di utilizzo.
- Verifica il download prima della pubblicazione. Controlla la dimensione e, idealmente, i flussi. Un file vuoto o senza audio che arriva agli utenti costa molto più da correggere di un download fallito che ritenti.
- Non dare per scontati i percorsi dei campi. La posizione di
outputnella risposta dell'attività è proprio il tipo di dettaglio che cambia: leggila dal riferimento API, non da un tutorial, incluso questo.
Controlla questo elenco in ordine prima di aprire un ticket sul modello. Nella maggior parte dei casi la correzione riguarda un URL, un nome di campo o la mancanza di backoff.
Questo è il ciclo completo: invia con l'immagine del primo fotogramma e il prompt, esegui il polling dell'attività finché non è completata, quindi scarica e verifica il video con audio nativo. Parti dalla pagina del modello veo-3.1-fast/image-to-video su HiAPI, conferma i campi attuali della richiesta, poi porta le due funzioni precedenti nel tuo servizio.
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