HiAPI
Blog

Tutorial

Cómo llamar a veo-3.1-fast/image-to-video mediante la API de HiAPI (curl + Python)

Una demostración de solicitud ejecutable: entrada de primer fotograma, audio nativo, sondeo asíncrono y descarga del resultado

Nov 3, 2026
深靛蓝底色上三层嵌套的扁平折线箭头依次向右推进,前缘为杏橙色,像信号流程图,是教程文章的封面图。

Qué construye este tutorial y qué necesitas preparar primero

Este tutorial demuestra de principio a fin una integración ejecutable de HiAPI usando veo-3.1-fast/image-to-video: envías una imagen de primer fotograma y un prompt, recibes una tarea asíncrona, sondeas hasta que se completa y luego descargas el vídeo generado junto con su audio nativo.

Al final del tutorial tendrás dos implementaciones equivalentes: un comando curl que puedes ejecutar en la terminal y un cliente Python que puedes colocar en un script o servicio; ambos acceden al mismo endpoint de HiAPI.

Requisitos previos

Antes de comenzar, prepara lo siguiente:

  • Una clave de API de HiAPI. No la incluyas en el control de versiones. En los ejemplos siguientes se lee desde una variable de entorno, en lugar de pegarla directamente en línea.
  • La URL base de HiAPI. Usa la dirección que aparece en tu panel de HiAPI o en la referencia de la API; guárdala como variable para configurarla una sola vez.
  • Una imagen de primer fotograma. Una URL de acceso público es la entrada más sencilla. Usa una imagen real sobre la que tengas derechos, en un formato común como JPEG o PNG.
  • Un cliente HTTP. Para esta demostración, curl es suficiente. Para la versión en Python solo necesitas requests, sin SDK de proveedor.
  • Un directorio local para almacenar el vídeo descargado.

Configura tu entorno

Exporta la clave y la URL base para que los ejemplos de shell y Python puedan reutilizarlas:

export HIAPI_API_KEY="your-api-key"
export HIAPI_BASE_URL="https://api.hiapi.ai"
export FIRST_FRAME_URL="https://example.com/your-first-frame.jpg"

La ruta exacta del endpoint y los nombres de parámetros de veo-3.1-fast/image-to-video se documentan junto a la entrada del modelo en la referencia de la API de HiAPI; consulta allí la lista canónica de campos en lugar de fiarte de fragmentos copiados de otro lugar. Este tutorial se centra en la estructura de la solicitud y el flujo de trabajo, que se mantienen aunque los detalles de los campos deban confirmarse en la documentación.

Vale la pena recordar ahora un punto: este es un modelo basado en tareas, no síncrono. No obtendrás el vídeo directamente en la respuesta de envío. Obtendrás un identificador de tarea y el vídeo llegará más tarde.

Estructura de una solicitud de imagen a vídeo

Antes de escribir código, conviene entender qué envías realmente y qué se devuelve. La generación de imagen a vídeo es más pesada que la generación de texto, por lo que la API la modela como una tarea en segundo plano.

Entrada: prompt y primer fotograma

Una solicitud a veo-3.1-fast/image-to-video tiene dos partes conceptuales:

  • Un prompt de texto que describe el movimiento, el comportamiento de la cámara y la intención de la escena, es decir, lo que debe ocurrir después del primer fotograma.
  • Una imagen de referencia que ancla el primer fotograma de la salida. En esto se diferencia imagen a vídeo de texto a vídeo: el modelo continúa a partir de tu imagen en lugar de crear un fotograma inicial de la nada.

El propio nombre del modelo indica el compromiso asumido: la variante fast está optimizada para una respuesta más rápida en tareas de imagen a vídeo, lo que la hace ideal para iterar y para canalizaciones donde la latencia importa más que la máxima fidelidad.

La salida generada incluye audio nativo, por lo que el archivo final es un vídeo completo con sonido, no un clip mudo que necesite una banda sonora aparte.

Ciclo de vida de la tarea

Como la generación tarda, la interacción se divide en tres fases:

  1. Envío. Haces POST del prompt y la imagen al endpoint del modelo. La respuesta es una confirmación ligera que contiene el identificador de la tarea, no el vídeo.
  2. Sondeo. Con ese identificador, haces GET repetidamente del estado de la tarea hasta que informe de que ha terminado (o ha fallado).
  3. Obtención. Una vez completada, la respuesta de la tarea apunta al vídeo generado, que descargas y almacenas.

Mantener estas fases separadas en el código es la decisión estructural más importante. Si las mezclas, los reintentos, los tiempos de espera y el manejo de errores se vuelven difusos.

Cómo se ve un envío bien formado

Independientemente de la serialización exacta, planifica una solicitud que contenga lo siguiente:

  • El identificador del modelo veo-3.1-fast/image-to-video.
  • El texto de tu prompt.
  • La referencia a la imagen de primer fotograma.
  • La autenticación mediante la clave de API en la cabecera de la solicitud.

Todo lo demás (parámetros opcionales, valores predeterminados, opciones de resolución de salida) pertenece a la referencia de la API de HiAPI de ese modelo. Léela una vez, coloca los nombres de campo confirmados en constantes y no los disperses por el código.

Trata la respuesta de envío como un recibo. Regístrala, guarda el ID de la tarea y continúa con el sondeo.

Realizar la llamada con curl

Enviemos una tarea. El siguiente comando envía al endpoint del modelo en HiAPI un cuerpo JSON que contiene el prompt y la imagen de primer fotograma, y se autentica con la clave del entorno.

curl -sS -X POST "$HIAPI_BASE_URL/v1/videos/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "veo-3.1-fast/image-to-video",
    "prompt": "Slow dolly forward through a sunlit forest path, leaves drifting, soft birdsong ambience",
    "image": "'"$FIRST_FRAME_URL"'"
  }'

Algunas notas sobre este comando:

  • La ruta del endpoint y el nombre del campo image deben verificarse con la referencia de la API de HiAPI de veo-3.1-fast/image-to-video; si difieren, sustitúyelos por los valores canónicos. Lo importante aquí es la estructura: modelo, prompt e imagen.
  • El prompt sí importa. Describe explícitamente el movimiento y el desplazamiento de cámara ("dolly lento hacia delante", "hojas flotando") y menciona la ambientación sonora que deseas, porque el modelo genera imagen y audio nativo a la vez.
  • -sS mantiene la salida silenciosa pero sigue mostrando errores, justo lo que quieres en una demostración de terminal.
  • La imagen se pasa como una cadena de URL. Si prefieres subir bytes, consulta las formas de entrada admitidas en la referencia en lugar de adivinar.

Leer la respuesta de envío

Un envío correcto devuelve un objeto JSON compacto con el identificador de la tarea y el estado, aproximadamente así:

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

Los nombres de campo son solo ejemplos; confírmalos con la respuesta real. Lo importante es que ahora tienes un ID que puedes usar para sondear y un estado que no es el de un vídeo terminado.

Captura ese ID en el shell para simplificar los pasos siguientes:

JOB_ID="job_abc123"

Si recibes un error de autenticación, tu clave falta o tiene un formato incorrecto. Un error de validación suele significar que el cuerpo de la solicitud no coincide con el esquema documentado; revisa primero los nombres y tipos de campo. Una respuesta de límite de tasa significa que envías con demasiada frecuencia; aplica retroceso exponencial en lugar de reintentar en un bucle apretado.

La misma solicitud en Python

A continuación se muestra el envío equivalente en Python. Solo usa requests, lee la configuración de variables de entorno y separa el paso de envío del sondeo para que puedas reutilizar cada parte.

import os
import time
import requests

API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = os.environ["HIAPI_BASE_URL"].rstrip("/")
FIRST_FRAME_URL = os.environ["FIRST_FRAME_URL"]

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

MODEL = "veo-3.1-fast/image-to-video"

def submit_job(prompt: str, image_url: str) -> str:
    payload = {
        "model": MODEL,
        "prompt": prompt,
        "image": image_url,
    }
    resp = requests.post(
        f"{BASE_URL}/v1/videos/generations",
        headers=HEADERS,
        json=payload,
        timeout=60,
    )
    resp.raise_for_status()
    data = resp.json()
    job_id = data["id"]
    print(f"submitted job: {job_id} (status={data.get('status')})")
    return job_id

if __name__ == "__main__":
    job_id = submit_job(
        prompt="Slow dolly forward through a sunlit forest path, "
               "leaves drifting, soft birdsong ambience",
        image_url=FIRST_FRAME_URL,
    )

Por qué esta estructura

  • Configuración en constantes. La URL base, las cabeceras, la ruta del endpoint y el nombre del modelo se definen una sola vez. Cuando la referencia de la API indique que un nombre de campo es distinto, solo tendrás que cambiar una línea.
  • timeout es explícito. Un envío que se queda colgado para siempre es peor que un fallo rápido. Para un cuerpo JSON pequeño, sesenta segundos es más que suficiente.
  • raise_for_status() expone los fallos de inmediato. Obtendrás una excepción ante 4xx y 5xx, en lugar de analizar silenciosamente un cuerpo de error como si fuera una tarea.
  • La función solo devuelve el ID de la tarea. Quien la llama no necesita saber cómo es el payload de envío; solo necesita algo con lo que sondear.

Si tu aplicación es un servicio web, llama a submit_job en el manejador de la solicitud y entrega el ID devuelto a un proceso en segundo plano. No bloquees la solicitud HTTP mientras se renderiza el vídeo.

Sondear la tarea asíncrona hasta que el vídeo esté listo

Ahora la tarea existe y está en cola. El sondeo es cómo te enteras de cuándo termina.

El bucle de sondeo básico

def poll_job(job_id: str, timeout_s: int = 900, initial_delay: float = 5.0) -> dict:
    url = f"{BASE_URL}/v1/videos/generations/{job_id}"
    delay = initial_delay
    deadline = time.time() + timeout_s

    while time.time() < deadline:
        resp = requests.get(url, headers=HEADERS, timeout=30)
        resp.raise_for_status()
        data = resp.json()
        status = data.get("status")
        print(f"status={status}")

        if status == "succeeded":
            return data
        if status in ("failed", "cancelled"):
            raise RuntimeError(f"job {job_id} ended with status={status}: {data}")

        time.sleep(delay)
        delay = min(delay * 1.5, 30.0)  # back off, cap at 30s

    raise TimeoutError(f"job {job_id} did not finish within {timeout_s}s")

Por qué existe cada parte

  • Sondeo, no webhook. La integración más sencilla y fiable consiste en preguntar por el estado de forma programada. Los webhooks son mejores a gran escala, pero requieren un endpoint público; el sondeo funciona en cualquier lugar.
  • Retroceso exponencial con límite. Las primeras consultas son baratas y frecuentes; los intervalos se amplían después. El límite evita que el intervalo se descontrole en tareas largas. Empieza en unos cinco segundos y ve aumentando.
  • Estados terminales explícitos. El éxito es un estado; el fallo y la cancelación son otros. Trata cualquier otro caso como "sigue esperando", pero nunca esperes indefinidamente.
  • Un plazo de tiempo real. La generación de vídeo es lenta en comparación con el texto. Elige un tiempo de espera total generoso que se ajuste a la paciencia de tu producto y falla limpiamente al superarlo, en lugar de repetir para siempre.
  • Registro de estados. Imprime o registra cada transición de estado. Es lo primero que querrás tener cuando algo vaya mal.

Sondear desde el shell

Para una comprobación manual rápida durante la depuración, una llamada a curl es suficiente:

curl -sS "$HIAPI_BASE_URL/v1/videos/generations/$JOB_ID" \
  -H "Authorization: Bearer $HIAPI_API_KEY"

Vuelve a ejecutarlo cada pocos segundos, o envuélvelo en un pequeño bucle while hasta que el estado cambie a uno terminal. Confirma los nombres exactos de campos y rutas con la referencia de la API de HiAPI; el ciclo de vida es estable, pero los detalles concretos son la fuente de verdad.

Cuando poll_job regrese, la respuesta debería contener una referencia al vídeo terminado. Eso es lo que descargarás a continuación.

Descargar y verificar la salida

La tarea ha terminado y la respuesta apunta al archivo generado. Descargarlo es un simple GET HTTP, pero hay que hacer bien dos cosas: escribirlo en disco por streaming y verificar lo que has obtenido.

Obtener el vídeo

import os
from urllib.parse import urlparse

def download_video(job: dict, out_dir: str = "outputs") -> str:
    video_url = job["output"]["url"]  # confirm the field path in the API reference
    os.makedirs(out_dir, exist_ok=True)

    parsed = urlparse(video_url)
    ext = os.path.splitext(parsed.path)[1] or ".mp4"
    dest = os.path.join(out_dir, f"{job['id']}{ext}")

    with requests.get(video_url, stream=True, timeout=120) as r:
        r.raise_for_status()
        with open(dest, "wb") as f:
            for chunk in r.iter_content(chunk_size=1 << 16):
                f.write(chunk)

    size = os.path.getsize(dest)
    if size == 0:
        raise IOError(f"downloaded file is empty: {dest}")
    print(f"saved {dest} ({size} bytes)")
    return dest

Notas:

  • Streaming por fragmentos. Los archivos de vídeo son grandes; cargarlos en memoria con r.content es un desperdicio y puede fallar en hosts con recursos limitados.
  • Conserva la extensión para que los reproductores y las herramientas posteriores reconozcan el formato del contenedor.
  • Nombra según el ID de la tarea. Es único, rastreable y evita colisiones al ejecutar lotes.
  • Rechaza archivos vacíos. Una descarga de cero bytes es una descarga fallida, incluso si el código de estado indica 200.

Si la URL del vídeo está firmada o tiene límite de tiempo, descarga el vídeo justo después de que termine la tarea, en lugar de guardar la URL para usarla más tarde.

Verificar que es un vídeo real con audio

Que el archivo exista no significa que el archivo sirva. Compruébalo:

  • Reprodúcelo localmente con un reproductor multimedia y confirma imagen y sonido. veo-3.1-fast/image-to-video genera audio nativo, así que un resultado silencioso es una señal de alarma que merece investigarse antes de que llegue al usuario.
  • Inspecciona el contenedor con ffprobe si tienes ffmpeg instalado:
ffprobe -hide_banner outputs/job_abc123.mp4

Busca en la salida un flujo de vídeo y un flujo de audio, además de una duración razonable. También es la forma más rápida de detectar un archivo truncado.

  • Haz una comprobación básica del primer fotograma. Debería parecerse a tu imagen de entrada. Si no es así, antes de culpar al modelo, sospecha de la URL de la imagen o del campo que usaste para pasarla.

Una vez validada la descarga, conserva el archivo en un almacenamiento persistente y registra también el ID de la tarea. Esa asociación permite depurar, volver a ejecutar y conciliar la facturación más adelante.

Errores comunes y comprobaciones antes de escalar

La mayoría de los problemas de integración con las API de vídeo asíncronas no son problemas del modelo, sino de la canalización. Esto es lo que deberías revisar primero.

Entrada de imagen

  • ¿La imagen es realmente accesible? Una URL que funciona en tu navegador puede requerir autenticación, bloquear el hotlinking o caducar. Prioriza URLs estables y de lectura pública.
  • ¿Está en un formato admitido? A menos que la referencia de la API indique lo contrario, usa formatos comunes como JPEG y PNG.
  • ¿El contenido es el correcto? El primer fotograma ancla todo el clip. Una imagen de baja calidad o fuera de tema producirá un vídeo decepcionante que ningún ajuste del prompt podrá arreglar.

Estructura de la solicitud

  • Nombres y tipos de campo. Verifícalos con la referencia de la API de HiAPI de veo-3.1-fast/image-to-video. Los fragmentos copiados de otras plataformas fallarán; el endpoint es de HiAPI y los nombres de campo distinguen mayúsculas de minúsculas.
  • Tipo de contenido. Envía Content-Type: application/json y un cuerpo JSON real, no datos de formulario.
  • Identificador del modelo. Usa la cadena completa veo-3.1-fast/image-to-video; los nombres truncados devuelven errores que parecen no relacionados.

Tiempos de espera y reintentos

  • Tiempos de espera independientes por fase. El envío, las solicitudes de sondeo y la descarga del archivo merecen cada uno el suyo. Un único tiempo de espera global será demasiado ajustado para la descarga o demasiado holgado para las comprobaciones de estado.
  • Reintenta con seguridad. El envío se puede reintentar ante errores de red transitorios y respuestas 5xx, pero solo si puedes determinar si la tarea se creó realmente; de lo contrario, podrías facturar y generar dos veces. El sondeo y la descarga son idempotentes y se pueden reintentar libremente.
  • Aplica retroceso ante los límites de tasa. Respeta la respuesta y reduce la velocidad en lugar de machacar el endpoint.

Comportamiento del sondeo

  • Nunca sondees en un bucle apretado. Desperdicia cuota y parece abuso.
  • Ten siempre una condición de terminación. Un plazo que lanza una excepción es mejor que una tarea atascada en memoria.
  • Registra cada transición de estado con su marca de tiempo. La necesitarás para inferir la latencia.

Higiene operativa

  • Mantén las claves de API en el servidor. Nunca las envíes al navegador ni las incrustes en una aplicación móvil.
  • Guarda el ID de la tarea junto a cada artefacto. Es la clave que conecta registros, archivos y registros de uso.
  • Valida las descargas antes de publicarlas. Comprueba el tamaño y, si es posible, los flujos. Un archivo vacío o silencioso que llega al usuario cuesta mucho más de arreglar que una descarga fallida que reintentas.
  • No des por sentadas las rutas de campo. La ubicación de output en la respuesta de la tarea completada es justo el tipo de detalle que cambia; léela en la referencia de la API, no en un tutorial, incluido este.

Antes de abrir un ticket sobre el modelo, revisa esta lista en orden. La mayoría de las veces, la solución es una URL, un nombre de campo o la falta de retroceso.

Ese es el ciclo completo: enviar con una imagen de primer fotograma y un prompt, sondear la tarea hasta que termine y luego descargar y verificar el vídeo con audio nativo. Empieza en la página del modelo veo-3.1-fast/image-to-video en HiAPI para confirmar los campos de solicitud actuales y después lleva las dos funciones anteriores a tu propio servicio.

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