HiAPI
Blog

Tutoriale

Come usare kling-4.0/text-to-video tramite l'API HiAPI: curl, Python e un esempio di richiesta funzionante

Guida pratica per sviluppatori: prompt cinematografici, audio sincronizzato, polling asincrono e download dell'output

Nov 3, 2026
深炭底色上一条宽大的琥珀色光带斜扫而过,下方配一条细窄的暗棕平行条带,整体用于文章封面。

Funzionalità e casi d'uso di kling-4.0/text-to-video

kling-4.0/text-to-video è un modello di video generativo offerto tramite l'API unificata di HiAPI. Gli passi un prompt testuale e ricevi una clip video: movimento, movimento di camera e composizione della scena sono determinati interamente dalla tua descrizione. È un endpoint text-to-video: non c'è un'immagine sorgente da animare, quindi il modello ha piena autonomia sull'inquadratura e sull'aspetto del soggetto.

Casi d'uso:

  • Devi generare clip cinematografiche brevi a partire da uno script o da un template di prompt: b-roll, trailer, concept board, video per i social media.
  • Vuoi che venga generato un audio sincronizzato insieme al video, invece di ottenere un file muto da sonorizzare in post-produzione.
  • Stai costruendo una pipeline in cui la generazione video è solo uno dei tanti passaggi (scrittura dello script, preparazione degli asset, pubblicazione), quindi ti serve un'API HTTP anziché un'interfaccia web.

Se invece ti serve un controllo preciso frame per frame su un soggetto reale specifico (i modelli image-to-video o di editing sono più adatti in questo caso), oppure hai bisogno di una risposta sincrona nella richiesta dell'utente, questo modello non è la scelta giusta: la generazione video richiede tempo, quindi l'endpoint è asincrono per design.

Poiché tutte le richieste passano per HiAPI, credenziali, formato di richiesta/risposta e ciclo di vita del task sono identici indipendentemente dal modello usato sulla piattaforma. Proprio questa coerenza è il motivo pratico per costruirci sopra: aggiungere o sostituire modelli in futuro non richiederà di riscrivere il livello di autenticazione o di polling.

Prerequisiti e autenticazione su HiAPI

Prima di inviare la prima richiesta, assicurati di avere:

  • Un account HiAPI e una chiave API di progetto. Trattala come riservata: non commetterla mai nel repository e non inserirla mai nel codice lato client.
  • Il client HTTP che preferisci. Gli esempi seguenti usano curl e requests di Python 3.
  • L'identificatore del modello, esattamente come appare nel catalogo: kling-4.0/text-to-video. Un nome del modello scritto male è la causa più comune di fallimento alla prima chiamata.

Le credenziali vengono inviate come header HTTP in ogni richiesta, incluse quelle di polling. Lo schema abituale su HiAPI è il bearer token:

export HIAPI_API_KEY="your-api-key-here"

Poi nella richiesta:

Authorization: Bearer $HIAPI_API_KEY
Content-Type: application/json

Conserva l'URL di base e la chiave in variabili d'ambiente o in un gestore di segreti, non codificarli direttamente. Per lo sviluppo locale bastano un file .env e una lettura tramite os.environ in Python. Per i servizi già distribuiti, inietta i segreti a runtime.

Un'abitudine di configurazione che vale la pena adottare subito: costruisci una piccola chiamata di tipo "chi sono io" — una qualsiasi richiesta leggera di autenticazione — per verificare che chiave e URL di base siano corretti prima di fare debug sui task video. Isolare l'autenticazione dalla logica del modello fa risparmiare tempo in futuro.

Una richiesta curl funzionante

La chiamata minima crea un task e restituisce l'identificatore del task. La struttura è questa:

curl -X POST "$HIAPI_BASE_URL/video/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kling-4.0/text-to-video",
    "prompt": "A slow aerial push over a rain-soaked Tokyo alley at night, neon signs reflecting in puddles, steam rising from vents, shallow depth of field, cinematic color grading",
    "generate_audio": true
  }'

Analisi campo per campo del corpo della richiesta:

  • model — l'identificatore del modello. Obbligatorio.
  • prompt — la descrizione della tua scena. Qui risiede gran parte del tuo budget di qualità; vedi la sezione sui prompt.
  • generate_audio — richiede la generazione di un audio sincronizzato insieme al video. Se ti serve solo la traccia visiva, omettilo o impostalo a false.

La risposta è un oggetto task, non il video completato. Aspettati un campo di stato e un id che userai per il polling — ad esempio con stato queued o processing, più un id del task. Memorizza subito quell'id: se il tuo processo termina, l'id è la tua unica credenziale sul lavoro in corso.

Due suggerimenti pratici. Primo, quando il prompt si allunga, salva il JSON in un file e usa -d @payload.json: la gestione delle virgolette nella shell con prompt multiriga è una fonte comune di errori. Secondo, cattura separatamente il corpo della risposta e il codice di stato HTTP (curl -s -w "\n%{http_code}"), così puoi distinguere una richiesta rifiutata da un guasto di rete.

Convertire la richiesta in Python

La stessa richiesta in Python, strutturata per essere inserita direttamente in uno script reale:

import os
import requests

BASE_URL = os.environ["HIAPI_BASE_URL"].rstrip("/")
API_KEY = os.environ["HIAPI_API_KEY"]

HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

def create_video_job(prompt: str, generate_audio: bool = True) -> dict:
    payload = {
        "model": "kling-4.0/text-to-video",
        "prompt": prompt,
        "generate_audio": generate_audio,
    }
    resp = requests.post(
        f"{BASE_URL}/video/generations",
        headers=HEADERS,
        json=payload,
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()

if __name__ == "__main__":
    job = create_video_job(
        "A slow aerial push over a rain-soaked Tokyo alley at night, "
        "neon signs reflecting in puddles, steam rising from vents, "
        "shallow depth of field, cinematic color grading"
    )
    print(job)

Perché è scritto così:

  • requests.post(..., json=payload) gestisce insieme serializzazione e content type, evitando json.dumps manuale.
  • raise_for_status() trasforma i 4xx/5xx in eccezioni catturabili, invece di interpretare silenziosamente un corpo di errore come un task.
  • timeout è obbligatorio. Senza, una connessione bloccata blocca il worker a tempo indefinito.
  • La configurazione risiede in costanti a livello di modulo lette dall'ambiente, quindi la funzione è testabile e riutilizzabile.

Questa funzione si limita a creare il task. Restituisce immediatamente l'handle del task: è esattamente ciò che ti serve in un handler web o in un worker di coda. Le sezioni successive spiegano come attendere il risultato.

Scrivere prompt cinematografici a prova di errore

I modelli text-to-video premiano la struttura. Un prompt che indica soggetto, camera, movimento ed emozione supera sempre l'idea di una sola frase, perché ogni elemento elimina un'ambiguità che il modello altrimenti deciderebbe a caso.

Un template utilizzabile:

[soggetto e scena], [posizione e movimento della camera],
[movimento nella scena], [illuminazione], [atmosfera o color grading], [riferimento di stile]

Esempio applicato:

All'alba, un ciclista solitario con impermeabile giallo attraversa un ponte di pietra, camera bassa che segue parallelamente all'altezza delle ruote, nebbia che fluttua sull'acqua, luce morbida dell'ora blu con caldi accenti di lanterne, calmo e leggermente malinconico, texture anamorfica da grande schermo.

Il ruolo di ciascuna parte:

  • Soggetto e scena — nomi concreti battono i concetti astratti. "Un ciclista solitario con impermeabile giallo" dà al modello qualcosa da renderizzare; "libertà" no.
  • Camera — posizione (camera bassa, ripresa aerea, altezza occhi) e movimento (inseguimento, lento push-in, statica) sono la leva più potente sul valore percepito della produzione.
  • Movimento nella scena — nebbia che fluttua, tessuti che ondeggiano, increspature sull'acqua. Il video ha bisogno di movimento; dichiaralo esplicitamente.
  • Illuminazione e atmosfera — ora del giorno, temperatura di colore, tono emotivo. Mantienili coerenti tra loro.
  • Stile — il linguaggio di lente o formato (lente anamorfica, profondità di campo ridotta, color grading cinematografico) modella la texture in modo più affidabile rispetto a indicare un regista.

Mantieni il prompt come una descrizione densa di poche frasi, non come un paragrafo di idee in competizione. Se una scena richiede due azioni nettamente diverse, genera due clip. E itera: cambia una sola variabile alla volta — prima la camera, poi l'illuminazione — così impari a cosa reagisce il modello invece di tirare a indovinare.

Gestire task asincroni e polling

La generazione video non è istantanea, quindi il pattern è prima crea, poi fai polling. La parte di creazione l'hai già; ecco il ciclo:

import time

def wait_for_job(job_id: str, timeout: int = 600) -> dict:
    deadline = time.monotonic() + timeout
    delay = 2.0
    while time.monotonic() < deadline:
        resp = requests.get(
            f"{BASE_URL}/video/generations/{job_id}",
            headers=HEADERS,
            timeout=30,
        )
        resp.raise_for_status()
        job = resp.json()
        status = job.get("status")

        if status == "succeeded":
            return job
        if status in ("failed", "cancelled"):
            raise RuntimeError(f"Job {job_id} ended with status {status}: {job}")

        time.sleep(delay)
        delay = min(delay * 1.5, 15.0)  # back off, cap at 15s

    raise TimeoutError(f"Job {job_id} did not finish in {timeout}s")

Decisioni di progettazione importanti in produzione:

Usa il polling con backoff, non un ciclo serrato

Parti da un intervallo breve e aumentalo gradualmente — il backoff esponenziale qui sopra si ferma a un tetto fisso. Un polling continuo e rapido consuma i tuoi limiti di frequenza senza alcun vantaggio, perché il tempo di generazione non si accorcia se chiedi prima.

Imposta sempre una scadenza

Ogni ciclo di polling ha bisogno di un tempo massimo effettivo e di un errore esplicito al superamento del limite. Altrimenti un task bloccato occupa silenziosamente un worker per sempre.

Tratta gli stati terminali come definitivi

Distingui gli stati in corso (queued, processing) da quelli terminali (succeeded, failed, cancelled). Restituisci l'output solo in caso di successo; gli altri stati devono generare un'eccezione o passare a un percorso di retry, non continuare il ciclo.

Disaccoppia il polling dall'handler delle richieste

In un server web, non far bloccare la risposta HTTP in attesa del video. Crea il task, restituisci l'id al client e fai il polling da un worker in background o da un consumer di coda. Persisti l'id del task e il suo ultimo stato noto, così dopo un riavvio non perdi la traccia del lavoro in corso.

Scaricare e verificare l'output

Quando il task raggiunge succeeded, la risposta contiene un riferimento al media generato — di solito un URL al file completato. Scaricalo tempestivamente e considera quell'URL temporaneo: recupera, archivia e registra la tua posizione persistente, invece di passare l'URL del provider direttamente all'utente finale.

import pathlib

def download_output(job: dict, dest: pathlib.Path) -> pathlib.Path:
    url = job["output"]["video_url"]
    with requests.get(url, stream=True, timeout=120) as r:
        r.raise_for_status()
        dest.parent.mkdir(parents=True, exist_ok=True)
        with open(dest, "wb") as f:
            for chunk in r.iter_content(chunk_size=1 << 20):
                f.write(chunk)
    return dest

Passaggi di verifica che vale la pena fare prima di consegnare il file a chiunque:

  • Controlla che il trasferimento sia completo. Fai streaming a blocchi, confronta la dimensione scritta con Content-Length (se presente) e fallisci in modo esplicito se hai letto meno.
  • Conferma la traccia audio. Se hai richiesto generate_audio, il file deve avere uno stream audio. Un rapido controllo con ffprobe è la verifica più economica:
ffprobe -v error -show_entries stream=codec_type -of csv=p=0 output.mp4

Dovresti vedere sia la riga video che quella audio. Se l'audio manca, ricontrolla che il flag fosse impostato nella chiamata di creazione: non è possibile aggiungerlo dopo.

  • Esegui una validazione di base su durata e risoluzione rispetto alle tue aspettative, per intercettare render troncati o con specifiche errate prima della pubblicazione.
  • Archivia con chiavi deterministiche (id del task più hash), scrivi prima su un percorso temporaneo, rinomina solo al termine del trasferimento e conserva l'id del task insieme all'asset per la tracciabilità.

Se il download fallisce a metà trasferimento, riprova con backoff; se l'URL è scaduto, devi recuperare di nuovo il record del task per ottenere un riferimento nuovo.

Incapsulare tutto in un client riutilizzabile

Le parti sopra si compongono naturalmente in una classe. Mettendole insieme ottieni qualcosa da importare tra progetti, senza duplicazioni:

class KlingTextToVideo:
    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url.rstrip("/")
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        }

    def generate(self, prompt: str, generate_audio: bool = True,
                 timeout: int = 600) -> dict:
        job = create_video_job(prompt, generate_audio)  # create half
        return wait_for_job(job["id"], timeout=timeout)  # poll half

Man mano che il client matura, ci sono alcune cose da aggiungere:

  • Log strutturati con chiave l'id del task — transizioni di stato, numero di polling, tempo trascorso e fallimenti. Quando un render si comporta in modo anomalo, l'id del task è la chiave di correlazione tra i log.
  • Retry limitati solo per errori transitori (rete, 5xx). Non riprovare mai ciecamente una richiesta rifiutata; un corpo di richiesta malformato fallisce allo stesso modo ogni volta, consumando solo quota.
  • Implementa l'idempotenza a livello tuo. Se il tuo servizio può inviare due volte lo stesso prompt, deduplica prima di chiamare la creazione, invece di pagare due task.
  • Tieni i template di prompt come dati, non come codice. Metti i template cinematografici in un file di configurazione, così iterare sui prompt non richiede un deploy.
  • Calcola i costi per secondi di output. La fatturazione si basa sull'utilizzo dell'output generato, quindi traccia la durata generata per ogni task per attribuire la spesa a una funzionalità o a un utente.

Una volta pronto il client, la pipeline è breve: costruisci il prompt dal template, chiama generate, scarica e ispeziona l'output, poi consegna il file archiviato alla tua applicazione. Se in futuro aggiungerai altri modelli su HiAPI, gli stessi header di autenticazione, ciclo di vita del task e pattern di download si applicano allo stesso modo: è questo il motivo principale per costruire il livello di astrazione una sola volta.

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