HiAPI
Blog

Tutorial

Cómo usar omnihuman-1.5 a través de la API de HiAPI: tutorial con curl y Python

Recorrido completo de la solicitud para la generación de videos de avatares digitales a partir de audio, sondeo asíncrono y descarga de resultados

Nov 3, 2026
深棕与燕麦色宽带交织的扁平编织纹理封面图,点缀克制黄铜色调,几何秩序感强

Qué hace la API de omnihuman-1.5 y para qué sirve

omnihuman-1.5 convierte un fragmento de audio más una imagen de retrato en un video de un avatar digital hablando: el rostro de la imagen se anima para que los movimientos de boca y cabeza sigan la voz proporcionada. En HiAPI, este modelo se ofrece como endpoint de audio a video: envías el audio, una imagen de referencia y unos pocos parámetros de generación, y la API devuelve una tarea que luego consultas repetidamente para obtener el video final.

En qué supera la API a las herramientas manuales

Las herramientas de navegador van bien para ediciones puntuales. Cuando la generación de video es una pieza dentro de un sistema mayor, la API es la opción correcta:

  • Pipelines por lotes: generar grandes cantidades de clips de avatares digitales a partir de colas de scripts y pistas de audio, sin intervención manual.
  • Funcionalidades de producto: narraciones dentro de una app, videos de onboarding localizados o mensajes personalizados, usando cada renderizado un audio distinto y un retrato distinto.
  • Automatización de backend: los flujos de trabajo de contenido generan video y este se entrega a un CMS, a un bucket de almacenamiento o a un servicio de distribución.

Forma de la estructura de la solicitud

Cada llamada a omnihuman-1.5 en HiAPI sigue la misma forma: un POST envía la tarea y las solicitudes posteriores comprueban su estado. Como el renderizado no es instantáneo, el patrón de integración es enviar → sondear → descargar, y no enviar → recibir el video en la misma respuesta. El resto de este tutorial construye ese flujo primero con curl y luego lo implementa en Python.

Requisitos previos y tus credenciales de HiAPI

Antes de la primera llamada, confirma tres cosas.

1. Cuenta y clave de API

Necesitas una cuenta de HiAPI y una clave de API creada en la consola. Trata la clave como un secreto: no la pongas en el control de versiones, en el código del cliente ni en los logs. En desarrollo, cárgala desde una variable de entorno; en producción, desde el gestor de secretos de tu plataforma.

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

2. Acceso a la red y herramientas de solicitud

Con curl basta cualquier shell moderno. Para la versión en Python solo necesitas la librería requests: no hace falta un SDK específico de un proveedor, porque la API es HTTPS normal con cuerpo JSON.

pip install requests

3. Entradas que requiere el modelo

Prepara lo siguiente antes de escribir la solicitud:

  • Audio: la pista de voz que impulsa la animación de la boca, proporcionada como URL que la API pueda leer o como referencia a un archivo ya subido, según el formato de entrada que acepte el endpoint.
  • Imagen de retrato: el rostro que se va a animar. Una foto nítida, frontal y con la boca visible produce los resultados más predecibles.
  • Parámetros de generación: cualquier opción de resolución, duración o calidad que exponga el modelo. Envía solo los campos que realmente necesites; en las primeras pruebas los valores por defecto suelen ser razonables.

Cada solicitud pasa la clave en la cabecera Authorization con la forma Bearer <your-key>. Si la primera llamada devuelve 401, revisa primero esta cabecera.

Hacer la solicitud con curl

La llamada de envío es un POST con cuerpo JSON. La ruta exacta y los nombres de campo de omnihuman-1.5 figuran en la página de referencia de la API de ese modelo; la estructura siguiente muestra la forma de la llamada y los campos que vas a rellenar.

curl -X POST "https://api.hiapi.ai/v1/omnihuman-1.5/generate" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "audio_url": "https://your-bucket.example.com/speech.mp3",
    "image_url": "https://your-bucket.example.com/portrait.jpg"
  }'

Explicación campo por campo

  • Authorization: lleva la clave de API. Sin ella, la solicitud se rechaza antes de empezar cualquier trabajo.
  • Content-Type: application/json: el cuerpo es JSON; omitir esta cabecera es una causa habitual de errores de validación confusos.
  • audio_url: la pista de voz. Usa una URL de lectura pública o accesible de otro modo por la API, y prioriza ubicaciones estables frente a enlaces firmados que caducan a mitad del renderizado.
  • image_url: el retrato que se va a animar. Se aplican las mismas reglas de accesibilidad.

Qué se devuelve

Un envío correcto no devuelve el video, sino un objeto de tarea que contiene un identificador (normalmente el campo id) y un estado como queued o processing. Guarda ese identificador; es el asa que usarás en cada comprobación de estado posterior.

{
  "id": "job_abc123",
  "status": "queued"
}

Puedes confirmar que el envío se realizó comprobando el código de estado HTTP y la presencia del identificador de tarea. Si la respuesta contiene mensajes de validación, apuntarán a los campos erróneos, normalmente URLs ausentes o mal formadas.

Implementar la misma solicitud en Python

La llamada curl se traduce directamente a Python. La versión siguiente está pensada para colocarla tal cual en un servicio: la clave viene de una variable de entorno, el payload es un diccionario que se construye dinámicamente y los errores se lanzan en lugar de silenciarse.

import os
import requests

API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = "https://api.hiapi.ai/v1"

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

def submit_omnihuman_job(audio_url: str, image_url: str, **options) -> str:
    payload = {
        "audio_url": audio_url,
        "image_url": image_url,
    }
    payload.update(options)

    response = requests.post(
        f"{BASE_URL}/omnihuman-1.5/generate",
        headers=HEADERS,
        json=payload,
        timeout=60,
    )
    response.raise_for_status()

    data = response.json()
    return data["id"]

Por qué está escrita así

  • json=payload hace que requests serialice el cuerpo y establezca el tipo de contenido, así no olvidas la cabecera JSON.
  • **options mantiene la función flexible: los parámetros de generación se pasan como argumentos con nombre sin tener que modificar el literal del payload.
  • timeout=60 evita que una conexión colgada bloquee indefinidamente el proceso de trabajo. El envío es una llamada rápida; las esperas largas corresponden al paso de sondeo, no aquí.
  • raise_for_status() convierte las respuestas no 2xx en excepciones que puedes capturar y registrar, y permite obtener el cuerpo de la respuesta para el diagnóstico.

Conserva el identificador de tarea devuelto y pásalo a la función auxiliar de sondeo; esa función se construye en la sección siguiente.

Gestionar el ciclo de vida asíncrono: sondear hasta completar

La generación de video lleva tiempo, así que la API es asíncrona por diseño. Tras el envío, consultas repetidamente el estado de la tarea hasta que alcanza un estado terminal.

Bucle de sondeo

import time

def wait_for_job(job_id: str, interval: int = 5, max_wait: int = 600) -> dict:
    waited = 0
    while waited < max_wait:
        response = requests.get(
            f"{BASE_URL}/omnihuman-1.5/jobs/{job_id}",
            headers=HEADERS,
            timeout=30,
        )
        response.raise_for_status()
        job = response.json()
        status = job["status"]

        if status == "succeeded" or status == "completed":
            return job
        if status == "failed" or status == "error":
            raise RuntimeError(f"Job {job_id} failed: {job}")

        time.sleep(interval)
        waited += interval

    raise TimeoutError(f"Job {job_id} did not finish within {max_wait}s")

Decidir cuándo detenerse

Tres resultados terminan el bucle y tu código debe manejar cada uno:

  • Éxito: la tarea reporta un estado de finalización e incluye una referencia al video de salida. Sal del bucle y pasa a la descarga.
  • Fallo: la tarea reporta un estado de error. No reintentes a ciegas; revisa primero el mensaje de error, porque los fallos suelen deberse a URLs de entrada ilegibles o audio no admitido.
  • Tiempo de espera agotado: se alcanza tu propio límite de tiempo real. Trátalo como algo distinto de un fallo: la tarea podría completarse aún en el servidor, así que decide de forma deliberada si seguir sondeando, ampliar la ventana de tiempo o abandonar.

Etiqueta del sondeo

Usa un intervalo moderado en lugar de un bucle apretado. Para el renderizado de video, unos segundos entre comprobaciones es suficiente y mantiene el volumen de solicitudes predecible. Si prevés colas largas, aplica un retroceso progresivo y establece siempre un límite para que un proceso de trabajo atascado no sondee para siempre. En producción, prefiere procesos de trabajo en segundo plano o colas de tareas a sondear dentro del manejador de una solicitud web.

Descargar y almacenar el video de salida

Cuando el sondeo devuelve una tarea completada, la respuesta incluye una referencia al video, normalmente un campo de URL dentro del objeto de resultado. Recupéralo y guárdalo en tu propio almacenamiento, en lugar de depender de ese enlace a largo plazo.

def download_video(job: dict, destination: str) -> None:
    video_url = job.get("output_url") or job.get("result", {}).get("video_url")
    if not video_url:
        raise ValueError(f"No video reference in job payload: {job}")

    with requests.get(video_url, stream=True, timeout=120) as response:
        response.raise_for_status()
        with open(destination, "wb") as file:
            for chunk in response.iter_content(chunk_size=8192):
                file.write(chunk)

Precauciones que ahorran tiempo de depuración

  • Descarga en streaming. Los videos son grandes; escribir por bloques evita cargar el archivo entero en memoria, algo importante en procesos de trabajo con memoria limitada.
  • Usa nombres de archivo deterministas. Derívalos del identificador de tarea más una marca de tiempo, para que las ejecuciones sean rastreables y las repeticiones no colisionen.
  • Descarga primero a un archivo temporal y luego muévelo. Escribe en una ruta temporal y renombra al terminar, para que una descarga parcial nunca se confunda con un archivo completo.
  • Trata la URL devuelta como temporal. Copia los bytes en tu propio bucket o sistema de archivos y registra el identificador de tarea junto al objeto almacenado para localizarlo después.
  • Verifica antes de declarar el éxito. Antes de marcar la tarea como completada en la base de datos, comprueba que el archivo descargado no está vacío y tiene un tamaño razonable.

Solucionar fallos habituales de solicitud y sondeo

La mayoría de los problemas de integración caen en unas pocas categorías. Revísalas en orden.

Errores de autenticación y solicitud

  • 401 Unauthorized: la clave falta, está mal formada o no se envía como token Bearer. Revisa con cuidado la cabecera, incluido el espacio después de Bearer.
  • 403: la clave es válida pero no tiene permiso para este endpoint; confirma que la clave pertenece al proyecto que esperas.
  • Errores de validación 422 / 400: lee el mensaje. Las causas habituales incluyen falta de audio_url, falta de image_url, URL ilegible o formato de audio no admitido.
  • HTML inesperado en la respuesta: has accedido a un host o ruta incorrectos. Verifica la URL base en la referencia de la API en lugar de reconstruirla de memoria.

La tarea falla o nunca termina

  • La API no puede acceder a la URL de entrada: un enlace que funciona en el navegador puede estar bloqueado por una red privada, una firma caducada o protección contra enlaces desde otros dominios. Prueba primero con una URL pública estable.
  • El audio y la imagen no encajan: pistas silenciosas, extremadamente largas o en formatos no admitidos pueden estancar o hacer fallar el renderizado. Valida el pipeline primero con un fragmento corto en formato estándar.
  • Sondeo demasiado frecuente: un bucle apretado puede activar límites de tasa y producir errores que parecen fallos de la tarea. Baja el ritmo y añade retroceso.
  • Tiempo de sondeo demasiado corto: abandonar unos segundos después con una tarea en cola produce tiempos de espera agotados falsos. Fija un tiempo máximo de espera realista y registra el estado visto antes de detenerte.
  • Estados terminales no gestionados: si el bucle solo comprueba el éxito, los fallos se quedarán girando hasta el tiempo de espera. Ramifica explícitamente para los estados de fallo.

Hábito de validación mínima

Cuando algo falla, reduce a la prueba mínima: un fragmento de audio corto, un retrato pequeño, un envío y una comprobación de estado unos segundos después. Si eso funciona, el problema está en tu payload, en la lógica de sondeo o en el alojamiento de las entradas, no en la llamada a la API en sí.

Artículos relacionados

Ver todos los artículos →

Empieza a crear con artefactos de IA persistentes

Consigue tu API key y llama a modelos de imagen, vídeo y audio a través de una única API basada en tasks.

Leer la documentación de la API