Tutoriale
Come usare l'API hailuo 2.3 image-to-video: curl, Python e un esempio di richiesta funzionante
Riepilogo
- Di cosa si tratta: una ricetta funzionante per usare l'API hailuo-2.3/image-to-video su hiapi — trasforma un'immagine statica in un breve video: crea l'attività, esegui il polling, scarica il file
.mp4. - Endpoint:
POST https://api.hiapi.ai/v1/tasks, nel corpo della richiesta scrivi"model": "hailuo-2.3/image-to-video", poi esegui il polling conGET /v1/tasks/<taskId>finché l'attività non raggiunge lo stato terminale. - Due campi di input obbligatori:
prompteimage_url— un singolo URL pubblico che punta al tuo primo frame (non un array). durationè una stringa, non un numero:"6"o"10". Passare l'intero6viene rifiutato con 400.- Lo schema è rigoroso. Non esistono parametri
motion_strength,camera_movementoresolution— se li invii vengono rifiutati con 400. Il movimento è controllato interamente tramite il prompt.
Cosa stai costruendo e cosa devi preparare
Hai un'immagine statica — una foto prodotto, un render di un personaggio, una fotografia — e vuoi che hailuo-2.3 la animi, generando una clip video che inizia esattamente da quel frame. hailuo-2.3 è noto per il movimento fisicamente credibile (tessuti, capelli, meccanica del corpo), quindi è ideale per dare vita alle immagini statiche.
Ti servono due cose:
- Una API key di hiapi. Creala nella dashboard. La key ha la forma
sk-...e va inserita nell'headerAuthorization: Bearer. - Un URL pubblico dell'immagine del primo frame. L'API scarica l'immagine da
image_url, quindi deve essere accessibile senza autenticazione — un link S3/R2/CDN va bene; un percorsolocalhosto un URL firmato scaduto no.
Come ogni modello di generazione su hiapi, anche questo usa l'interfaccia asincrona delle attività: esegui un POST di un'attività, ricevi subito un taskId, poi raccogli il risultato tramite polling o callback. Non esiste un endpoint sincrono per i video — il rendering della clip richiede tempo, oltre i tipici timeout HTTP.
Schema della richiesta
Lo schema di input completo di hailuo-2.3/image-to-video — verificato sull'API in produzione:
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
prompt |
string | ✅ | Descrivi il movimento, non la scena — la scena viene dalla tua immagine. |
image_url |
string | ✅ | Un URL pubblico, cioè il primo frame. È singolare — non image_urls. |
duration |
string | Facoltativo | "6" o "10" (secondi). Deve essere una stringa. |
prompt_optimizer |
boolean | Facoltativo | Fa espandere il tuo prompt dalla piattaforma prima della generazione. |
Lo schema è rigoroso: qualsiasi cosa non presente in questa tabella viene rifiutata. Inviando motion_strength, camera_movement o resolution, riceverai esattamente:
{
"code": 400,
"error_code": "INVALID_REQUEST",
"message": "invalid input: <root>: additional properties 'resolution', 'camera_movement', 'motion_strength' not allowed"
}
Due conseguenze da tenere a mente:
- Nessuna manopola per regolare il movimento. La fisica del movimento di hailuo-2.3 è guidata solo dal prompt — vedi la sezione sulla scrittura dei prompt qui sotto.
- Su questo endpoint non c'è controllo di risoluzione o aspect ratio. Non costruire una UI per parametri che l'API rifiuterà con 400.
Esempio minimo funzionante: curl
Crea l'attività:
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "hailuo-2.3/image-to-video",
"input": {
"prompt": "The woman turns toward the camera and smiles; wind gently moves her hair; slow cinematic push-in",
"image_url": "https://your-cdn.example.com/first-frame.jpg",
"duration": "6",
"prompt_optimizer": true
}
}'
La risposta (estratto) contiene l'id dell'attività sotto data.taskId:
{ "data": { "taskId": "..." } }
Esegui il polling:
curl -s https://api.hiapi.ai/v1/tasks/<taskId> \
-H "Authorization: Bearer sk-<your-key>"
Durante il rendering della clip, data.status riporta uno stato non terminale. Quando passa a "success", l'URL del video si trova in data.output[0].url; se è "fail", data.error.code e data.error.message spiegano il motivo.
Scarica il file immediatamente dopo il successo dell'attività. L'URL di output è firmato e ha una scadenza (expireAt) — salva i byte nel tuo storage, non usare mai l'URL dell'attività come hotlink.
Esempio minimo funzionante: Python
Script completo dello stesso flusso — crea, esegui polling, scarica:
import time
import requests
API_BASE = "https://api.hiapi.ai/v1/tasks"
API_KEY = "sk-<your-key>"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
def create_task() -> str:
payload = {
"model": "hailuo-2.3/image-to-video",
"input": {
"prompt": (
"The cat stretches, hops off the windowsill, and walks toward "
"the camera; soft afternoon light, subtle handheld camera sway"
),
"image_url": "https://your-cdn.example.com/first-frame.jpg",
"duration": "6", # string, not int: "6" or "10"
"prompt_optimizer": True,
},
}
r = requests.post(API_BASE, json=payload, headers=HEADERS, timeout=30)
task_id = (r.json().get("data") or {}).get("taskId")
if not task_id:
raise RuntimeError(f"create failed ({r.status_code}): {r.text[:300]}")
return task_id
def wait_task(task_id: str, timeout_s: int = 900, poll_s: int = 10) -> dict:
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS, timeout=30)
task = r.json().get("data") or {}
status = task.get("status")
if status == "success":
return task
if status == "fail":
err = task.get("error") or {}
raise RuntimeError(f"task failed: {err.get('code')}: {err.get('message')}")
time.sleep(poll_s)
raise TimeoutError(f"task {task_id} not finished after {timeout_s}s")
def main() -> None:
task_id = create_task()
print("task created:", task_id)
task = wait_task(task_id)
url = task["output"][0]["url"]
# The URL is signed and expires -- download immediately, store the bytes yourself.
video = requests.get(url, timeout=120).content
with open("hailuo-output.mp4", "wb") as f:
f.write(video)
print("saved hailuo-output.mp4")
if __name__ == "__main__":
main()
Le attività video richiedono molto più tempo di quelle per immagini, quindi lo script esegue il polling ogni 10 secondi con un limite generoso di 15 minuti. Non eseguire polling più aggressivo di circa ogni 5 secondi — non renderà il rendering più veloce.
Scrivere prompt che controllano il movimento
Poiché non ci sono parametri di movimento, il prompt è il tuo unico volante — e per hailuo-2.3 è sufficiente. Tre regole pratiche:
- Descrivi il movimento, non l'aspetto. Il modello ha già visto la tua immagine.
"a beautiful woman in a red dress"spreca il prompt;"she turns her head slowly to the left as the camera pushes in"lo usa bene. - Indica il movimento di camera con un linguaggio semplice.
"slow push-in","orbit right","handheld sway","static camera"— vanno scritti nel testo del prompt, non in un campo (inesistente)camera_movement. - In caso di dubbio, lascia fare a
prompt_optimizer. Con"prompt_optimizer": truela piattaforma espande i prompt brevi prima della generazione — un'ottima impostazione predefinita per input sintetici. Disattivalo quando vuoi che la tua formulazione accurata venga seguita alla lettera.
Considerazioni per la produzione
Preferisci i callback al polling. Allega un callback alla creazione dell'attività e hiapi invierà via POST il risultato dello stato terminale al tuo endpoint, così non dovrai mantenere un ciclo aperto:
{
"model": "hailuo-2.3/image-to-video",
"input": { "...": "..." },
"callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}
"when": "final" scatta una sola volta allo stato terminale. Il polling va bene per script e notebook; i callback sono la forma corretta per i server — il rendering video è abbastanza lungo che un worker bloccato in polling è un costo reale.
Gestisci esplicitamente questi errori:
401con"code": "permission_denied"— problema con la key, o la key non ha l'autorizzazione per usare questo modello. Il messaggio contiene unrequest_idche puoi inviare al supporto.400 INVALID_REQUEST— violazione dello schema. Il messaggio è preciso, ad esempioduration: got number, want stringoimage_url: missing required field "image_url". Registralo così com'è.402alla creazione dell'attività — credito insufficiente. Nota che i video costano più delle immagini per attività, quindi una key che riesce ancora a eseguire attività per immagini potrebbe iniziare a fallire sulla creazione dei video. Ricarica, o controlla l'utilizzo nella dashboard.- L'attività passa a
"fail"— errore lato generazione; controlladata.error. In particolare, una causa comune per image-to-video:image_urlnon è scaricabile (bucket privato, URL firmato scaduto).
Rendi idempotente il flusso attorno ai retry. Se il tuo processo muore tra la creazione e il download, un riavvio ingenuo creerebbe (e pagherebbe) un secondo rendering. Persisti il taskId subito dopo che create_task restituisce, e al riavvio continua il polling invece di ricreare.
Letture correlate
- Pagina del modello hailuo-2.3/image-to-video — playground, prezzi al secondo e schema aggiornato.
- hailuo-2.3-fast/image-to-video — versione a bassa latenza, stesso flusso di attività.
- hailuo-2.3/text-to-video — nessuna immagine di partenza, generazione solo dal prompt.
- Documentazione hiapi — guida completa all'interfaccia delle attività, inclusi i callback.
- Prezzi — tariffe video al secondo attuali per tutti i modelli.
Domande frequenti
Quali durate supporta hailuo-2.3 image-to-video?
Due: "6" e "10" secondi, passate come stringhe. Qualsiasi altro valore — o lo stesso valore scritto come numero — restituisce 400 INVALID_REQUEST.
Perché "duration": 6 fallisce mentre "duration": "6" funziona?
Lo schema definisce duration come enumerazione di stringhe. L'API te lo dice esplicitamente: duration: got number, want string. È il 400 più comune su questo endpoint.
Posso impostare la risoluzione o l'aspect ratio dell'output?
No. L'endpoint non espone i campi resolution o aspect_ratio, e inviarne uno dà 400 — lo schema rifiuta le proprietà sconosciute invece di ignorarle.
Come posso controllare il movimento di camera o l'intensità del movimento?
Tramite il prompt. Non ci sono parametri camera_movement o motion_strength — scrivi le istruzioni di camera e movimento in linguaggio semplice ("slow push-in", "she walks toward the camera").
Posso passare più immagini di riferimento?
No — image_url è una singola stringa, cioè il primo frame. Questo è diverso da altri modelli su hiapi che accettano un array image_urls; lo schema è definito per modello, quindi controlla sempre la pagina del modello.
Quali formati immagine supporta il primo frame? I formati web standard (JPEG/PNG), serviti da un URL accessibile pubblicamente. Se la piattaforma non riesce a scaricare quell'URL, l'attività fallisce dopo la creazione — quindi verifica che il link sia accessibile pubblicamente prima di inviare.
In cosa differisce da hailuo-2.3/text-to-video?
Il text-to-video inventa la scena solo dal tuo prompt; l'image-to-video fissa il primo frame sulla tua immagine e anima a partire da essa. Il ciclo di vita dell'attività è identico — cambia solo lo schema di input (qui image_url è obbligatorio, mentre nel text-to-video non esiste affatto un campo immagine).
Quanto costa? I video sono fatturati sui secondi di output, e le tariffe cambiano — consulta la pagina del modello hailuo-2.3/image-to-video e la pagina dei prezzi per i valori correnti.
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