HiAPI
Blog

Tutoriale

Come usare l'API image-to-video di Kling 2.5 Turbo (curl + Python)

Esercitazione eseguibile dalla prima immagine al video: crea l'attività, esegui il polling, scarica l'MP4 — con lo schema di input rigoroso elencato per intero.

Oct 21, 2026
Come usare l'API image-to-video di Kling 2.5 Turbo (curl + Python)

TL;DR

  • kling-2.5-turbo/image-to-video trasforma un'immagine del primo fotogramma in una clip video di 5 o 10 secondi; passando l'URL di una seconda immagine è possibile fissare il fotogramma finale. Il modello gira sull'API asincrona delle attività di HiAPI — prima POST /v1/tasks per creare l'attività, poi polling su GET /v1/tasks/{taskId} per ottenere l'MP4 generato.
  • Costo dell'output: $0.06 al secondo: clip da 5 secondi $0.30, clip da 10 secondi $0.60. La fatturazione avviene per ogni secondo di output completato; le attività fallite per input non valido non producono output e non vengono addebitate.
  • Lo schema di input è rigoroso e chiuso. Obbligatori: image_urls (array, 1–2 elementi — attenzione al plurale), prompt, duration (solo 5 o 10). Opzionali: aspect_ratio, negative_prompt, cfg_scale. La forma singolare image_url viene rifiutata direttamente.
  • Il modello è già online su HiAPI: apri la pagina del modello per provarlo nel browser prima di collegare l'API qui sotto.

Cosa costruirai e cosa ti serve

Al termine di questo articolo avrai una chiamata prima immagine → video funzionante: invii immagine e prompt di movimento, esegui il polling dell'attività e scarichi l'MP4. Servono due cose:

  1. Una API Key dalla dashboard di HiAPI — ogni richiesta deve essere inviata come Authorization: Bearer sk-.... Se non l'hai ancora creata, consulta la documentazione sull'autenticazione.
  2. Un URL di immagine del primo fotogramma accessibile pubblicamente. La chiamata di creazione verifica solo la struttura dell'input; l'immagine viene effettivamente recuperata durante l'esecuzione dell'attività, quindi un URL localhost o privato crea un'attività che fallirà in seguito (vedi sotto).

Questo è image-to-video, quindi l'id del modello è kling-2.5-turbo/image-to-video. Se vuoi generare un video solo da un prompt, esiste un endpoint text-to-video separato.

Esempio minimo funzionante (curl)

Crea l'attività. Il nome del modello va al livello superiore; tutto ciò che il modello legge va dentro input:

curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kling-2.5-turbo/image-to-video",
    "input": {
      "prompt": "The perfume bottle rotates slowly on the marble stand, light glints across the glass, petals drift down, slow orbit",
      "image_urls": ["https://static.hiapi.ai/gallery/2026/10/4852c542c4447593.jpg"],
      "duration": 5,
      "aspect_ratio": "9:16"
    }
  }'

Una creazione riuscita restituisce l'id dell'attività — non il video:

{ "code": 200, "data": { "taskId": "tk-hiapi-01M5CW..." }, "message": "success" }

Poi esegui il polling dell'endpoint di dettaglio finché status non raggiunge uno stato finale:

curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-01M5CW... \
  -H "Authorization: Bearer sk-YOUR_KEY"

Mentre l'attività è in esecuzione vedrai stati non finali; al completamento l'URL dell'MP4 è disponibile sotto data.output[0].url:

{
  "code": 200,
  "data": {
    "status": "success",
    "model": "kling-2.5-turbo/image-to-video",
    "storage": "temp",
    "output": [{ "url": "https://temp.hiapi.ai/kling-i2v/tk-hiapi-01M5CW.mp4?expireAt=1761350400" }]
  },
  "message": "success"
}

Quel output[0].url è un link temporaneo — scaricalo subito, non usarlo come hotlink direttamente nella tua applicazione (vedi Note per la produzione).

Esempio minimo funzionante (Python)

Nessun SDK richiesto — l'API delle attività di HiAPI è un'interfaccia HTTP pura, quindi basta la libreria standard:

import time
import requests

API = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": "Bearer sk-YOUR_KEY", "Content-Type": "application/json"}


def create(image_url: str, prompt: str, duration: int = 5) -> str:
    payload = {
        "model": "kling-2.5-turbo/image-to-video",
        "input": {
            "prompt": prompt,
            "image_urls": [image_url],   # 1–2 items; 2nd = last frame
            "duration": duration,        # 5 or 10 only
        },
    }
    r = requests.post(API, json=payload, headers=HEADERS, timeout=60)
    body = r.json()
    task_id = (body.get("data") or {}).get("taskId")
    if not task_id:
        raise RuntimeError(f"create failed: {body}")
    return task_id


def wait(task_id: str, timeout_s: int = 600, interval: int = 5) -> str:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        r = requests.get(f"{API}/{task_id}", headers=HEADERS, timeout=30)
        task = r.json().get("data") or {}
        status = task.get("status")
        if status == "success":
            return task["output"][0]["url"]
        if status == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
        time.sleep(interval)
    raise TimeoutError(f"task {task_id} did not finish in {timeout_s}s")


task_id = create(
    "https://static.hiapi.ai/gallery/2026/10/4852c542c4447593.jpg",
    "slow orbit around the product, light glints across the glass, petals drift down",
    duration=5,
)
mp4_url = wait(task_id)

# Download immediately — the URL is short-lived.
with requests.get(mp4_url, stream=True, timeout=120) as resp:
    resp.raise_for_status()
    with open("clip.mp4", "wb") as f:
        for chunk in resp.iter_content(1 << 16):
            f.write(chunk)
print("saved clip.mp4")

Note per la produzione

Polling o callback. Il polling ogni 5 secondi (come mostrato sopra) è il percorso più semplice ed è quello usato dall'esempio. Se preferisci ricevere i risultati in push, la chiamata di creazione accetta un oggetto callback al livello superiore — {"url": "https://you/webhook", "when": "final"} — e HiAPI invierà una POST al tuo endpoint quando l'attività raggiunge lo stato finale. Il contratto esatto del webhook è nella documentazione sulla creazione delle attività; per la maggior parte delle attività con una singola clip, il polling dell'endpoint di dettaglio è sufficiente.

Basa sempre la logica su data.status, mai sul codice di stato HTTP della chiamata di creazione. Un 200 restituito dalla creazione significa accettato, non renderizzato. La clip esiste davvero solo quando l'endpoint di dettaglio restituisce status: "success".

Tratta gli URL di output come link temporanei. storage è temp e gli URL scadono, quindi scarica l'MP4 subito dopo il successo dell'attività (oppure copialo nel tuo bucket). Non fornire gli URL di HiAPI direttamente agli utenti finali.

Controllo dei costi. Paghi per ogni secondo di output completato — $0.06/secondo — quindi una clip da 5 secondi costa $0.30 e una da 10 secondi $0.60. Durante l'iterazione di prompt e primo fotogramma, esegui prima un render da 5 secondi e poi rilancia a 10 secondi quando sei soddisfatto. Per i prezzi aggiornati vedi la pagina dei prezzi.

Note dalle prove pratiche e avvertenze

Tutto il contenuto di questo articolo deriva da richieste reali all'endpoint di produzione (schema riverificato il 2026-10-21), non solo dalle tabelle dei parametri:

  • Il campo è image_urls (plurale) e lo schema è chiuso. Inviare il naturale image_url fallisce prima della creazione dell'attività:
    { "code": 400, "error_code": "INVALID_REQUEST",
      "message": "invalid input: image_urls: missing required field \"image_urls\"; <root>: additional properties 'image_url' not allowed" }
    
    Qualsiasi campo non riconosciuto (mode, refusi, ecc.) viene rifiutato allo stesso modo — non esistono campi extra ignorati silenziosamente.
  • duration è un'enumerazione, non un intervallo. Sono accettati solo 5 e 10; 7 restituisce duration: value must be one of 5, 10.
  • image_urls contiene 1 o 2 elementi. Un URL è il primo fotogramma; il secondo URL fissa il fotogramma finale per una transizione controllata. Tre URL restituiscono maxItems: got 3, want 2; un array vuoto restituisce minItems: got 0, want 1.
  • Campi opzionali realmente esistenti: aspect_ratio accetta solo 16:9, 9:16, 1:1, 4:3, 3:4, 21:9; cfg_scale ha un massimo di 1 (passare 99 restituisce cfg_scale: maximum: got 99, want 1); negative_prompt deve essere una stringa. Omettere aspect_ratio permette al modello di seguire il primo fotogramma.
  • La chiamata di creazione valida solo la struttura, non la raggiungibilità — questo è il punto più insidioso. Un URL immagine sintatticamente valido ma non raggiungibile restituisce 200 e un taskId, poi l'attività passa asincronamente a stato di errore:
    { "data": { "status": "fail",
      "error": { "code": "INVALID_REQUEST",
        "message": "The image URL is not readable. Please provide a valid public HTTP(S) URL or an accessible file path and try again." } } }
    
    Quell'attività non ha prodotto output e non è stata addebitata — i fallimenti non generano costi — ma se controlli solo la risposta di creazione penserai erroneamente che sia riuscita. Ecco perché il ciclo di polling deve basarsi su data.status.

Contenuti correlati

Domande frequenti

Quanto può durare una clip? 5 o 10 secondi — sono gli unici due valori di duration accettati dallo schema.

Posso controllare il finale? Sì. Inserisci due URL in image_urls: il primo è il fotogramma di apertura, il secondo è il fotogramma finale. Il modello interpola il movimento tra i due.

Perché la creazione restituisce 200 ma l'attività mostra fail? La chiamata di creazione controlla solo che il tuo JSON rispetti lo schema. L'immagine viene recuperata durante l'esecuzione dell'attività, quindi un URL inaccessibile o privato crea l'attività e poi la fa fallire. Leggi sempre data.status dall'endpoint di dettaglio e assicurati che l'URL del primo fotogramma sia pubblicamente accessibile.

Le attività fallite vengono addebitate? No. Paghi per ogni secondo di output completato, quindi le attività fallite per input non valido non producono output e non vengono addebitate.

C'è un controllo del rapporto d'aspetto? aspect_ratio è opzionale e accetta 16:9, 9:16, 1:1, 4:3, 3:4 o 21:9. Se lo ometti, si segue il primo fotogramma.

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