HiAPI
Blog

Tutoriale

API PixVerse V6 da immagine a video: esempi curl e Python pronti all'uso

Oct 20, 2026
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 da pixverse-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 localhost o 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 enum aspect_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 riempi image_url a 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 come first_frame, image, input_image, motion_mode e camera_movement vengono tutti rifiutati: l'immagine iniziale va in image_url, il movimento di camera si scrive nel testo di prompt.
  • duration è un intero, a differenza di alcuni altri modelli video della piattaforma che accettano stringhe ("6"). Inviare "5" restituisce invalid input: duration: got string, want integer. I valori fuori intervallo riportano direttamente il limite, ad esempio duration: 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 restituisce image_url: got number, want string; ometterlo restituisce missing 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 su pixverse-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 (predefinito false): inviare una stringa restituisce generate_audio: got string, want boolean. Se vuoi l'audio, impostalo a true; 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

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.

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