Tutorial
Cómo llamar a veo-3.1-lite/text-to-video mediante la API de HiAPI: curl y Python
Ejemplos de solicitud listos para copiar, el flujo de generación de video a partir de un prompt y el patrón de sondeo asíncrono
Qué te ofrece veo-3.1-lite/text-to-video
veo-3.1-lite/text-to-video es un endpoint de texto a video en HiAPI: envías un prompt de texto y el modelo devuelve un clip de video generado. El "lite" del nombre es la señal más importante a la hora de elegir el modelo. El nivel ligero está pensado para el rendimiento y la iteración rápida, no para la máxima fidelidad, por lo que es una buena opción por defecto cuando necesitas muchos clips cortos, cuando haces prototipos de una función antes de decidirte por un modelo más pesado, o cuando el clip es solo una pieza de una tubería de automatización mayor y la latencia por clip y el volumen por lotes importan más que la calidad cinematográfica.
Cuando el clip es en sí mismo el producto —por ejemplo, un plano principal, material de marketing o cualquier contenido que vaya a ser examinado fotograma a fotograma— conviene usar un modelo de texto a video más pesado. En cambio, cuando el clip es solo un componente —una vista previa de borrador, un bucle de fondo, un boceto de storyboard o una primera renderización que luego se retocará— elige veo-3.1-lite/text-to-video.
Dos características de este endpoint determinan todo lo que sigue:
- Es asíncrono. Una solicitud de generación no te devuelve en la misma respuesta un archivo reproducible. Envías una tarea, recibes una referencia a esa tarea y luego sondeas hasta que la tarea termina y la URL de salida está disponible.
- Está impulsado por el prompt. En este flujo no hay imagen de entrada ni video de origen: el texto que envías es el brief creativo completo, así que la calidad del prompt es la palanca principal que controlas.
El resto de esta guía es un recorrido práctico: construir la solicitud, enviarla con curl, portarla a Python, sondear correctamente y gestionar los fallos que realmente encontrarás.
Requisitos previos y autenticación
Antes de la primera llamada, asegúrate de tener lo siguiente:
- Una clave de API de tu cuenta de HiAPI. Trátala como un secreto con el mismo cuidado que la contraseña de una base de datos.
- Una forma de enviar solicitudes HTTP:
curlpara comprobaciones rápidas y Python 3 con la bibliotecarequestspara cualquier tarea automatizada. - Un lugar donde guardar el clip final, ya que la salida llega como una URL que tendrás que descargar.
La clave se envía en cada solicitud mediante una cabecera HTTP. Copia exactamente el nombre de la cabecera y el prefijo Bearer tal como se muestran; una cabecera ausente o mal formada es la causa más común de fallo en la primera llamada.
Mantén la clave fuera del control de código fuente
Nunca pegues una clave real en un archivo que vayas a confirmar. Usa variables de entorno:
export HIAPI_API_KEY="your-key-here"
Y luego léela en tiempo de ejecución. Si trabajas en equipo o en una tubería de CI, guarda la clave en un gestor de secretos e inyéctala como variable de entorno durante el despliegue. Si la clave se expone alguna vez —por un commit accidental, por pegarla en un ticket o por imprimirla en un log— rótala de inmediato en lugar de confiar en que nadie lo note.
Antes de empezar a depurar cualquier otra cosa, haz una comprobación rápida de que la variable de entorno está establecida:
test -n "$HIAPI_API_KEY" && echo "key is set" || echo "key is missing"
Una solicitud curl que funciona
A continuación se muestra la forma de la solicitud de envío de texto a video. Sustituye la dirección base del endpoint de marcador de posición por la que se documenta en tu referencia de HiAPI y mantén el identificador del modelo exactamente igual: veo-3.1-lite/text-to-video.
curl -X POST "$HIAPI_BASE_URL/v1/videos/generations" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "veo-3.1-lite/text-to-video",
"prompt": "A slow dolly-in on a rain-soaked neon sign at night, shallow depth of field, gentle camera drift, cinematic color"
}'
Un envío exitoso no devuelve bytes de video. Devuelve un objeto de tarea que incluye el identificador de la tarea, un campo status con un valor inicial como queued o processing, y metadatos sobre la hora de creación. Ese identificador es el identificador que usarás para todo lo demás: sondeas con él hasta que el estado pasa a ser terminal, y solo entonces la respuesta incluye la URL del clip generado.
Si estás probando manualmente, captura la respuesta completa en lugar de limitarte a mirarla:
curl -s -X POST "$HIAPI_BASE_URL/v1/videos/generations" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"veo-3.1-lite/text-to-video","prompt":"A paper boat drifting down a sunlit gutter, macro lens"}' \
| tee submission.json
Hay dos hábitos que conviene adquirir desde ahora. Primero, envía siempre Content-Type: application/json; un cuerpo de solicitud que parece JSON pero no se declara como JSON será rechazado. Segundo, si la llamada falla, lee el cuerpo de la respuesta de error antes de cambiar nada: HiAPI devuelve mensajes estructurados que suelen indicar el problema con más precisión que el mero código de estado (clave incorrecta, modelo desconocido, campo ausente).
Si aquí se devuelve un código de estado que no es 2xx, significa que la tarea nunca fue aceptada. No sondees una solicitud de envío fallida; corrige primero la solicitud.
Implementar la misma solicitud en Python
La llamada curl se puede portar directamente. El siguiente script envía el prompt, comprueba si el envío se realizó correctamente y guarda la referencia de la tarea para usarla en el paso de sondeo.
import os
import requests
BASE_URL = os.environ["HIAPI_BASE_URL"]
API_KEY = os.environ["HIAPI_API_KEY"]
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
payload = {
"model": "veo-3.1-lite/text-to-video",
"prompt": (
"A single lantern floating on still black water, "
"soft ripples, warm light against cool darkness, slow push-in"
),
}
response = requests.post(
f"{BASE_URL}/v1/videos/generations",
headers=headers,
json=payload,
timeout=30,
)
if response.status_code >= 400:
raise RuntimeError(f"Submission failed: {response.status_code} {response.text}")
job = response.json()
job_id = job["id"]
print(f"submitted job {job_id} with status {job.get('status')}")
En este fragmento de código hay varias decisiones deliberadas:
- Usar
json=payloaden lugar de concatenar cadenas a mano. Deja que la biblioteca se encargue de la serialización y del tipo de contenido; así evitas toda una clase de errores relacionados con las comillas. - Establecer
timeoutexplícitamente. Si no lo haces, una conexión estancada dejará el script colgado indefinidamente. La llamada de envío es rápida: incluso un tiempo de espera generoso aquí seguirá siendo corto. - Fallar de forma dura ante códigos que no sean 2xx. Lanzar una excepción de inmediato hace que el error quede junto a su causa, en lugar de dejar que un ID de tarea corrupto se propague al bucle de sondeo.
- La clave proviene de
os.environ. El archivo en sí no contiene ningún secreto.
Si lo encapsulas en una función que devuelva job_id, ya tienes la parte de envío de cualquier tubería.
Sondear la tarea asíncrona y descargar el clip
La generación de video lleva tiempo. La llamada de envío devuelve en uno o dos segundos; la renderización real no. Si escribes código que asume que la primera respuesta contiene la URL del video, fallará, no porque la API esté rota, sino porque estás leyendo una tarea que aún no ha terminado.
El patrón correcto es el sondeo acotado: dormir entre comprobaciones, aumentar el intervalo a medida que crece la espera y abandonar tras un número máximo de intentos en lugar de entrar en un bucle infinito.
import time
import requests
def wait_for_video(job_id, headers, base_url, max_attempts=60, initial_delay=5):
delay = initial_delay
for attempt in range(1, max_attempts + 1):
r = requests.get(
f"{base_url}/v1/videos/generations/{job_id}",
headers=headers,
timeout=30,
)
if r.status_code >= 400:
raise RuntimeError(f"Poll failed: {r.status_code} {r.text}")
data = r.json()
status = data.get("status")
print(f"attempt {attempt}: {status}")
if status == "completed":
return data
if status in {"failed", "cancelled"}:
raise RuntimeError(f"Job {job_id} ended as {status}: {data}")
time.sleep(delay)
delay = min(delay * 1.5, 30)
raise TimeoutError(f"Job {job_id} did not finish within polling budget")
Cuando la tarea alcanza completed, la respuesta incluye la ubicación de salida, es decir, la URL del clip terminado. Si el clip es grande, descárgalo en lugar de leerlo en memoria como un flujo:
result = wait_for_video(job_id, headers, BASE_URL)
video_url = result["output"]["url"]
with requests.get(video_url, stream=True, timeout=120) as r:
r.raise_for_status()
with open(f"{job_id}.mp4", "wb") as f:
for chunk in r.iter_content(chunk_size=1 << 16):
f.write(chunk)
print("saved", f"{job_id}.mp4")
Hay dos detalles importantes en producción. Primero, la URL de salida puede tener una validez limitada, así que descarga el archivo a tiempo en lugar de guardar el enlace durante días y recuperarlo después: persiste el archivo o cópialo a tu propio almacenamiento. Segundo, registra el ID de la tarea junto a cada artefacto. Cuando algo falle aguas abajo, el ID de la tarea te permite contrastar tus registros con lo que la API devolvió realmente.
Errores comunes y cómo solucionarlos
La mayoría de los fallos en la primera ejecución pertenecen a unas pocas categorías. A continuación se muestra cómo se manifiesta cada una y qué hay que cambiar.
Fallo de autenticación
Un 401 o 403 al enviar significa que la clave no fue aceptada. Comprueba en orden: si la variable de entorno tiene realmente un valor en ese shell o proceso; si el nombre de la cabecera es Authorization; si el prefijo Bearer está presente y seguido de un espacio; si la clave ha sido rotada o revocada. Que una clave funcione en tu terminal pero falle en CI casi siempre significa que el secreto no se inyectó en ese entorno.
Identificador de modelo no válido
Un 400 o 404 que señala el nombre del modelo indica que la cadena no coincide. El identificador es veo-3.1-lite/text-to-video: la barra y el número de versión forman parte de él. Cópialo y pégalo; no lo escribas de nuevo; un espacio extra o un número de versión modificado basta para que falle.
Prompt mal formado o vacío
Un prompt vacío, la ausencia del campo prompt o el envío del cuerpo de la solicitud sin Content-Type: application/json se rechazan antes de que empiece la generación. Si construyes el prompt de forma dinámica, registra la cadena exacta que enviaste cuando la llamada falle: los errores de plantilla que producen prompts en blanco o truncados son habituales e invisibles hasta que se imprimen.
Tiempo de espera agotado en el sondeo
Si wait_for_video agota su presupuesto, no vuelvas a enviar la tarea de inmediato. Puede que la tarea simplemente siga en ejecución. Vuelve a comprobar el estado, confirma que tu intervalo de sondeo no es tan largo que se pase el presupuesto durmiendo y comprueba que el presupuesto sea realista para la duración del clip solicitado. Reenviar la tarea por cada tiempo de espera agotado es justo lo que hace que pagues varias veces por el mismo clip sin darte cuenta.
Límites de velocidad
Un 429 significa que hay que retroceder. El retardo exponencial del bucle de sondeo ya ayuda en esto; aplica la misma moderación al envío y pon el trabajo en cola en lugar de enviarlo en ráfagas. En caso de duda, sigue primero cualquier indicación de reintento que aparezca en las cabeceras de respuesta y luego aplica tu propio plan de retroceso.
Costos y consejos de iteración antes de producción
La generación de video se factura por la salida, es decir, por la duración del contenido que produces, de modo que el error caro es probar ideas pequeñas con clips largos. El flujo de trabajo más económico es el que nunca genera clips que no necesitas.
Prueba la estructura del prompt, no el resultado final. Escribe y ajusta los prompts para la duración más corta que admita el endpoint. Una vez que la composición, el movimiento y el lenguaje de cámara se leen correctamente en clips cortos, aumenta la duración para la renderización final. La mayoría de las iteraciones de un prompt tienen que ver con la redacción, no con la longitud, y una renderización corta responde igual de bien a las preguntas de redacción.
Mantén un registro de prompts. Anota el prompt de cada prueba, la duración y tu propia valoración. Diez iteraciones documentadas te enseñan más sobre aquello a lo que responde este modelo que cincuenta iteraciones sin registro, y no volverás a probar un prompt que ya descartaste.
Procesa por lotes de forma consciente. Si necesitas muchos clips, ponlos en cola y ejecútalos en paralelo dentro de los límites de velocidad, pero haz primero una prueba del lote completo con unos pocos prompts. Descubrir un error sistemático en los prompts después de enviar doscientas tareas es el costo más evitable.
Sondea con moderación. Sondear con frecuencia no hará que la renderización termine antes y puede empujarte a los límites de velocidad. Un intervalo inicial moderado junto con un retroceso suave son los valores predeterminados correctos.
Falla pronto y de forma visible. Cada protección del código anterior —lanzar una excepción ante códigos que no sean 2xx al enviar, detectar fallos terminales en el sondeo, limitar el número de intentos— está pensada para evitar que pagues dos veces por el mismo trabajo. Consérvalas en producción.
Empieza con un prompt corto y una solicitud curl, confirma de extremo a extremo el ciclo de vida de la tarea y luego traslada el mismo flujo a tu tubería de Python. La referencia completa del endpoint está en la página de documentación del modelo veo-3.1-lite/text-to-video de HiAPI.
Artículos relacionados
Ver todos los artículos →-
Tutorial
Cómo llamar a hailuo-2.3-fast/image-to-video a través de la API de HiAPI: práctica con curl y Python
La primera solicitud ejecutable del nivel rápido y de bajo coste de imagen a vídeo: del ID del modelo a la descarga del MP4
-
Tutorial
Cómo llamar a hailuo-2.3/text-to-video mediante la API de HiAPI: curl, Python y ejemplos de solicitud ejecutables
-
Tutorial
Uso de Kandinsky 6.0 Pro texto a video en HiAPI: tutorial con curl y Python
Demostración práctica centrada en la solicitud: construir la llamada de prompt a clip de video, enviar la solicitud, sondear el estado de finalización y descargar el resultado