Tutoriel
Comment utiliser omnihuman-1.5 via l'API hiapi : curl, Python et requêtes directement exécutables
omnihuman-1.5 transforme une photo de portrait et une piste audio en une vidéo parlante à synchronisation labiale. Sur hiapi, comme tous les autres modèles média, il fonctionne sur le même protocole de tâche asynchrone : appelez d'abord POST /v1/tasks pour créer une tâche, puis interrogez (ou recevez un webhook) jusqu'à ce que l'URL de la vidéo soit prête. Ce guide utilise le schéma de requête réel — vérifié champ par champ avec l'API en production — afin que le code curl et Python ci-dessous puisse être copié et exécuté tel quel, et non comme simple exemple.
Ce que vous allez construire
Un script qui envoie une image de visage et une piste vocale à omnihuman-1.5 et renvoie un fichier MP4 où ce visage parle en synchronisation labiale avec l'audio. La durée du clip suit votre audio — il n'y a pas de champ duration à définir.
Prérequis
- Une clé API hiapi. Créez-en une dans le dashboard ; elle est envoyée comme Bearer token à chaque requête. Consultez la documentation d'authentification pour le format de l'en-tête.
- Une URL d'image et une URL audio accessibles publiquement. omnihuman-1.5 les télécharge côté serveur, donc une URL
http(s)que votre machine peut ouvrir anonymement suffit ; les cheminslocalhostet les URL signées qui expirent en cours de tâche ne fonctionnent pas.
C'est tout — pas de SDK, aucun compte nécessaire en dehors de la clé.
Schéma d'entrée
omnihuman-1.5 accepte un objet input strict. Les champs inconnus sont rejetés, voici donc l'ensemble des champs disponibles :
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
image_url |
string | Oui | URL d'un portrait. Un visage unique, net et de face donne les meilleurs résultats. |
audio_url |
string | Oui | Piste vocale utilisée pour la synchronisation labiale. Détermine la durée de sortie. |
prompt |
string | Non | Guidage optionnel pour l'animation/les expressions. |
resolution |
string | Non | "720p" ou "1080p". |
Il n'y a pas de mode texte seul : audio_url est obligatoire. Si vous n'avez qu'un script, générez d'abord la voix (avec un modèle TTS, ou un avatar numérique piloté par texte comme heygen-avatar-v), puis passez l'URL audio générée ici.
Exemple minimal exécutable (curl)
Créer la tâche :
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 réponse contient l'id de tâche :
{ "code": 200, "data": { "taskId": "b3f1c2e8-...", "status": "pending" } }
Interrogez la tâche jusqu'à ce qu'elle atteigne un état terminal :
curl -s https://api.hiapi.ai/v1/tasks/b3f1c2e8-... \
-H "Authorization: Bearer sk-YOUR_KEY"
Une fois terminée, data.status vaut "success" et la vidéo se trouve dans data.output[0].url :
{
"code": 200,
"data": {
"taskId": "b3f1c2e8-...",
"status": "success",
"output": [
{ "url": "https://temp.hiapi.ai/.../omnihuman.mp4", "expireAt": 1760000000 }
]
}
}
Cette URL output est temporaire (elle porte un expireAt). Téléchargez le MP4 dès que la tâche réussit — ne faites pas de hotlink vers cette adresse en production.
Le même appel en 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")
Passer en production
Privilégiez les callbacks plutôt qu'un polling intensif. Ajoutez un objet callback au niveau racine et hiapi enverra en POST la tâche terminée à votre endpoint, au lieu de vous laisser interroger :
{
"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 n'accepte que "final", et se déclenche en cas de succès et d'échec — votre webhook doit donc brancher selon data.status. Le corps livré est le même objet que celui obtenu via GET /v1/tasks/{id}. La livraison est de type au moins une fois, dédupliquez donc par taskId.
Ne resoumettez jamais sur une erreur temporaire. Si une requête de polling expire ou si la connexion est interrompue, interrogez à nouveau le même taskId — n'appelez pas POST /v1/tasks une seconde fois. Une resoumission démarre une deuxième tâche facturée. Enregistrez le taskId dès que vous l'obtenez.
Traitez séparément les trois formes d'échec :
- Échec d'authentification → HTTP
401, body{"error": {"code": "permission_denied", "type": "hiapi_error", ...}}. Corrigez la clé ; ne réessayez pas. - Requête invalide (champ manquant/invalide) → HTTP
400, body{"code": 400, "error_code": "INVALID_REQUEST", "message": "..."}. Le message indique le champ problématique — corrigez puis renvoyez. - Tâche démarrée mais échouée → HTTP
200, avecdata.status == "fail"etdata.error. La cause la plus fréquente est que le serveur ne peut pas téléchargerimage_url/audio_url. Vérifiez que les deux sont accessibles publiquement et toujours valides. Branchez toujours surdata.status, pas seulement sur le code de statut HTTP.
Le prix est facturé à la seconde de sortie et indiqué sur la page de tarifs ; comme la durée du clip suit votre audio, le coût varie selon la durée de l'audio.
Guides associés
- Présentation de l'API asynchrone unifiée — le contrat de création et de polling
/v1/taskspartagé par tous les modèles. - Démarrage rapide — réalisez votre première requête hiapi de bout en bout.
- Tutoriel de l'API HeyGen Avatar IV — un autre modèle photo-vers-vidéo parlante, utile pour comparer les approches de synchronisation labiale.
- HeyGen Avatar V — un avatar numérique piloté par script (texte-vers-vidéo) lorsque vous n'avez pas de fichier audio.
FAQ
Puis-je piloter omnihuman-1.5 avec du texte plutôt qu'un fichier audio ?
Non. audio_url est obligatoire et le modèle synchronise les lèvres sur cet audio. Pour un flux script-vers-vidéo, synthétisez d'abord la voix (n'importe quel TTS), ou utilisez un avatar numérique piloté par texte comme heygen-avatar-v, puis passez l'URL audio ici.
Quelle est la durée de la vidéo de sortie ?
Elle suit votre piste audio — il n'y a pas de paramètre duration. Une piste vocale de 12 secondes produit environ 12 secondes de vidéo.
Quelle image donne les meilleurs résultats ? Un portrait unique, de face, avec un éclairage net, hébergé sur une URL publique. Les photos de groupe ou les visages fortement masqués donnent une synchronisation labiale moins bonne.
720p ou 1080p ?
Les deux sont acceptés dans resolution. Commencez par 720p pendant les itérations ; passez à 1080p pour le rendu final. Une résolution plus élevée coûte plus cher par seconde — voir pricing.
Ma tâche renvoie immédiatement status: "fail". Pourquoi ?
Presque toujours parce que le serveur ne parvient pas à récupérer une URL d'entrée. Vérifiez que image_url et audio_url sont accessibles publiquement (ouvrez-les dans une fenêtre de navigation privée) et non expirées. Le message data.error aide à cerner le problème.
Dois-je faire du polling, ou puis-je utiliser un webhook ?
Les deux sont possibles. Un script simple peut interroger via GET /v1/tasks/{id} ; en production, enregistrez callback.url avec when défini à "final" pour éviter de maintenir une boucle de polling.
Articles similaires
Voir tous les articles →-
Tutoriel
Comment appeler hailuo-2.3-fast/image-to-video via l'API HiAPI : curl et Python en pratique
La première requête exploitable du palier image-vers-vidéo rapide et économique — de l'ID du modèle au téléchargement du MP4
-
Tutoriel
Comment appeler hailuo-2.3/text-to-video via l'API HiAPI : curl, Python et exemples de requêtes exécutables
-
Tutoriel
Utiliser Kandinsky 6.0 Pro texte-vers-vidéo sur HiAPI : tutoriel curl et Python
Démonstration pratique axée sur la requête : construire l'appel du prompt au clip vidéo, soumettre la requête, interroger l'état d'achèvement et télécharger le résultat