HiAPI
Blog

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

Nov 3, 2026
深靛蓝底色上三层嵌套的扁平折线箭头依次向右推进,前缘为杏橙色,像信号流程图,是教程文章的封面图。

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 solo requests, 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:

  1. Invio. Esegui un POST del prompt e dell'immagine all'endpoint del modello. La risposta è una conferma leggera contenente un identificatore di attività, non il video.
  2. Polling. Usi quell'identificatore per eseguire ripetutamente una GET sullo stato dell'attività finché non riporta il completamento (o un errore).
  3. 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 image vanno verificati nel riferimento API HiAPI di veo-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.
  • -sS mantiene 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.
  • timeout esplicito. 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-video genera 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/json e 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 output nella 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.

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