Tutoriale
API video da riferimento HappyHorse 1.1: tutorial curl + Python pronto all'uso
HappyHorse 1.1 R2V trasforma fino a nove immagini di riferimento più un prompt testuale in un breve video, mantenendo coerenti soggetto, abbigliamento e stile della scena tra le varie inquadrature. Su HiAPI il modello è disponibile tramite l'endpoint unificato per attività asincrone, con ID modello happyhorse-1.1/reference-to-video. Questo tutorial mostra prima la richiesta minima funzionante, poi una versione di livello produttivo con callback.
Obiettivi e prerequisiti
Completerai le seguenti operazioni:
- Carica (o ospita) da 1 a 9 immagini di riferimento su URL HTTPS accessibili all'API.
- Invia con POST a
https://api.hiapi.ai/v1/tasksun'attività che fa riferimento a questi URL. - Quando l'attività raggiunge lo stato
success, leggi l'indirizzo MP4 finale dadata.output[0].url.
Prerequisiti:
- Un account HiAPI e una chiave API che inizia con
sk-. Puoi ottenerla dalla console HiAPI e consultare la documentazione sull'autenticazione per le regole degli header. La chiave deve essere trasmessa comeAuthorization: Bearer sk-...e non va mai inserita nel codice lato client. - Requisiti di hosting delle immagini: ogni immagine di riferimento deve essere in formato JPEG, PNG o WEBP, con lato più corto ≥ 400px e dimensioni ≤ 20MB. Gli URL CDN pubblici vanno bene; anche gli URL S3 firmati sono validi, purché l'esecutore dell'attività possa accedervi.
Richiesta minima funzionante (curl)
Questo è il corpo della richiesta più piccolo accettato dallo schema. Usa l'output predefinito 1080p / 16:9 / 5 secondi, quindi devi impostare solo i campi prompt e reference_image.
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/reference-to-video",
"input": {
"prompt": "Il personaggio in [Image 1] cammina in una scena in stile [Image 2], inquadratura cinematografica",
"reference_image": [
"https://static.hiapi.ai/example/ref-1.jpg",
"https://static.hiapi.ai/example/ref-2.jpg"
]
}
}'
La risposta è la busta dell'attività; l'unico campo che ti serve davvero è data.taskId:
{
"code": 200,
"data": { "taskId": "task_xxx", "status": "queued" }
}
Esegui polling fino allo stato terminale:
curl https://api.hiapi.ai/v1/tasks/task_xxx \
-H "Authorization: Bearer sk-YOUR_KEY"
Quando data.status diventa success, l'URL del video si trova in data.output[0].url. Tale URL include un timestamp expireAt, quindi scarica subito il file oppure trasferiscilo su uno storage persistente: non collegare direttamente quell'indirizzo sul tuo sito.
Parametri
L'endpoint R2V convalida rigorosamente l'input; i campi sconosciuti restituiscono 400 INVALID_REQUEST. Schema completo (verificato in tempo reale per POST /v1/tasks):
| Campo | Tipo | Obbligatorio | Valori ammessi | Valore predefinito |
|---|---|---|---|---|
model |
string | Sì | happyhorse-1.1/reference-to-video (valore fisso) |
- |
input.prompt |
string | Sì | Usa [Image 1], [Image 2]… per fare riferimento alle immagini, nello stesso ordine di reference_image |
- |
input.reference_image |
string[] | Sì | Da 1 a 9 URL HTTPS, formato JPEG/PNG/WEBP, lato più corto ≥ 400px, ≤ 20MB ciascuna | - |
input.resolution |
enum | No | 720p, 1080p |
1080p |
input.aspect_ratio |
enum | No | 16:9, 9:16, 3:4, 4:3, 4:5, 5:4, 1:1, 9:21, 21:9 |
16:9 |
input.duration |
integer | No | 3-15 (secondi) | 5 |
callback.url |
string | No | URL HTTPS che riceve gli eventi di stato terminale dell'attività | - |
callback.when |
enum | No | final (attualmente l'unico valore) |
final |
Ecco alcune regole di convalida poco intuitive che vale la pena conoscere prima che ti causino problemi:
- Anche con una sola immagine di riferimento, passa
reference_imagecome array. Passare direttamente una stringa restituiscereference_image: got string, want array. durationdeve essere un intero compreso tra 3 e 15 inclusi.999e1restituiscono entrambi un messaggio di errore esplicito di superamento dei limiti.- A livello superiore non esistono
seed,negative_prompt,fps,cfg_scaleosteps. L'API rifiuta le proprietà sconosciute.
Per il prezzo completo in base alla combinazione di risoluzione e durata, consulta la pagina dei prezzi HiAPI: il costo varia in base a entrambi i parametri resolution e duration.
Python: end-to-end + polling
Il blocco di codice seguente è un generatore autonomo che invia l'attività, esegue polling fino allo stato terminale e scarica il video. È volutamente privo di dipendenze (usa solo requests), così puoi copiarlo in qualsiasi servizio. Per una descrizione più completa del ciclo di vita delle richieste, consulta la guida rapida.
import os
import time
import requests
API = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"}
def submit_r2v(prompt: str, reference_images: list[str], duration: int = 5,
resolution: str = "1080p", aspect_ratio: str = "16:9") -> str:
if not 1 <= len(reference_images) <= 9:
raise ValueError("reference_image must contain 1-9 URLs")
payload = {
"model": "happyhorse-1.1/reference-to-video",
"input": {
"prompt": prompt,
"reference_image": reference_images,
"resolution": resolution,
"aspect_ratio": aspect_ratio,
"duration": duration,
},
}
r = requests.post(API, json=payload, headers=HEADERS, timeout=60)
r.raise_for_status()
data = r.json()
if data.get("code") != 200:
raise RuntimeError(f"create failed: {data}")
return data["data"]["taskId"]
def wait_for_video(task_id: str, timeout_s: int = 600, poll_s: int = 5) -> str:
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(f"{API}/{task_id}", headers=HEADERS, timeout=30)
r.raise_for_status()
task = r.json()["data"]
status = task["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(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=300) 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 = submit_r2v(
prompt="Mantieni il design del soggetto in [Image 1], mentre cammina nell'atmosfera di [Image 2]",
reference_images=[
"https://static.hiapi.ai/example/char.jpg",
"https://static.hiapi.ai/example/mood.jpg",
],
duration=5,
resolution="720p",
aspect_ratio="9:16",
)
print("submitted", task_id)
video_url = wait_for_video(task_id)
print("ready", video_url)
download(video_url, "out.mp4")
Questo codice fa intenzionalmente quanto segue:
- Legge la chiave dalla variabile d'ambiente
HIAPI_API_KEY, così non finisce mai in git o nei log dei container. - Usa
r.raise_for_status()per intercettare gli errori di trasporto prima di analizzare il JSON. - Considera stati terminali solo
successefail: tutti gli altri stati (queued,processing, ecc.) fanno continuare il ciclo. - Scarica subito l'URL di output. Gli URL di output sono indirizzi firmati e scadono; se ti serve il file più tardi, trasferiscilo su uno storage persistente o copialo nel tuo bucket.
In produzione: passare dal polling al webhook
Il polling ogni 5 secondi va bene per un notebook o un worker a basso traffico. Per un servizio reale, registra un callback al momento della creazione dell'attività, così HiAPI invia gli eventi di stato terminale al tuo endpoint: i tuoi worker restano inattivi e non paghi più per richieste GET a vuoto. Per lo schema completo della richiesta consulta Creare un'attività, mentre per il polling di riserva consulta Ottenere i dettagli dell'attività.
payload = {
"model": "happyhorse-1.1/reference-to-video",
"input": {
"prompt": "Il personaggio in [Image 1] attraversa la strada cittadina di [Image 2]",
"reference_image": [
"https://cdn.example.com/char.jpg",
"https://cdn.example.com/street.jpg",
],
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 8,
},
"callback": {
"url": "https://your-domain.com/hiapi/callback",
"when": "final",
},
}
L'endpoint di ricezione deve gestire due aspetti:
- Idempotenza per
taskId. Siasuccesschefailsono stati terminali; i tentativi ripetuti di HiAPI (o i duplicati generati dal tuo livello di retry) consegnano lo stessotaskId. Deduplica prima di attivare qualsiasi effetto collaterale (copia su S3, messaggio Slack, evento di fatturazione). - Polling di verifica di riserva. Se il tuo endpoint è inattivo, il callback va perso. Mantieni un'attività di verifica leggera che chiami
GET /v1/tasks/<id>per tutte le attività in statoprocessingda più del tempo di generazione nel caso peggiore.
Errori che incontrerai realmente
Questi meritano una gestione esplicita; tutti gli altri sono 5xx temporanei, che l'SDK o il tuo client HTTP dovrebbero ritentare con backoff.
401 permission_denied: headerAuthorizationmancante, malformato o chiave revocata. Richiedi una nuova chiave dalla console invece di modificare l'header.400 INVALID_REQUESTcon percorso del campo: il messaggio di errore ti dice esattamente dov'è fallito, ad esempioreference_image: maxItems: got 10, want 9oppureaspect_ratio: value must be one of .... Convalida l'input prima che il worker invii la richiesta, per fallire rapidamente in locale.task failed(stato terminale): il polling/callback restituiscestatus: "fail"conerror.codeeerror.message. Cause comuni: URL di immagini di riferimento che il worker non riesce a recuperare, attivazione della moderazione NSFW o lato più corto dell'immagine inferiore alla soglia di 400px.
Domande frequenti
Si possono referenziare più di 9 immagini? No. Lo schema limita reference_image a un massimo di 9. Se ti servono più variazioni, componi fuori dalla piattaforma un'unica immagine di riferimento più densa (una griglia o uno storyboard) e usala come singolo input.
È obbligatorio usare i marcatori [Image N] nel prompt? Questi marcatori sono il modo in cui indichi immagini specifiche. Se il prompt non specifica mai le immagini è comunque valido, ma il modello mescolerà liberamente tutte le immagini di riferimento: per scenari con più inquadrature coerenti, di solito non è quello che vuoi.
Perché ricevo 503 nsfw_moderation_unavailable? È un errore temporaneo della piattaforma causato dal servizio di moderazione, indipendente dalla tua richiesta. Riprova dopo un breve backoff.
Meglio 720p o 1080p? 1080p è il valore predefinito e offre una qualità migliore, ma costa di più e richiede più tempo di rendering. Per clip verticali da social che i player riducono comunque di risoluzione, 720p con aspect_ratio: 9:16 è di solito un compromesso più adeguato.
Si può passare seed per rendere l'output riproducibile? Al momento no. L'API considera seed una proprietà sconosciuta e la rifiuta. Se ti serve riproducibilità, fissa esattamente prompt, reference_image, aspect_ratio, resolution e duration e accetta una certa variabilità tra un'esecuzione e l'altra.
Dove vengono conservati gli URL di output e per quanto tempo sono validi? Sono URL firmati con TTL breve; la risposta include expireAt. Dopo il successo dell'attività, scarica subito il file oppure trasferiscilo su uno storage persistente.
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