HiAPI
Blog

Tutorial

Cómo usar la API de imagen a vídeo de hailuo 2.3 Fast: curl, Python y una solicitud ejecutable

Jul 10, 2026
Cómo usar la API de imagen a vídeo de hailuo 2.3 Fast: curl, Python y una solicitud ejecutable

Tienes una imagen estática y quieres convertirla en un vídeo corto. Esta guía te muestra cómo integrarte con hailuo-2.3-fast/image-to-video a través de la API de tareas de hiapi: el esquema de entrada exacto (verificado contra el validador en línea), una solicitud curl lista para copiar, un script completo de Python que sondea hasta que la tarea finaliza, y detalles de producción — callbacks, reintentos idempotentes y las respuestas de error que realmente encontrarás.

Lo que necesitas

  • Una clave de API de hiapi — obtenla desde tu consola. La clave tiene la forma sk-... y se envía en el encabezado Authorization: Bearer.
  • Una imagen de origen alojada en una URL HTTPS accesible públicamente. La API recibe una URL, no un archivo subido.
  • Cualquier cliente HTTP. Los ejemplos siguientes usan curl y requests de Python.

El precio por segundo de este modelo está publicado en la página de precios.

Esquema de entrada (verificado)

hailuo-2.3-fast/image-to-video usa el endpoint unificado de tareas asíncronas: POST /v1/tasks crea una tarea y GET /v1/tasks/<taskId> la sondea. El esquema de entrada es estricto — los campos desconocidos se rechazan con un 400 en lugar de ignorarse silenciosamente.

Campo Tipo Obligatorio Descripción
prompt string ✅ Describe el movimiento que deseas
image_url string ✅ Forma singular — una cadena URL, no un arreglo image_urls
duration string Opcional "6" o "10" — es una cadena, no un número
prompt_optimizer boolean Opcional Deja que el backend reescriba tu prompt para el movimiento

El validador detecta tres trampas:

  1. duration debe ser una cadena. Enviar "duration": 6 devuelve 400 con el mensaje duration: got number, want string. Envía "duration": "6".
  2. El campo es image_url en singular. Muchos otros modelos de vídeo de la plataforma (Grok Imagine, Kling) reciben un arreglo image_urls. Este modelo recibe una sola cadena.
  3. No hay resolution, aspect_ratio, seed, watermark ni end_image_url. Todos ellos devuelven additional properties ... not allowed. La relación de aspecto de salida sigue a tu imagen de origen; el control del movimiento solo se realiza mediante el prompt.

Solicitud mínima ejecutable: curl

curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "hailuo-2.3-fast/image-to-video",
    "input": {
      "prompt": "La cámara se acerca lentamente, el vapor se eleva sobre la taza de café, luz suave de la mañana",
      "image_url": "https://your-cdn.example.com/coffee.jpg",
      "duration": "6"
    }
  }'

Respuesta exitosa:

{
  "code": 200,
  "data": { "taskId": "tk-hiapi-01KX4W3SVW8S8EHD5VX45JH95V" },
  "message": "success"
}

Luego sondea:

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

Mientras el vídeo se renderiza, data.status es "handling":

{
  "code": 200,
  "data": {
    "taskId": "tk-hiapi-01KX4W3SVW8S8EHD5VX45JH95V",
    "model": "hailuo-2.3-fast/image-to-video",
    "status": "handling",
    "storage": "temp",
    "completed": 0,
    "created": 1783648872
  },
  "message": "success"
}

Los estados terminales son "success" (la URL del vídeo está en data.output[0].url) y "fail" (los detalles están en data.error).

Script completo de Python

import os
import time

import requests

API_BASE = "https://api.hiapi.ai/v1/tasks"
TOKEN = os.environ["HIAPI_API_KEY"]  # sk-...
HEADERS = {"Authorization": f"Bearer {TOKEN}"}


def create_task(prompt: str, image_url: str, duration: str = "6") -> str:
    resp = requests.post(
        API_BASE,
        headers={**HEADERS, "Content-Type": "application/json"},
        json={
            "model": "hailuo-2.3-fast/image-to-video",
            "input": {
                "prompt": prompt,
                "image_url": image_url,
                "duration": duration,  # cadena: "6" o "10"
            },
        },
        timeout=60,
    )
    body = resp.json()
    if resp.status_code != 200 or not body.get("data", {}).get("taskId"):
        raise RuntimeError(f"create failed: {resp.status_code} {body}")
    return body["data"]["taskId"]


def wait_task(task_id: str, timeout_s: int = 600, poll_s: int = 5) -> dict:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        task = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS, timeout=30).json()["data"]
        status = task.get("status")
        if status == "success":
            return task
        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} still running after {timeout_s}s")


if __name__ == "__main__":
    task_id = create_task(
        prompt="El gato parpadea, gira la cabeza hacia la ventana, una brisa le roza el pelaje",
        image_url="https://your-cdn.example.com/cat.jpg",
        duration="6",
    )
    print("task:", task_id)

    task = wait_task(task_id)
    video_url = task["output"][0]["url"]
    print("video:", video_url)

    # La URL está en almacenamiento temporal con caducidad; descárgala de inmediato, no la enlaces directamente.
    mp4 = requests.get(video_url, timeout=120).content
    with open("output.mp4", "wb") as f:
        f.write(mp4)
    print(f"saved output.mp4 ({len(mp4)} bytes)")

Este script hace dos cosas correctamente que las soluciones improvisadas suelen pasar por alto:

  • Descarga el vídeo de inmediato. storage: "temp" significa que la URL de salida tiene caducidad (expireAt). Persiste los bytes en tu propio almacenamiento; nunca guardes en tu base de datos la URL devuelta.
  • Trata fail como terminal. Volver a sondear una tarea fallida no la reanima — crea una tarea nueva.

Consideraciones para producción

Usa callbacks en lugar de sondeo

Para casos de uso fuera de scripts, registra un webhook al crear la tarea y omite por completo el bucle de sondeo:

{
  "model": "hailuo-2.3-fast/image-to-video",
  "input": { "prompt": "...", "image_url": "https://...", "duration": "6" },
  "callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}

Con "when": "final", tu endpoint se invoca una vez cuando la tarea alcanza success o fail. Regla general: el sondeo va bien para CLI, tareas por lotes y cualquier escenario transitorio; una vez que tienes un servidor ya escuchando, los callbacks son superiores — las tareas de vídeo tardan minutos, y un bucle de sondeo durante esos minutos es una conexión ocupada y un worker desperdiciado.

Idempotencia

La llamada de creación no es idempotente — cada POST /v1/tasks que pasa la validación crea una nueva tarea (facturada). Si tu ejecutor de tareas reintenta tras un timeout, almacena el taskId obtenido con tu propio id de solicitud como clave antes de procesar el resultado. Al reintentar, si esa clave ya tiene un taskId, sondea ese en lugar de crear una tarea duplicada.

Errores que realmente encontrarás

Síntoma Significado Solución
401 — {"error":{"code":"permission_denied","type":"hiapi_error"}} Clave inválida o la clave no puede usar este modelo Revisa la clave en la consola; la respuesta incluye un request_id para soporte
400 INVALID_REQUEST — duration: got number, want string Se usó un número JSON en lugar de una cadena "duration": "6"
400 INVALID_REQUEST — additional properties '...' not allowed Enviaste un campo fuera del esquema Quítalo — el mensaje indica cada campo infractor
400 INVALID_REQUEST — missing required field "prompt" / "image_url" Entrada vacía o nombre de campo incorrecto Ambos campos son obligatorios
La tarea llega a fail después de crearse Fallo en tiempo de ejecución — causa común: una URL de imagen que el backend no puede obtener El validador no obtiene tu imagen al crear; asegúrate de que la URL sea accesible públicamente

Vale la pena enfatizar la última fila: la validación al crear solo comprueba el esquema. Un image_url mal escrito o privado te da un taskId que parece normal y una tarea que muere unos minutos después — así que alerta sobre fail, no solo sobre errores HTTP.

Lecturas relacionadas

Preguntas frecuentes

¿Puedo establecer la resolución o la relación de aspecto? No. El esquema de hailuo-2.3-fast/image-to-video rechaza resolution y aspect_ratio como campos desconocidos. La relación de aspecto sigue a tu imagen de origen; si necesitas una relación específica, recorta primero la imagen de entrada.

¿Qué duraciones se admiten? Exactamente dos: "6" y "10" segundos, pasadas como cadenas. Cualquier otro valor devuelve 400 con el mensaje value must be one of '6', '10'.

¿Puedo pasar varias imágenes de referencia? No — el campo es una sola cadena image_url. Los modelos que aceptan arreglos de imágenes (por ejemplo, image_urls de Grok Imagine) usan un esquema distinto; no copies el cuerpo de la solicitud entre modelos sin revisarlo.

¿Cómo controlo el movimiento? Solo mediante el prompt — no hay parámetros de intensidad de movimiento ni de cámara. Describe el movimiento de forma explícita ("la cámara se acerca lentamente", "el cabello se mece con el viento"). Opcionalmente, establece "prompt_optimizer": true para que el backend amplíe tu prompt.

¿Hay una variante más lenta y de mayor calidad? Sí — hailuo-2.3/image-to-video (sin -fast) también está disponible en la plataforma, y hailuo-2.3/text-to-video cubre escenarios sin imagen de origen. Revisa el esquema de cada variante antes de cambiar; los campos de entrada no están garantizados como idénticos.

¿Cuánto cuesta? La facturación se calcula por los segundos del vídeo generado y varía según el modelo; consulta las tarifas actuales en la página de precios.

¿Necesito una clave de API para probarlo? Sí — todas las llamadas a la API de tareas requieren una clave Bearer sk-.... Puedes crear una en la consola y probarla primero en el playground de la página del modelo.

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