Tutoriale
Come usare l'API di text-to-video Ray 3.2: curl, Python e una richiesta eseguibile subito
Ray 3.2 è un modello text-to-video del catalogo modelli di hiapi e, come tutti gli altri modelli video, si richiama tramite l'API asincrona a task unificata: l'id del modello è ray-3.2/text-to-video e si invia a POST /v1/tasks. A differenza di alcuni modelli video in cui l'unico controllo è il prompt, Ray 3.2 offre tre controlli reali sull'output — duration, resolution e aspect_ratio — ciascuno validato rigorosamente lato server.
Questa guida ti permette di inviare subito una richiesta che va a buon fine: lo schema di input preciso (verificato contro l'API di produzione il 2026-10-19), una chiamata curl da copiare e incollare, uno script Python completo per invio/polling/download e i dettagli per la produzione — callback, retry idempotenti e le risposte di errore che incontrerai davvero.
Cosa costruirai e cosa ti serve
Obiettivo: inviare un prompt testuale, fare polling del task e scaricare una clip .mp4 senza audio (da 5 o 10 secondi).
Prerequisiti:
- Una chiave API hiapi — ottienila dalla tua console. La chiave ha la forma
sk-...e va inserita nell'headerAuthorization: Bearer. Nuovo alle API a task? Inizia dalla guida rapida. - Un qualsiasi client HTTP. Mostreremo
curle Python (requests).
L'output di Ray 3.2 è fatturato per clip, e il prezzo cresce notevolmente con le tue scelte — consulta le tariffe attuali nella pagina dei prezzi. Quando iteri sul prompt, parti da 540p / 5 secondi; una clip 1080p da 10 secondi può costare molte volte di più di un rendering di immagini contenuto nel budget, quindi non lasciare il tuo ciclo di sviluppo sempre impostato al massimo.
Lo schema di input esatto (un campo obbligatorio, tre enum rigorosi)
Tutto va dentro un oggetto input. Verificato campo per campo contro l'API di produzione:
| Campo | Tipo | Obbligatorio | Valori ammessi | Valore predefinito |
|---|---|---|---|---|
prompt |
string | Sì | Descrizione della scena, massimo 6000 caratteri | — |
duration |
integer | No | 5 o 10 |
5 |
resolution |
string | No | 540p, 720p, 1080p |
540p |
aspect_ratio |
string | No | 16:9, 9:16, 1:1, 4:3, 3:4, 21:9 |
16:9 |
Prima di scrivere codice, tre cose da tenere a mente:
durationè un intero, non una stringa. Invia un numero nudo —"duration": 5. È l'opposto di alcuni altri modelli video dell'API a task (ad esempio hailuo-2.3 richiede"duration": "6"come stringa). Se porti un body da un modello a un altro, ricontrolla questo campo; è l'errore cross-modello più comune.- I valori enum sono validati, non troncati. I valori fuori intervallo vengono rifiutati con
400, non arrotondati silenziosamente. Non esiste2k, né480p, né2:1. - Non esistono
prompt_optimizer,seedonegative_prompt. La composizione si controlla interamente tramiteaspect_ratiopiù il prompt. Se invii campi che lo schema non riconosce, aspettati un 400 — lo schema di input di/v1/tasksvaria da modello a modello, quindi non dare per scontato che chiavi valide altrove siano riutilizzabili qui.
Cosa restituisce davvero la validazione
Ecco risposte reali dell'endpoint di produzione (invio deliberato di valori non validi, nessun task creato, nessun addebito):
// POST with "resolution": "2k"
{"code":400,"data":null,"error_code":"INVALID_REQUEST",
"message":"invalid input: resolution: value must be one of '540p', '720p', '1080p'"}
// POST with "duration": 7
{"code":400,"data":null,"error_code":"INVALID_REQUEST",
"message":"invalid input: duration: value must be one of 5, 10"}
// POST with "aspect_ratio": "2:1"
{"code":400,"data":null,"error_code":"INVALID_REQUEST",
"message":"invalid input: aspect_ratio: value must be one of '16:9', '9:16', '1:1', '4:3', '3:4', '21:9'"}
Il message indica il campo esatto e l'insieme esatto dei valori ammessi — quindi se dimentichi un valore enum, l'API te lo dice. Nota che i valori 5, 10 di duration sono senza virgolette (numeri), mentre '540p' di resolution è una stringa tra virgolette: questa differenza nelle virgolette ti ricorda quale campo è un intero.
Esempio minimo eseguibile: curl
Crea il task:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "ray-3.2/text-to-video",
"input": {
"prompt": "Di notte sotto la pioggia, una ballerina di strada gira sotto le luci al neon, le pozzanghere riflettono luci rosa e blu, la sua giacca svolazza mentre ruota, la camera la circonda dal basso, atmosfera cinematografica",
"duration": 5,
"resolution": "540p",
"aspect_ratio": "9:16"
}
}'
La risposta di creazione include un id del task (troncato):
{ "data": { "taskId": "task_01k..." } }
Esegui il polling finché non raggiunge uno stato terminale:
curl -s https://api.hiapi.ai/v1/tasks/task_01k... \
-H "Authorization: Bearer sk-YOUR_KEY"
Mentre il task è in esecuzione, data.status riporta uno stato in corso. Gli stati terminali sono success e fail. In caso di successo (troncato):
{
"data": {
"taskId": "task_01k...",
"status": "success",
"output": [ { "url": "https://.../clip.mp4" } ]
}
}
Scarica subito data.output[0].url. Gli URL di output sono firmati e scadono — salva i byte nel tuo storage invece di fare hotlink diretto all'URL del task. Ray 3.2 restituisce un MP4 senza audio (nessuna traccia audio), quindi non aspettare un flusso audio che non arriverà mai.
Script Python completo (invio → polling → download)
import time
import requests
API_BASE = "https://api.hiapi.ai/v1"
API_KEY = "sk-YOUR_KEY"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
def create_task(prompt: str, duration: int = 5,
resolution: str = "540p", aspect_ratio: str = "9:16") -> str:
resp = requests.post(
f"{API_BASE}/tasks",
headers=HEADERS,
json={
"model": "ray-3.2/text-to-video",
"input": {
"prompt": prompt,
"duration": duration, # 5 or 10 — an INTEGER, not a string
"resolution": resolution, # "540p" | "720p" | "1080p"
"aspect_ratio": aspect_ratio, # "16:9" | "9:16" | "1:1" | "4:3" | "3:4" | "21:9"
},
},
timeout=30,
)
resp.raise_for_status()
return resp.json()["data"]["taskId"]
def wait_task(task_id: str, timeout_s: int = 900, poll_interval: int = 5) -> dict:
deadline = time.time() + timeout_s
while time.time() < deadline:
resp = requests.get(f"{API_BASE}/tasks/{task_id}", headers=HEADERS, timeout=30)
resp.raise_for_status()
task = resp.json()["data"]
if task["status"] == "success":
return task
if task["status"] == "fail":
err = task.get("error") or {}
raise RuntimeError(f"task failed: {err.get('code')}: {err.get('message')}")
time.sleep(poll_interval) # any non-terminal status = still running
raise TimeoutError(f"task {task_id} not finished after {timeout_s}s")
task_id = create_task(
"Una barchetta di carta che va alla deriva lungo un canale di scolo pieno di pioggia, "
"le gocce creano increspature sulla superficie, foglie autunnali vorticano passando, "
"ripresa dal basso vicino all'acqua, luce morbida di una giornata nuvolosa"
)
print("task created:", task_id)
task = wait_task(task_id)
video_url = task["output"][0]["url"]
# Output URLs expire — download right away and store the bytes yourself.
video = requests.get(video_url, timeout=120).content
with open("ray32-clip.mp4", "wb") as f:
f.write(video)
print(f"saved ray32-clip.mp4 ({len(video) / 1e6:.1f} MB)")
Installa le dipendenze con pip install requests, inserisci la tua chiave e al termine del task avrai un .mp4 sul disco. I task video richiedono minuti, non secondi — dai al tuo ciclo di polling una scadenza ampia (sopra sono 15 minuti) invece di dare per scontato che torni subito.
Considerazioni per la produzione: callback, idempotenza, errori
Preferisci le callback al polling
Per qualsiasi uso oltre gli script, registra un webhook al momento della creazione del task invece di fare polling:
{
"model": "ray-3.2/text-to-video",
"input": { "prompt": "...", "duration": 10, "resolution": "720p" },
"callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}
Con "when": "final", hiapi chiama il tuo endpoint una volta quando il task raggiunge lo stato terminale — nessun traffico di polling e nessuna notifica di completamento persa tra un intervallo e l'altro. Il tuo handler esegue quindi GET /v1/tasks/<id> (o legge il body della consegna) per ottenere output[0].url. Per CLI e notebook che non possono esporre un endpoint HTTPS, il polling resta valido.
Mantieni idempotenti i retry
Persisti il taskId nel momento in cui la chiamata di creazione ritorna. Se il tuo processo si rompe a metà della generazione, riprendi facendo polling sull'id salvato — non ricreare il task, altrimenti pagherai per un secondo rendering. Ricrea solo se la richiesta di creazione originale stessa è fallita prima di restituire l'id del task. Poiché Ray 3.2 è fatturato per clip e le clip ad alta risoluzione non sono economiche, la creazione duplicata è un errore di costo concreto, non solo una perdita di tempo.
Gli errori che vedrai davvero
Ogni riga qui sotto è una forma di risposta di produzione verificata:
| Sintomo | Risposta | Come risolvere |
|---|---|---|
| Chiave mancante/non valida | 401, annidata {"error":{"code":"permission_denied","type":"hiapi_error","request_id":"..."}} |
Controlla l'header Authorization: Bearer sk-... e che la chiave abbia accesso. Fornisci request_id quando contatti il supporto. |
| Valore enum errato | 400 INVALID_REQUEST, piatta {"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: <field>: value must be one of ..."} |
Leggi il campo indicato nel message; elenca l'insieme esatto dei valori ammessi. |
duration inviato come stringa |
400 INVALID_REQUEST |
Invia un intero nudo 5 o 10, non "5". |
| Id modello sconosciuto | 400, error_code: "MODEL_UNAVAILABLE", "model not available via /v1/tasks" |
Usa esattamente ray-3.2/text-to-video — controlla l'id canonico nella pagina del modello. |
| Id task errato nel polling | 404, piatta {"code":404,"data":null,"message":"task not found"} (nota: sul 404 non c'è error_code) |
Id errato, oppure stai interrogando con la chiave di un altro account. |
status: "fail" nel polling |
Task in stato terminale, dettagli in data.error |
Leggi data.error.code / data.error.message. I task falliti vengono rimborsati; flag di policy sui contenuti e problemi di capacità a monte emergono qui — per i flag sui contenuti, riprova con un prompt riformulato. |
Risorse correlate
- Pagina del modello text-to-video Ray 3.2 — prova online, stato in tempo reale e listino prezzi completo
- API image-to-video di Ray 3.2 — stessa famiglia di modelli, ma anima un frame di riferimento invece di partire dal testo (schema di input diverso — sonda prima di riutilizzare questo body)
- Come usare l'API text-to-video di Hailuo 2.3 — una ricetta simile in cui
durationè una stringa, utile contrasto con l'intero di Ray - Prezzi hiapi — tariffe video per clip suddivise per risoluzione e durata
Domande frequenti
Qual è l'id del modello e quale endpoint devo chiamare?
ray-3.2/text-to-video, inviato come model di primo livello a POST https://api.hiapi.ai/v1/tasks, con i parametri sotto input. È asincrono — ricevi un taskId, poi fai polling su GET /v1/tasks/<id> (oppure registra una callback).
duration è in secondi e perché "5" fallisce?
Le clip durano 5 o 10 secondi e il campo è un intero: invia 5 o 10. Una stringa tra virgolette, o qualsiasi altro numero come 7, fallisce con 400 INVALID_REQUEST: duration: value must be one of 5, 10.
Posso impostare risoluzione e aspect ratio?
Sì — è proprio ciò che distingue Ray 3.2 dai modelli video solo-prompt. resolution è 540p, 720p o 1080p; aspect_ratio è uno tra 16:9, 9:16, 1:1, 4:3, 3:4, 21:9. Entrambi sono opzionali (predefiniti 540p e 16:9) e rifiutano i valori fuori intervallo con un 400.
Quanto costa una clip?
Ray 3.2 è fatturato per clip e la risoluzione è la leva più importante — a parità di durata, una clip 1080p costa diverse volte una 540p, e una clip da 10 secondi costa circa tre volte una da 5 secondi. Le tariffe cambiano, quindi consulta la pagina dei prezzi invece di codificare un numero fisso, e fai il prototipo a 540p/5 secondi.
Le clip hanno l'audio? No — il text-to-video di Ray 3.2 restituisce un MP4 senza audio. Se ti serve il suono, aggiungilo in post-produzione o usa un modello audio separato.
Ray 3.2 supporta l'image-to-video?
Sì, come id modello separato sulla stessa API a task: ray-3.2/image-to-video. Il suo schema di input è diverso (accetta un'immagine di riferimento), quindi sonda prima di riutilizzare questo body — vedi la pagina del modello image-to-video.
La mia generazione è lenta o sembra bloccata. Cosa è normale?
I task video durano alcuni minuti. Nel tuo ciclo di polling lascia passare gli stati non terminali di data.status e attendi — prevedi una scadenza di 10–15 minuti, oppure usa una callback con "when": "final" così hiapi ti notifica allo stato terminale invece di indovinare un intervallo.
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