HiAPI
Blog

Tutoriale

Come usare omnihuman-1.5 tramite l'API hiapi: curl, Python e richieste eseguibili immediatamente

Oct 14, 2026
Come usare omnihuman-1.5 tramite l'API hiapi: curl, Python e richieste eseguibili immediatamente

omnihuman-1.5 combina una foto ritratto con una traccia audio per generare un video parlante con sincronizzazione labiale. Su hiapi, come tutti gli altri modelli media, funziona sullo stesso protocollo di attività asincrone: prima si chiama una volta POST /v1/tasks per creare l'attività, poi si esegue il polling (o si riceve un webhook) finché l'URL del video non è pronto. Questa guida usa lo schema di richiesta reale — verificato campo per campo con l'API live — quindi il codice curl e Python qui sotto è direttamente copiabile ed eseguibile, non solo esemplificativo.

Cosa costruirai

Uno script che invia un'immagine di volto e una traccia vocale a omnihuman-1.5 e restituisce un file MP4 in cui il volto parla con sincronizzazione labiale seguendo l'audio. La durata del clip segue il tuo audio — non esiste un campo duration da impostare.

Prerequisiti

  1. Una API key di hiapi. Creane una nella dashboard; viene inviata come Bearer token a ogni richiesta. Per il formato dell'header consulta la documentazione sull'autenticazione.
  2. Un URL immagine e un URL audio accessibili pubblicamente. omnihuman-1.5 scarica entrambi lato server, quindi va bene qualsiasi URL http(s) apribile in modo anonimo dalla tua macchina; percorsi localhost e URL firmati che scadono durante l'attività non vanno bene.

Tutto qui — nessun SDK necessario, nessun account oltre alla key.

Schema di input

omnihuman-1.5 accetta un oggetto input rigoroso. I campi sconosciuti vengono rifiutati, quindi questi sono tutti i campi disponibili:

Campo Tipo Obbligatorio Descrizione
image_url string Sì URL di un ritratto. Un singolo volto nitido e frontale dà i risultati migliori.
audio_url string Sì Traccia vocale per la sincronizzazione labiale. Determina la durata dell'output.
prompt string No Guida opzionale per animazione/espressione.
resolution string No "720p" o "1080p".

Non esiste una modalità solo testo: audio_url è obbligatorio. Se hai solo uno script, genera prima la voce (con un modello TTS, o con un avatar digitale guidato da testo come heygen-avatar-v), poi passa qui l'URL audio generato.

Esempio minimo eseguibile (curl)

Crea l'attività:

curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "omnihuman-1.5",
    "input": {
      "image_url": "https://example.com/portrait.jpg",
      "audio_url": "https://example.com/voiceover.mp3",
      "resolution": "720p"
    }
  }'

La risposta contiene l'id dell'attività:

{ "code": 200, "data": { "taskId": "b3f1c2e8-...", "status": "pending" } }

Esegui il polling dell'attività finché non raggiunge uno stato terminale:

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

Al completamento, data.status è "success" e il video si trova in data.output[0].url:

{
  "code": 200,
  "data": {
    "taskId": "b3f1c2e8-...",
    "status": "success",
    "output": [
      { "url": "https://temp.hiapi.ai/.../omnihuman.mp4", "expireAt": 1760000000 }
    ]
  }
}

L'URL di output è temporaneo (con expireAt). Scarica subito l'MP4 dopo il successo dell'attività — non collegare direttamente quell'indirizzo in produzione.

La stessa chiamata in Python

import time
import requests  # pip install requests

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


def create_task() -> str:
    payload = {
        "model": "omnihuman-1.5",
        "input": {
            "image_url": "https://example.com/portrait.jpg",
            "audio_url": "https://example.com/voiceover.mp3",
            "resolution": "720p",
        },
    }
    r = requests.post(API, headers=HEADERS, json=payload, timeout=60)
    r.raise_for_status()
    return r.json()["data"]["taskId"]


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


if __name__ == "__main__":
    tid = create_task()
    print("task:", tid)
    video_url = wait_for_video(tid)
    # URLs expire — persist the bytes immediately.
    mp4 = requests.get(video_url, timeout=120).content
    with open("talking_head.mp4", "wb") as f:
        f.write(mp4)
    print("saved talking_head.mp4")

Passare in produzione

Preferisci i callback al polling intensivo. Aggiungi un oggetto di primo livello callback e hiapi invierà via POST l'attività completata al tuo endpoint, invece di farti eseguire il polling:

{
  "model": "omnihuman-1.5",
  "input": {
    "image_url": "https://example.com/portrait.jpg",
    "audio_url": "https://example.com/voiceover.mp3",
    "resolution": "1080p"
  },
  "callback": { "url": "https://your.app/webhooks/hiapi", "when": "final" }
}

when accetta solo "final" e si attiva sia in caso di successo sia di errore — quindi il tuo webhook deve gestire i casi in base a data.status. Il body consegnato è lo stesso oggetto ottenuto con GET /v1/tasks/{id}. La consegna è at-least-once, quindi deduplica per taskId.

Non reinviare mai in caso di errori temporanei. Se una richiesta di polling va in timeout o la connessione cade, esegui di nuovo il polling dello stesso taskId — non chiamare di nuovo POST /v1/tasks. Un reinvio avvia una seconda attività fatturata. Salva il taskId immediatamente dopo averlo ottenuto.

Gestisci separatamente le tre forme di errore:

  • Errore di autenticazione → HTTP 401, body {"error": {"code": "permission_denied", "type": "hiapi_error", ...}}. Correggi la key; non riprovare.
  • Richiesta errata (campi mancanti/non validi) → HTTP 400, body {"code": 400, "error_code": "INVALID_REQUEST", "message": "..."}. Il message indica il campo problematico — correggi e reinvia.
  • Attività avviata ma fallita → HTTP 200, con data.status == "fail" e data.error. La causa più comune è che il server non riesce a scaricare image_url/audio_url. Verifica che entrambi siano accessibili pubblicamente e ancora validi. Gestisci sempre i casi in base a data.status, non solo al codice di stato HTTP.

Il prezzo è calcolato sui secondi di output, elencato nella pagina dei prezzi; poiché la durata del clip segue il tuo audio, il costo varia con la durata dell'audio.

Guide correlate

Domande frequenti

Posso guidare omnihuman-1.5 con testo invece che con un file audio? No. audio_url è obbligatorio e il modello sincronizza le labbra su quell'audio. Se ti serve un flusso da script a video, sintetizza prima la voce (qualsiasi TTS), oppure usa un avatar digitale guidato da testo come heygen-avatar-v, poi passa qui l'URL audio.

Quanto dura il video di output? Segue la tua traccia audio — non c'è un parametro duration. Una traccia vocale di 12 secondi genera circa 12 secondi di video.

Che tipo di immagine funziona meglio? Un ritratto singolo, frontale e ben illuminato, su un URL pubblico. Foto di gruppo o volti molto occlusi producono una sincronizzazione labiale peggiore.

720p o 1080p? Entrambi sono disponibili per resolution. Inizia con 720p durante le iterazioni; passa a 1080p per il rendering finale. La risoluzione più alta costa di più al secondo — vedi pricing.

La mia attività restituisce subito status: "fail". Perché? Quasi sempre il server non riesce a recuperare un URL di input. Verifica che image_url e audio_url siano accessibili pubblicamente (aprendoli in una finestra in incognito) e non scaduti. Il messaggio di data.error aiuta a restringere il problema.

Devo fare polling o posso usare un webhook? Entrambi vanno bene. Per script semplici puoi fare polling con GET /v1/tasks/{id}; in produzione puoi registrare callback.url e impostare when su "final", così non devi mantenere un ciclo di polling.

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