Tutorial
Cómo usar la API de texto a vídeo de Ray 3.2: curl, Python y una solicitud lista para ejecutar
Ray 3.2 es un modelo de texto a vídeo del catálogo de modelos de hiapi y, como todos los demás modelos de vídeo, se invoca mediante la API unificada de tareas asíncronas: el id del modelo es ray-3.2/text-to-video y se envía a POST /v1/tasks. A diferencia de algunos modelos de vídeo cuyo único parámetro ajustable es el prompt, Ray 3.2 ofrece tres controles de salida realmente utilizables —duration, resolution y aspect_ratio— y cada uno de ellos se valida estrictamente en el servidor.
Esta guía te permite enviar solicitudes que funcionen a la primera: el esquema de entrada exacto (verificado contra la API en producción el 2026-10-19), una llamada curl que puedes copiar y pegar, un script completo de Python para enviar, consultar y descargar, y detalles para producción —callbacks, reintentos idempotentes y las respuestas de error que realmente verás.
Qué vas a construir y qué necesitas
Objetivo: enviar un prompt de texto, consultar la tarea y descargar un clip .mp4 sin audio (de 5 o 10 segundos).
Requisitos previos:
- Una clave de API de hiapi — obtenla desde tu consola. La clave tiene la forma
sk-...y se coloca en la cabeceraAuthorization: Bearer. ¿Es tu primera vez con la API de tareas? Empieza por la guía de inicio rápido. - Cualquier cliente HTTP. Mostraremos
curly Python (requests).
La salida de Ray 3.2 se cobra por clip, y el precio aumenta considerablemente según tus elecciones — consulta las tarifas actuales en la página de precios. Mientras iteres con prompts, empieza con 540p / 5 segundos; un clip de 1080p y 10 segundos puede costar muchas veces más que una renderización de imagen de presupuesto ajustado, así que no dejes tu ciclo de desarrollo funcionando siempre con los ajustes más altos.
El esquema de entrada exacto (un campo obligatorio, tres enumeraciones estrictas)
Todo va dentro de un objeto input. Verificado campo por campo contra la API en producción:
| Campo | Tipo | Obligatorio | Valores permitidos | Valor predeterminado |
|---|---|---|---|---|
prompt |
string | Sí | Descripción de la escena, hasta 6000 caracteres | — |
duration |
integer | No | 5 o 10 |
5 |
resolution |
string | No | 540p, 720p, 1080p |
540p |
aspect_ratio |
string | No | 16:9, 9:16, 1:1, 4:3, 3:4, 21:9 |
16:9 |
Antes de escribir código, hay tres cosas que conviene tener presentes:
durationes un entero, no una cadena. Envía el número sin comillas —"duration": 5. Esto es lo contrario de lo que ocurre con otros modelos de vídeo de la API de tareas (por ejemplo, hailuo-2.3 exige"duration": "6"como cadena). Si trasladas un cuerpo de solicitud de un modelo a otro, vuelve a revisar este campo; es el error entre modelos más frecuente.- Los valores de enumeración se validan, no se truncan. Los valores fuera de rango se rechazan con
400en lugar de redondearse silenciosamente. No existe2k, ni480p, ni2:1. - No hay
prompt_optimizer,seedninegative_prompt. La composición se controla exclusivamente medianteaspect_ratiomás el prompt. Si envías campos que el esquema no reconoce, espera recibir un 400 — el esquema de entrada de/v1/tasksvaría según el modelo, así que no supongas que las claves que funcionan en otro sitio sirven aquí.
Qué devuelve realmente la validación
A continuación se muestran respuestas reales del endpoint en producción (se enviaron valores no válidos a propósito; no se creó ninguna tarea ni se facturó nada):
// POST with "resolution": "2k"
{"code":400,"data":null,"error_code":"INVALID_REQUEST",
"message":"invalid input: resolution: value must be one of '540p', '720p', '1080p'"}
// POST with "duration": 7
{"code":400,"data":null,"error_code":"INVALID_REQUEST",
"message":"invalid input: duration: value must be one of 5, 10"}
// POST with "aspect_ratio": "2:1"
{"code":400,"data":null,"error_code":"INVALID_REQUEST",
"message":"invalid input: aspect_ratio: value must be one of '16:9', '9:16', '1:1', '4:3', '3:4', '21:9'"}
El message indica el campo exacto y el conjunto exacto de valores permitidos, así que si olvidas algún valor de enumeración, la API te lo dirá. Fíjate en que los valores de duration 5, 10 no llevan comillas (son números), mientras que el valor de resolution '540p' es una cadena entre comillas: esa diferencia de comillas es la pista de qué campo es entero.
Ejemplo mínimo funcional: curl
Crear la tarea:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "ray-3.2/text-to-video",
"input": {
"prompt": "En una noche lluviosa, una bailarina callejera gira bajo las luces de neón, los charcos reflejan luces rosas y azules, su chaqueta ondea al girar, la cámara la rodea en ángulo bajo, estilo cinematográfico",
"duration": 5,
"resolution": "540p",
"aspect_ratio": "9:16"
}
}'
La respuesta de creación incluye un id de tarea (truncado):
{ "data": { "taskId": "task_01k..." } }
Consúltala hasta que alcance un estado terminal:
curl -s https://api.hiapi.ai/v1/tasks/task_01k... \
-H "Authorization: Bearer sk-YOUR_KEY"
Mientras la tarea se ejecuta, data.status informa de un estado en curso. Los dos estados terminales son success y fail. En caso de éxito (truncado):
{
"data": {
"taskId": "task_01k...",
"status": "success",
"output": [ { "url": "https://.../clip.mp4" } ]
}
}
Descarga data.output[0].url de inmediato. Las URL de salida son firmadas y caducan; guarda los bytes en tu propio almacenamiento en lugar de enlazar directamente la URL de la tarea. Ray 3.2 devuelve un MP4 sin audio (sin pista de sonido), así que no esperes un flujo de audio que nunca llegará.
Script completo de Python (enviar → consultar → descargar)
import time
import requests
API_BASE = "https://api.hiapi.ai/v1"
API_KEY = "sk-YOUR_KEY"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
def create_task(prompt: str, duration: int = 5,
resolution: str = "540p", aspect_ratio: str = "9:16") -> str:
resp = requests.post(
f"{API_BASE}/tasks",
headers=HEADERS,
json={
"model": "ray-3.2/text-to-video",
"input": {
"prompt": prompt,
"duration": duration, # 5 or 10 — an INTEGER, not a string
"resolution": resolution, # "540p" | "720p" | "1080p"
"aspect_ratio": aspect_ratio, # "16:9" | "9:16" | "1:1" | "4:3" | "3:4" | "21:9"
},
},
timeout=30,
)
resp.raise_for_status()
return resp.json()["data"]["taskId"]
def wait_task(task_id: str, timeout_s: int = 900, poll_interval: int = 5) -> dict:
deadline = time.time() + timeout_s
while time.time() < deadline:
resp = requests.get(f"{API_BASE}/tasks/{task_id}", headers=HEADERS, timeout=30)
resp.raise_for_status()
task = resp.json()["data"]
if task["status"] == "success":
return task
if task["status"] == "fail":
err = task.get("error") or {}
raise RuntimeError(f"task failed: {err.get('code')}: {err.get('message')}")
time.sleep(poll_interval) # any non-terminal status = still running
raise TimeoutError(f"task {task_id} not finished after {timeout_s}s")
task_id = create_task(
"Un barco de papel flota por un desagüe lleno de agua de lluvia, las gotas formsan ondas en la superficie, "
"hojas de otoño giran y pasan flotando, cámara a baja altura cerca del agua, luz suave de día nublado"
)
print("task created:", task_id)
task = wait_task(task_id)
video_url = task["output"][0]["url"]
# Output URLs expire — download right away and store the bytes yourself.
video = requests.get(video_url, timeout=120).content
with open("ray32-clip.mp4", "wb") as f:
f.write(video)
print(f"saved ray32-clip.mp4 ({len(video) / 1e6:.1f} MB)")
Instala la dependencia con pip install requests, pon tu clave y tendrás un .mp4 en disco cuando termine la tarea. Las tareas de vídeo tardan minutos, no segundos — da a tu bucle de consulta un plazo generoso (en el ejemplo, 15 minutos) en lugar de suponer que responderá rápido.
Consideraciones para producción: callbacks, idempotencia y errores
Prefiere callbacks en lugar de consultas periódicas
Para cualquier uso que no sea un script, registra un webhook al crear la tarea en lugar de consultar periódicamente:
{
"model": "ray-3.2/text-to-video",
"input": { "prompt": "...", "duration": 10, "resolution": "720p" },
"callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}
Con "when": "final", hiapi llamará a tu endpoint una vez cuando la tarea alcance un estado terminal — sin tráfico de consultas ni notificaciones de finalización perdidas entre dos intervalos. Tu manejador ejecuta después GET /v1/tasks/<id> (o lee el cuerpo de la respuesta entregada) para obtener output[0].url. Las consultas periódicas siguen siendo válidas para CLI y notebooks que no pueden exponer un endpoint HTTPS.
Haz que los reintentos sean idempotentes
Persiste taskId en el momento en que la llamada de creación devuelve la respuesta. Si tu proceso falla a mitad de la generación, recupéralo consultando el id almacenado — no vuelvas a crear la tarea, o pagarás por una segunda renderización. Vuelve a crearla solo si la solicitud de creación original falló antes de devolver el id de tarea. Como Ray 3.2 se cobra por clip y los clips en alta resolución no son baratos, duplicar la creación es un error de coste real, no solo una pérdida de tiempo.
Errores que realmente verás
Cada fila de la siguiente tabla es una forma de respuesta verificada en producción:
| Síntoma | Respuesta | Solución |
|---|---|---|
| Clave ausente/no válida | 401, anidado {"error":{"code":"permission_denied","type":"hiapi_error","request_id":"..."}} |
Comprueba la cabecera Authorization: Bearer sk-... y que la clave tenga permiso de acceso. Proporciona el request_id al contactar con soporte. |
| Valor de enumeración incorrecto | 400 INVALID_REQUEST, plano {"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: <field>: value must be one of ..."} |
Lee el campo indicado en message; enumera el conjunto exacto de valores permitidos. |
duration enviado como cadena |
400 INVALID_REQUEST |
Envía el entero sin comillas 5 o 10, no "5". |
| Id de modelo desconocido | 400, error_code: "MODEL_UNAVAILABLE", "model not available via /v1/tasks" |
Usa exactamente ray-3.2/text-to-video — consulta el id canónico en la página del modelo. |
| Id de tarea incorrecto al consultar | 404, plano {"code":404,"data":null,"message":"task not found"} (nota: en 404 no hay error_code) |
El id es incorrecto, o consultaste con la clave de otra cuenta. |
status: "fail" al consultar |
Estado terminal de la tarea, con detalles en data.error |
Lee data.error.code / data.error.message. Las tareas fallidas se reembolsan; aquí aparecen las marcas de políticas de contenido y los problemas de capacidad del proveedor — para marcas de contenido, reintenta con un prompt reformulado. |
Recursos relacionados
- Página del modelo de texto a vídeo Ray 3.2 — pruébalo en línea, estado en tiempo real y tabla de precios completa
- API de imagen a vídeo Ray 3.2 — la misma familia de modelos, pero anima un fotograma de referencia en lugar de partir de texto (el esquema de entrada es distinto — sondea antes de reutilizar este cuerpo de solicitud)
- Cómo usar la API de texto a vídeo de Hailuo 2.3 — una receta similar cuyo
durationes una cadena, un contraste útil frente al entero de Ray - Precios de hiapi — tarifas de vídeo por clip desglosadas por resolución y duración
Preguntas frecuentes
¿Cuál es el id del modelo y qué endpoint debo llamar?
ray-3.2/text-to-video, enviado como model de nivel superior a POST https://api.hiapi.ai/v1/tasks, con los parámetros bajo input. Es asíncrono — recibirás un taskId y luego consultarás GET /v1/tasks/<id> (o registrarás un callback).
¿duration está en segundos y por qué falla "5"?
Los clips duran 5 o 10 segundos, y el campo es un entero: envía 5 o 10. Una cadena entre comillas, o cualquier otro número como 7, falla con 400 INVALID_REQUEST: duration: value must be one of 5, 10.
¿Puedo establecer la resolución y la relación de aspecto?
Sí — precisamente en esto se diferencia Ray 3.2 de los modelos de vídeo que solo admiten prompt. resolution es 540p, 720p o 1080p; aspect_ratio es uno de 16:9, 9:16, 1:1, 4:3, 3:4, 21:9. Ambos son opcionales (por defecto 540p y 16:9) y ambos rechazan los valores fuera de rango con un 400.
¿Cuánto cuesta un clip?
Ray 3.2 se cobra por clip, y la resolución es la palanca más importante — con la misma duración, un clip en 1080p cuesta varias veces más que uno en 540p, y un clip de 10 segundos cuesta aproximadamente tres veces más que uno de 5 segundos. Las tarifas cambian, así que consulta la página de precios en lugar de codificar un número fijo, y haz prototipos en 540p/5 segundos.
¿Los clips tienen audio? No — el texto a vídeo de Ray 3.2 devuelve un MP4 sin audio. Si necesitas sonido, añádelo en posproducción o usa un modelo de audio aparte.
¿Ray 3.2 admite imagen a vídeo?
Sí, como un id de modelo independiente en la misma API de tareas: ray-3.2/image-to-video. Su esquema de entrada es distinto (recibe una imagen de referencia), así que sondea antes de reutilizar este cuerpo de solicitud — consulta la página del modelo de imagen a vídeo.
Mi generación va lenta o parece atascada. ¿Qué es normal?
Las tareas de vídeo tardan varios minutos. Mantén los valores no terminales de data.status en tu bucle de consulta y espera — reserva un plazo de 10–15 minutos, o usa un callback con "when": "final" para que hiapi te notifique en el estado terminal en lugar de que tú adivines un intervalo.
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