Tutoriale
API PixVerse V6 da immagine a video: esempi curl e Python pronti all'uso
- Modello
- pixverse-v6
Animare un'immagine statica con pixverse-v6/image-to-video su hiapi richiede una sola chiamata POST /v1/tasks più un polling——esattamente lo stesso schema di attività asincrona usato da tutti i modelli generativi della piattaforma. La differenza rispetto all'endpoint text-to-video sta nell'input: devi fornire un fotogramma iniziale (image_url) e un prompt che descriva come deve muoversi. Questa guida offre esempi curl e Python copia-incollabili, i campi di input che l'endpoint valida effettivamente e una sorpresa che farà inciampare chi arriva dal text-to-video.
Cosa ti serve
- Una chiave API hiapi. Creala nella console e inviala tramite
Authorization: Bearer sk-<your-key>. Per il formato dell'header consulta la documentazione sull'autenticazione. - L'ID del modello è
pixverse-v6/image-to-video(mantieni la barra: fa parte dell'ID, non è un percorso URL). È un ID di modello diverso dapixverse-v6/text-to-video. - Un URL immagine accessibile pubblicamente da usare come primo fotogramma. La piattaforma recupera l'immagine lato server, quindi i link a
localhosto a bucket privati non funzionano: ospita il primo fotogramma in un punto raggiungibile tramite GET dall'API. - Con curl non serve installare nulla. Per l'esempio Python bastano la libreria standard e
requests.
PixVerse V6 image-to-video usa l'immagine statica che fornisci come fotogramma iniziale e la anima in una clip di 1–15 secondi. Funziona tramite l'API unificata delle attività asincrone: invii l'attività, esegui il polling del risultato e poi scarichi l'output prima che l'URL scada.
Richiesta minima funzionante (curl)
Invia l'attività a POST /v1/tasks. image_url e prompt sono entrambi obbligatori:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "pixverse-v6/image-to-video",
"input": {
"image_url": "https://cdn.example.com/your-first-frame.jpg",
"prompt": "Slow push-in; the subject turns toward the camera and smiles, soft breeze in the hair",
"duration": 5,
"resolution": "720p"
}
}'
La risposta restituisce un ID attività:
{ "code": 200, "data": { "taskId": "a1b2c3d4-...." } }
Poi esegui il polling di GET /v1/tasks/<taskId> finché data.status non diventa success:
curl -s https://api.hiapi.ai/v1/tasks/a1b2c3d4-.... \
-H "Authorization: Bearer sk-<your-key>"
In caso di successo, l'URL del video completato si trova in data.output[0].url:
{
"code": 200,
"data": {
"status": "success",
"output": [{ "url": "https://temp.hiapi.ai/....mp4", "expireAt": 1760000000 }]
}
}
L'URL di output è temporaneo: contiene un timestamp expireAt. Scarica subito il file .mp4 e salvalo nel tuo bucket; non usarlo come hotlink.
Lo stesso flusso in Python
import time, requests
BASE = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": "Bearer sk-<your-key>", "Content-Type": "application/json"}
def create_task():
payload = {
"model": "pixverse-v6/image-to-video",
"input": {
"image_url": "https://cdn.example.com/your-first-frame.jpg",
"prompt": "Slow push-in; the subject turns toward the camera and smiles",
"duration": 5,
"resolution": "720p",
},
}
r = requests.post(BASE, json=payload, headers=HEADERS, timeout=60)
r.raise_for_status()
data = r.json()
task_id = (data.get("data") or {}).get("taskId")
if not task_id:
raise RuntimeError(f"create failed: {data}") # 400s echo the exact bad field
return task_id
def wait_for(task_id, timeout_s=600):
deadline = time.time() + timeout_s
while time.time() < deadline:
try:
r = requests.get(f"{BASE}/{task_id}", headers=HEADERS, timeout=30)
task = (r.json().get("data") or {})
except requests.RequestException:
time.sleep(5) # one transient network blip should not abort the whole poll
continue
status = task.get("status")
if status == "success":
return task["output"][0]["url"]
if status == "fail":
raise RuntimeError(f"task failed: {task.get('error')}")
time.sleep(5) # still pending — keep the SAME task_id, re-poll
raise TimeoutError(task_id)
task_id = create_task()
url = wait_for(task_id)
video = requests.get(url, timeout=120).content
open("out.mp4", "wb").write(video)
print("saved out.mp4")
Il ciclo di polling controlla esplicitamente success e fail, continuando ad attendere per qualsiasi altro stato, così gestisce in modo robusto qualunque etichetta di stato intermedio che l'attività riporti durante il rendering. Se una richiesta di polling fallisce, dormi e ripeti il polling dello stesso task_id salvato, senza reinviare: un nuovo invio comporta un secondo costo di rendering.
I parametri che pixverse-v6/image-to-video valida davvero
Ecco i campi effettivamente accettati dall'endpoint online. Li ho verificati uno per uno inviando valori non validi e leggendo gli errori di validazione, poi ho fatto un confronto incrociato con la documentazione del modello:
| Campo | Tipo | Valori / intervallo | Descrizione |
|---|---|---|---|
image_url |
string | Un URL immagine pubblico | Obbligatorio. Primo fotogramma. Una singola stringa URL, non un array. |
prompt |
string | — | Obbligatorio. Descrive il movimento applicato al fotogramma. |
duration |
integer | 1–15, predefinito 5 |
In secondi. Deve essere un intero, non una stringa. |
resolution |
string | 360p, 540p, 720p, 1080p |
Risoluzione di output. |
negative_prompt |
string | — | Opzionale; contenuti da evitare. |
seed |
integer | — | Opzionale; riutilizzalo per ottenere risultati riproducibili. |
style |
string | anime, 3d_animation, clay, comic, cyberpunk |
Stile predefinito opzionale. |
generate_audio |
boolean | true / false, predefinito false |
Opzionale; attiva o disattiva la traccia audio/ambiente generata. |
Qui non esiste il campo aspect_ratio. È l'unica vera differenza rispetto alle richieste text-to-video che potresti aver visto, e vale la pena ricordarla (vedi sotto).
Note per la produzione
- Nessun
aspect_ratio: la geometria dell'output segue l'immagine di input. L'endpoint text-to-video accetta un enumaspect_ratio; questo endpoint lo rifiuta. Poiché fornisci tu il primo fotogramma, la clip ne eredita le dimensioni. Se ti serve una clip verticale 9:16, ritaglia o riempiimage_urla 9:16 prima dell'invio: non c'è alcuna manopola del rapporto che possa sovrascriverlo. - La struttura della richiesta è chiusa. Qualsiasi campo al di fuori della tabella sopra viene rifiutato con l'errore
invalid input: <root>: additional properties '<field>' not allowed. Errori di battitura o campi ipotizzati falliscono in modo evidente all'invio, invece di essere ignorati silenziosamente. Campi comefirst_frame,image,input_image,motion_modeecamera_movementvengono tutti rifiutati: l'immagine iniziale va inimage_url, il movimento di camera si scrive nel testo diprompt. durationè un intero, a differenza di alcuni altri modelli video della piattaforma che accettano stringhe ("6"). Inviare"5"restituisceinvalid input: duration: got string, want integer. I valori fuori intervallo riportano direttamente il limite, ad esempioduration: maximum: got 999, want 15.- Gli errori restituiscono un campo alla volta, con un formato piatto:
{"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: image_url: missing required field \"image_url\""}. Correggi il campo segnalato e reinvia; il successivo campo non valido (se presente) emergerà alla chiamata seguente. - Un'attività fallita restituisce comunque HTTP 200. L'informazione di fallimento si trova nel corpo della risposta sotto forma di
data.status: "fail": controlla il campo di stato, non solo il codice di stato HTTP. Un URL immagine che la piattaforma non riesce a recuperare è una causa comune, quindi verifica prima che il tuo primo fotogramma sia pubblico. - Fatturazione al secondo, che cresce con risoluzione e durata, quindi una clip 1080p/15s costa molto più di una 540p/3s. Prima di generare in batch, consulta le tariffe attuali nella pagina dei prezzi.
Cosa ho verificato (e cosa no)
Ho ricostruito il contratto di input inviando valori volutamente non validi all'endpoint /v1/tasks online di pixverse-v6/image-to-video e leggendo le risposte di validazione dell'API stessa: si è trattato di un'esplorazione dello schema in sola lettura, senza callback e senza costi di rendering. Ecco i risultati concreti da ricordare:
image_urlè il campo obbligatorio del primo fotogramma ed è una singola stringa, non un array. Inviare un numero restituisceimage_url: got number, want string; ometterlo restituiscemissing required field "image_url". I campi alternativi ipotizzabili (first_frame,image,input_image) vengono tutti rifiutati come proprietà sconosciute.- Questo endpoint rifiuta
aspect_ratio(additional properties 'aspect_ratio' not allowed), anche se è valido supixverse-v6/text-to-video. È questo il motivo concreto per cui una richiesta image-to-video appare diversa da una text-to-video: la forma dell'output è determinata dal tuo fotogramma, non da un parametro. generate_audioè un vero interruttore booleano (predefinitofalse): inviare una stringa restituiscegenerate_audio: got string, want boolean. Se vuoi l'audio, impostalo atrue; non viene forzato né attivo né disattivo.
In questa esplorazione non ho renderizzato clip, quindi questa guida non esprime giudizi sulla qualità del movimento né su quanto fedelmente il modello preservi il fotogramma di input: documenta solo il contratto di richiesta esatto applicato dall'endpoint.
Contenuti correlati
- PixVerse V6 image-to-video — pagina del modello
- PixVerse V6 image-to-video — documentazione API
- Riferimenti Crea attività e Ottieni dettagli attività
- Autenticazione
- Prezzi
- Altri tutorial image-to-video su hiapi: hailuo-2.3 e Grok Imagine 1.5
Domande frequenti
Perché image-to-video non ha aspect_ratio?Perché la clip inizia con il tuo fotogramma image_url e ne eredita le dimensioni. Per controllare il rapporto di output, ridimensiona o ritaglia l'immagine di input prima dell'invio: l'endpoint rifiuta aspect_ratio come campo sconosciuto.
image_url accetta più immagini?No. Su questo endpoint è una singola stringa URL; passare un array causa un errore di validazione. Imposta solo il fotogramma iniziale.
duration è in secondi o in fotogrammi?In secondi: un intero qualsiasi da 1 a 15, predefinito 5. Deve essere un intero; la stringa "5" viene rifiutata.
In cosa differisce dall'endpoint text-to-video?pixverse-v6/image-to-video anima il fotogramma che fornisci (image_url + prompt, senza aspect_ratio). pixverse-v6/text-to-video è un ID di modello separato, genera solo dal prompt e accetta effettivamente aspect_ratio. Usa image-to-video quando hai già il fotogramma iniziale esatto che vuoi far muovere.
Perché la mia richiesta fallisce con "additional properties not allowed"?Hai inviato un campo che non esiste nello schema. L'input accetta solo image_url, prompt, duration, resolution, negative_prompt, seed, style e generate_audio.
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