HiAPI
Blog

Tutorial

Tutorial de kandinsky-6.0-pro/image-to-video en HiAPI: curl + Python

Realiza una solicitud de imagen a video funcional en HiAPI: desde la carga del primer fotograma hasta el sondeo asíncrono y la descarga final

Nov 3, 2026
深色底上青绿与石板灰的扁平液态色块相互交叠,构成 kandinsky-6.0-pro 视频教程的封面图。

Visión general del flujo de image-to-video en HiAPI

Cada llamada de imagen a video en HiAPI sigue el mismo ciclo de vida de tres fases. Conocerlo de antemano hace que el resto del tutorial parezca metódico en lugar de misterioso.

Fase 1: enviar la tarea

Envías una solicitud POST al endpoint kandinsky-6.0-pro/image-to-video con la imagen del primer fotograma y un breve prompt que describe el movimiento que esperas. La respuesta no devuelve un flujo de bytes de vídeo; acepta la tarea y devuelve un identificador de tarea: todas las operaciones posteriores dependen de este identificador.

Fase 2: sondear el estado

La generación de vídeo lleva tiempo, por lo que el endpoint es asíncrono. Llamas a la ruta de estado con el identificador de la tarea, y esta informa el estado actual de la tarea: en cola, procesando, exitosa o fallida. Repites la comprobación a intervalos cortos hasta que el estado alcance un estado terminal.

Fase 3: obtener la salida

Una vez que la tarea informa éxito, la carga útil del estado incluye la ubicación del vídeo final. Lo descargas y lo escribes en disco. Ese es todo el contrato.

Por qué importa esta forma

La separación entre envío y sondeo no es casualidad. Significa que tu cliente HTTP no necesita mantener un socket abierto durante todo el renderizado, y también que puedes persistir el identificador de la tarea, irte y retomarla después. Cuando algo falla, el campo de estado te dice dónde falló, lo cual es mucho más útil que un timeout opaco.

Recuerda las tres fases: enviar, sondear, descargar. Los ejemplos de curl y Python a continuación son solo dos formas de escribir la misma secuencia.

Preparar el primer fotograma y las credenciales de la API

Solo hay dos lugares donde suelen surgir problemas antes de la primera llamada: la clave y la imagen. Resuelve ambos ahora y el resto del tutorial irá sobre ruedas.

Configurar la clave de API como variable de entorno

No pegues la clave en el archivo fuente. Expórtala en el shell para que tanto curl como Python puedan obtenerla sin codificar nada de forma rígida:

export HIAPI_API_KEY="your_api_key_here"

En Windows PowerShell, el equivalente es $env:HIAPI_API_KEY="your_api_key_here". Antes de continuar, confirma que el shell puede leerla:

echo "$HIAPI_API_KEY" | head -c 4; echo

Deberías ver los primeros caracteres de la clave. Si no ves nada, la variable no se persistió en la sesión de shell actual; esta es una causa común de respuestas 401 posteriores.

Elegir una imagen de primer fotograma válida

El primer fotograma determina el sujeto, la composición y la relación de aspecto de todo el vídeo. Algunas restricciones prácticas evitan la mayoría de los rechazos:

  • Formato: usa un formato de imagen web estándar, como JPEG o PNG. Evita códecs poco comunes, casos límite progresivos o perfiles de color CMYK.
  • Accesibilidad: la API necesita leer realmente esos bytes. Si pasas una URL, asegúrate de que sea accesible públicamente o esté correctamente autenticada; si pasas datos en línea, asegúrate de que la codificación esté completa.
  • Tamaño: las imágenes de tamaño razonable se validan más rápido. Los archivos extremadamente grandes ralentizan la solicitud y a menudo son la raíz de errores de carga.
  • Contenido: elige un fotograma que tenga sentido como punto de partida del movimiento: sujeto claro y fondo sencillo. El prompt de movimiento describe cómo alejarse de este fotograma, por lo que un fotograma desordenado producirá un vídeo desordenado.

Decidir cómo referenciar la imagen

El cuerpo de la solicitud te permite apuntar al primer fotograma mediante una URL o datos codificados en línea. Elige según dónde esté la imagen:

  • Si el archivo ya está alojado y es legible públicamente, la URL es la opción más limpia.
  • Si la imagen es local o privada, la codificación en línea hace que toda la solicitud sea autocontenida, a costa de una carga útil más grande.

De cualquier manera, mantén la misma forma de variable para la referencia de imagen en curl y Python, para que ambos ejemplos sigan siendo directamente comparables.

Realizar la solicitud con curl

Esta es una solicitud de envío completa. Sustituye tu propia referencia de imagen y prompt y ejecútala.

curl -sS -X POST "https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "https://your-host.example/first-frame.jpg",
    "prompt": "slow dolly-in on the subject, gentle ambient motion, stable camera",
    "duration": 5,
    "aspect_ratio": "16:9"
  }'

Qué hace cada parte

  • URL apunta específicamente al modelo kandinsky-6.0-pro/image-to-video. La selección del modelo está en la ruta, no en el cuerpo de la solicitud, así que un error tipográfico aquí dará un 404 en lugar de un mensaje de validación amable.
  • Authorization lleva el token bearer de la variable de entorno que exportaste. Nada más autentica la llamada.
  • Content-Type: application/json es importante para cualquier POST con cuerpo JSON; si lo omites, algunos proxies lo estropean antes de que la carga útil llegue a la API.
  • image es tu referencia al primer fotograma: aquí en forma de URL, o una cadena codificada en línea si la imagen es privada.
  • prompt describe el movimiento. Mantenlo centrado en el movimiento y el comportamiento de la cámara, no en repetir lo que ya contiene la imagen.
  • duration y aspect_ratio dan forma a la salida. La relación de aspecto debe coincidir con el primer fotograma; una discrepancia provocará recorte o barras negras. La facturación se mide por la salida que generas, así que estos ajustes también determinan tu coste.

Leer la respuesta

Un envío exitoso devuelve la carga útil de la tarea, no el vídeo. Captura el identificador de la tarea: lo pasarás más adelante al endpoint de estado:

curl -sS -X POST "https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json | jq -r '.id'

Usar un archivo body.json en lugar de -d en línea mantiene legibles los prompts largos y las imágenes codificadas. jq es opcional, pero convierte la respuesta en una sola línea que puedes pegar directamente en la siguiente llamada.

Llamar al mismo endpoint con Python

La versión en Python es la misma solicitud, el mismo cuerpo, solo envuelta en una función para reutilizarla. Mantén el cuerpo idéntico al de la versión curl: esa es precisamente la razón de mostrar ambos.

import os
import json
import requests

API_BASE = "https://api.hiapi.ai/v1"
ENDPOINT = f"{API_BASE}/kandinsky-6.0-pro/image-to-video"

def submit_video_job(image_ref: str, prompt: str, duration: int = 5, aspect_ratio: str = "16:9") -> str:
    """Submit a first-frame image-to-video job and return the job id."""
    api_key = os.environ.get("HIAPI_API_KEY")
    if not api_key:
        raise RuntimeError("HIAPI_API_KEY is not set in this environment")

    body = {
        "image": image_ref,
        "prompt": prompt,
        "duration": duration,
        "aspect_ratio": aspect_ratio,
    }

    response = requests.post(
        ENDPOINT,
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        },
        json=body,
        timeout=60,
    )

    if response.status_code >= 400:
        raise RuntimeError(f"submit failed: {response.status_code} {response.text}")

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

if __name__ == "__main__":
    job_id = submit_video_job(
        image_ref="https://your-host.example/first-frame.jpg",
        prompt="slow dolly-in on the subject, gentle ambient motion, stable camera",
    )
    print(json.dumps({"job_id": job_id}, indent=2))

Notas sobre la versión en Python

  • Falla rápido si falta la clave. La comprobación explícita del entorno convierte el error más común en un mensaje de error local claro, en lugar de un 401 remoto.
  • json=body frente a data=json.dumps(body). Pasar json= hace que requests configure el tipo de contenido por ti, manteniendo las cabeceras mínimas. Si usas data=, establece Content-Type explícitamente.
  • Timeout holgado. El envío de vídeo a veces implica transmitir cargas útiles de imagen grandes. Un timeout demasiado corto produce fallos falsos.
  • Leer el cuerpo de la respuesta en caso de error. Lanzar tanto el código de estado como el cuerpo de la respuesta significa que el mensaje de error realmente te dice qué rechazó la API.

Si el primer fotograma es un archivo local, léelo, codifícalo en base64 y usa esa cadena para reemplazar image_ref dentro de submit_video_job. La firma de la función no cambia, que es exactamente por lo que ambos estilos de llamada comparten la misma definición de cuerpo de solicitud.

Sondear la tarea y descargar el vídeo

Una tarea enviada no es el vídeo final. Sondea hasta que la tarea alcance un estado terminal y luego descarga.

Bucle de sondeo

import time
import requests

def wait_for_video(job_id: str, interval: float = 10.0, max_wait: float = 600.0) -> str:
    """Poll the job until it finishes; return the output URL."""
    import os
    api_key = os.environ["HIAPI_API_KEY"]
    url = f"https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video/{job_id}"
    headers = {"Authorization": f"Bearer {api_key}"}

    deadline = time.time() + max_wait
    while time.time() < deadline:
        response = requests.get(url, headers=headers, timeout=30)
        response.raise_for_status()
        payload = response.json()
        status = payload.get("status")

        if status == "succeeded":
            return payload["output"]
        if status == "failed":
            raise RuntimeError(f"job failed: {payload.get('error', 'unknown error')}")

        time.sleep(interval)

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

Tres detalles hacen que este bucle funcione bien:

  • Esperar por intervalos, no confiar en la suerte. Un sueño fijo de unos diez segundos es un punto de partida razonable; no bombardees el endpoint en un bucle ajustado.
  • Manejar cada estado terminal. succeeded te da la salida, failed te da el mensaje de error. Ambos deben tratarse como salida. Un bucle que solo comprueba el éxito seguirá girando hasta la fecha límite en una tarea muerta.
  • Limitar el tiempo de espera. max_wait garantiza que el script termine, en lugar de ejecutarse indefinidamente tras una tarea atascada.

Descargar el vídeo final

Una vez que tengas la URL de salida, escribe en disco en streaming en lugar de cargar todo el archivo en memoria:

def download_video(output_url: str, path: str = "result.mp4") -> str:
    with requests.get(output_url, stream=True, timeout=120) as response:
        response.raise_for_status()
        with open(path, "wb") as handle:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    handle.write(chunk)
    return path

Combina las partes:

job_id = submit_video_job(
    image_ref="https://your-host.example/first-frame.jpg",
    prompt="slow dolly-in on the subject, gentle ambient motion, stable camera",
)
output_url = wait_for_video(job_id)
saved = download_video(output_url, "kandinsky_result.mp4")
print(f"saved to {saved}")

En producción, persiste el job_id en el momento en que el envío tenga éxito. Si el proceso se reinicia, puedes reanudar el bucle de sondeo con el identificador almacenado, sin pagar por un nuevo envío.

Solucionar errores comunes de solicitud y tarea

La mayoría de los fallos se agrupan en tres categorías: clave, imagen o estado de la tarea. Aísla la categoría antes de cambiar cualquier otra cosa.

Clave rechazada

Síntoma: error de autenticación o autorización antes de crear cualquier tarea.

  • Confirma que la variable está realmente exportada en el shell actual: echo "$HIAPI_API_KEY".
  • Comprueba si se pegó espacio en blanco o un salto de línea junto con la clave; elimínalos.
  • Verifica que la cabecera esté escrita como Authorization, con un valor que empiece por Bearer seguido de un espacio.
  • Si curl funciona pero Python no, imprime os.environ.get("HIAPI_API_KEY") desde el mismo intérprete: puede que la variable exista en tu shell pero no en el entorno que hereda el script.

Imagen rechazada

Síntoma: error de validación al enviar, o la tarea falla de inmediato.

  • Formato incorrecto o poco común. Vuelve a exportar como JPEG o PNG estándar y reintenta. Los perfiles CMYK y los códecs exóticos son culpables habituales.
  • URL inaccesible. Abre la URL de tu imagen en una ventana de navegación privada; si tú no obtienes los bytes, la API tampoco.
  • Codificación en línea dañada. Si codificaste en base64 localmente, decodifica la cadena una vez y confirma que es una imagen válida antes de enviarla.
  • Relación de aspecto no coincidente. Si el vídeo vuelve recortado o con barras negras, alinea aspect_ratio con el primer fotograma en lugar de pelearte con la salida después.

La tarea no termina o falla

Síntoma: el estado se queda en queued/processing, o informa failed.

  • Lee el campo error. Las tareas fallidas llevan un mensaje. Normalmente indica el campo o la condición que falló. No adivines: registra toda la carga útil del estado.
  • Respeta el diseño asíncrono. Es normal esperar unos segundos antes de comprobar. Un bucle de reintentos ajustado que se rinde a los dos segundos no es un mecanismo de estado.
  • Limita el tiempo de espera. Si una tarea supera un límite razonable, trátala como fallida, expón el id de la tarea y envía una nueva, en lugar de sondear para siempre.
  • Compara con una llamada que sabes que funciona. Si una imagen personalizada falla, ejecuta el curl exacto de la parte anterior de este tutorial con una imagen pública pequeña y un prompt corto. Si funciona, el problema está en tu entrada; si también falla, el problema está en las credenciales o en la forma de la solicitud.

Errores de descarga tras el éxito

Si la tarea informa succeeded pero la descarga falla, la URL de salida puede haber caducado o estar bajo control de acceso. Vuelve a sondear la tarea para obtener una nueva ubicación de salida, en lugar de reintentar la caducada.

Aborda estas categorías en orden: clave, luego imagen, luego estado de la tarea. Cada error se reduce rápidamente a una causa. Ese es todo el ciclo: enviar el primer fotograma, sondear hasta completar, descargar el resultado.

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