Tutoriale
API video da immagine di Happyhorse: esempi curl e Python funzionanti (hiapi)
Vuoi trasformare un'immagine statica in un breve video coerente direttamente dal tuo codice. Il modello happyhorse-1.1/image-to-video su hiapi fa esattamente questo: passi un URL di un'immagine e un prompt di movimento opzionale, e in pochi secondi ricevi un MP4 a 720p o 1080p. Questa guida è il percorso funzionante più breve — corpi di richiesta reali, messaggi di errore reali, sia in curl che in Python — così puoi andare in produzione oggi senza impazzire sui nomi dei campi.
Cosa ti serve prima di iniziare
- Una chiave API di hiapi. Ottienila dalla console hiapi — ha la forma
sk-...e va inserita nell'headerAuthorization: Bearer .... - Un URL di immagine accessibile (HTTPS pubblico). I file locali non vanno bene; il modello recupera l'URL da solo.
- Un'intenzione di movimento espressa a parole (opzionale ma consigliata). Più chiaro è il verbo, migliore è l'effetto del movimento.
Il modello è fatturato al secondo di video, con i prezzi indicati nella pagina dei prezzi di hiapi; il resto del catalogo dei modelli è in hiapi models — dai un'occhiata a entrambi prima di aumentare il volume.
La richiesta minima funzionante (curl)
Ogni video su hiapi passa dall'endpoint asincrono /v1/tasks: prima fai una POST di creazione, ricevi un taskId, poi o fai polling su GET /v1/tasks/{taskId} oppure ricevi un callback al completamento.
Crea il task:
curl -X POST "https://api.hiapi.ai/v1/tasks" \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1.1/image-to-video",
"input": {
"image_urls": ["https://example.com/your-still.jpg"],
"prompt": "the cat slowly turns its head toward the camera and blinks",
"resolution": "1080p",
"duration": 5
}
}'
Una risposta di successo è così:
{
"code": 200,
"data": { "taskId": "tk-hiapi-01KW3D..." },
"message": "success"
}
Ora fai polling sul risultato (una volta ogni 5 secondi basta — la maggior parte delle clip termina entro due minuti):
curl "https://api.hiapi.ai/v1/tasks/tk-hiapi-01KW3D..." \
-H "Authorization: Bearer sk-your-key"
Durante l'esecuzione ottieni "status": "handling". Al completamento ottieni:
{
"code": 200,
"data": {
"status": "success",
"model": "happyhorse-1.1/image-to-video",
"output": [
{
"type": "video",
"url": "https://temp.hiapi.ai/.../01KW....mp4",
"expireAt": 1783131245
}
],
"taskId": "tk-hiapi-01KW3D..."
}
}
Quel url è ciò che devi scaricare — e dovresti scaricarlo subito, perché expireAt è un timestamp Unix che indica quando l'archiviazione temporanea lo eliminerà.
Python: polling fino al successo
Questo è uno script completo ed eseguibile — inserisci la tua chiave e l'URL dell'immagine e parti.
import os
import time
import requests
API = "https://api.hiapi.ai/v1/tasks"
TOKEN = os.environ["HIAPI_KEY"] # sk-...
def create_video(image_url: str, prompt: str, duration: int = 5,
resolution: str = "1080p") -> str:
"""Submit an image-to-video task. Returns the taskId."""
r = requests.post(
API,
headers={"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json"},
json={
"model": "happyhorse-1.1/image-to-video",
"input": {
"image_urls": [image_url], # exactly one URL
"prompt": prompt, # optional
"resolution": resolution, # "720p" or "1080p"
"duration": duration, # int, 3..15
},
},
timeout=60,
)
body = r.json()
if body.get("code") != 200:
raise RuntimeError(f"create failed: {body}")
return body["data"]["taskId"]
def wait_for_video(task_id: str, timeout_s: int = 600,
poll_s: int = 5) -> str:
"""Block until the task succeeds, fails, or times out. Returns the MP4 URL."""
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(
f"{API}/{task_id}",
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=30,
)
task = r.json().get("data") or {}
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(poll_s)
raise TimeoutError(f"task {task_id} did not finish in {timeout_s}s")
def download(url: str, path: str) -> None:
with requests.get(url, stream=True, timeout=120) as r:
r.raise_for_status()
with open(path, "wb") as f:
for chunk in r.iter_content(1 << 16):
f.write(chunk)
if __name__ == "__main__":
task_id = create_video(
image_url="https://example.com/your-still.jpg",
prompt="the dancer spins once and ends in a pose",
duration=5,
resolution="1080p",
)
print("submitted:", task_id)
mp4_url = wait_for_video(task_id)
download(mp4_url, "out.mp4")
print("saved out.mp4")
Il tutto è di circa 50 righe, ed è l'intera superficie API pronta per la produzione — nessun SDK, nessuna coda in background, nessun protocollo di streaming. Eseguitelo con HIAPI_KEY=sk-... python video.py.
In produzione: callback invece del polling
Il polling va bene per task una tantum e per i test. Per il carico del server, vuoi che la piattaforma ti richiami quando la clip è pronta — niente loop, scalabilità senza lavoro aggiuntivo, e il flusso di richieste ritorna immediatamente.
Aggiungi un oggetto callback al livello radice del corpo della richiesta (non dentro input):
curl -X POST "https://api.hiapi.ai/v1/tasks" \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1.1/image-to-video",
"input": {
"image_urls": ["https://example.com/still.jpg"],
"prompt": "slow zoom out from the subject",
"resolution": "1080p",
"duration": 6
},
"callback": {
"url": "https://your-app.example.com/webhooks/hiapi",
"when": "final"
}
}'
Quando il task raggiunge lo stato terminale, hiapi fa una POST al tuo URL con lo stesso oggetto task che avresti potuto ottenere via polling (status, output[].url, error, ecc.). when: "final" significa che riceverai solo callback di successo o fallimento — nessun rumore di avanzamento.
Alcune regole che fanno risparmiare tempo di debug reale:
- Rendi il webhook idempotente. Lo stesso
taskIdpuò arrivare due volte (i retry di rete esistono davvero). Prima di elaborare, controlla il task nel database. - Ripubblica l'MP4 all'interno del gestore. L'URL che hiapi ti fornisce punta a un'archiviazione temporanea con un campo
expireAt; alla prima ricezione trasmettilo nel tuo bucket, altrimenti lo perdi. - Conferma rapidamente. Restituisci subito un
2xx, poi fai il lavoro pesante in modo asincrono. I webhook lenti rischiano retry. - Usa il polling come fallback. Incapsula il
taskIdin un task ritardato e fai polling dopo 10 minuti, nel caso il webhook vada perso. Se ti capita spesso, consulta possibili cause del mancato callback dei task hiapi per le cause comuni.
Messaggi di errore che incontrerai
Queste sono le stringhe esatte restituite dall'API; confrontarle nel tuo codice è più affidabile che indovinare.
| Situazione | HTTP | Forma del corpo di risposta |
|---|---|---|
| Chiave errata o revocata | 401 | {"error":{"code":"permission_denied","message":"This API key cannot use the selected model...","type":"hiapi_error"}} |
| Campo obbligatorio mancante | 400 | {"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: image_urls: missing required field \"image_urls\""} |
resolution non valido |
400 | ... resolution: value must be one of '720p', '1080p' |
| Durata fuori intervallo | 400 | ... duration: minimum: got 2, want 3 / duration: maximum: got 30, want 15 |
Lunghezza di image_urls errata |
400 | ... image_urls: maxItems: got 2, want 1 (o minItems: got 0, want 1) |
Campo sconosciuto a livello radice o dentro input |
400 | ... additional properties 'identity_preserving' not allowed |
| Il task stesso fallisce dopo l'invio | 200 (GET del task) con data.status == "fail" e data.error.{code,message} |
Gli errori di autenticazione e validazione indicano che non devi riprovare; tutti gli altri casi (5xx della piattaforma, timeout di rete durante il polling, task fail transitori con codici ritentabili) sono adatti a un piccolo retry con backoff.
Ottimizzare l'output
Ci sono solo quattro parametri che contano davvero — qualsiasi altro (seed, aspect_ratio, fps, cfg_scale, negative_prompt) al momento viene rifiutato come campo sconosciuto. Resta all'interno di questo insieme:
image_urls— un URL HTTPS. Il rapporto d'aspetto e la composizione dell'output seguono questa immagine.prompt— una breve descrizione del movimento ("she smiles and tilts her head"). Le indicazioni cinematografiche guidate da verbi battono gli elenchi di aggettivi.resolution—"720p"per prototipi e miniature,"1080p"per la consegna.duration— secondi interi, da3a15. Clip più lunghe costano più tempo e più denaro; scegli la durata più breve che racconta la storia.
Per progetti con frame iniziale e finale, consulta happyhorse-1.1 reference-to-video. Per il puro text-to-video senza immagine di partenza, happyhorse-1.1 text-to-video è il capitolo gemello. Sfoglia il resto in hiapi models catalogue o nella sezione delle ricette del blog.
FAQ
Posso passare due immagini per controllare primo e ultimo frame?
Su happyhorse-1.1/image-to-video no — image_urls accetta al massimo un elemento (maxItems: 1). Se hai bisogno di questo, usa happyhorse-1.1/reference-to-video oppure costruisci con happyhorse-1.1 reference-to-video.
Che risoluzione ha effettivamente l'MP4 che ottengo?
L'output segue il rapporto d'aspetto dell'immagine di input e il flag resolution che hai impostato. "1080p" dà il più alto dei due; "720p" è più veloce ed economico per i test.
Perché la mia chiave funziona su altri modelli ma ottengo 401 permission_denied?
Alcuni modelli sono limitati per chiave. Controlla la pagina del modello nella console hiapi — se happyhorse-1.1/image-to-video non è nella tua allowlist, genera una nuova chiave o contatta il supporto indicando il request_id presente nell'errore.
Ci sono risposte in streaming/SSE? No. Ogni modello video usa il pattern asincrono a task descritto in questo articolo. Scegli polling o callback in base a se controlli tu il server.
Dove vedo il costo effettivo per clip?
Nella pagina dei prezzi di hiapi. Il costo è al secondo di video, quindi il duration che passi determina direttamente la spesa — mantieni le clip di sviluppo/test a duration: 3 per risparmiare.
L'URL di output non funziona più dopo un giorno.
È il comportamento previsto — l'URL è un'archiviazione temporanea e la risposta contiene un timestamp Unix expireAt. Scarica sempre l'MP4 nel momento in cui il task ha successo (o trasmettilo tramite la tua CDN).
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