Tutorial
API de texto a video PixVerse V6: un ejemplo curl y Python que funciona
- Modelo
- pixverse-v6
Usar pixverse-v6/text-to-video para generar un vídeo en hiapi consiste, en esencia, en un POST /v1/tasks más una ronda de sondeo: exactamente el mismo patrón de tareas asíncronas que usa cada modelo generativo de la plataforma. Este artículo te ofrece ejemplos curl y Python listos para copiar, los campos de entrada que el endpoint realmente valida, y el ciclo de vida de la tarea y los tropiezos que registré cuando ejecuté una generación completa con éxito.
Qué necesitas antes de empezar
- Una API key de hiapi. Créala en la consola y envíala con la cabecera
Authorization: Bearer sk-<tu key>. El formato de la cabecera está en la documentación de autenticación. - El model id es
pixverse-v6/text-to-video. Conserva la barra intermedia: forma parte del id, no es una ruta de URL. - curl no requiere instalar nada; el ejemplo de Python solo usa la biblioteca estándar más
requests.
PixVerse V6 texto a video convierte un prompt de texto en un clip de vídeo de 1 a 15 segundos, de 360p a 1080p. Funciona con la interfaz unificada de tareas asíncronas: enviar la tarea → sondear el resultado → descargar el vídeo antes de que caduque la URL de salida.
Petición mínima funcional (curl)
Envía la tarea a POST /v1/tasks:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<tu key>" \
-H "Content-Type: application/json" \
-d '{
"model": "pixverse-v6/text-to-video",
"input": {
"prompt": "Bajo la luz del sol de la mañana, un zorro rojo trota a través de la nieve recién caída, calidad cinematográfica, cámara lenta",
"aspect_ratio": "16:9",
"duration": 5,
"resolution": "720p"
}
}'
La respuesta incluye un id de tarea:
{ "code": 200, "data": { "taskId": "tk-hiapi-...." }, "message": "success" }
Después sondea GET /v1/tasks/<taskId> hasta que data.status cambie a success:
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-.... \
-H "Authorization: Bearer sk-<tu key>"
Si tiene éxito, la URL del vídeo final está en data.output[0].url:
{
"code": 200,
"data": {
"status": "success",
"storage": "temp",
"output": [
{ "type": "video", "url": "https://temp.hiapi.ai/....mp4", "expireAt": 1793036664 }
]
},
"message": "success"
}
Esta URL de salida es temporal: lleva un timestamp expireAt (en la prueba real, 7 días tras la generación). Descarga el .mp4 de inmediato y guárdalo en tu propio almacenamiento de objetos; no enlaces directamente esta dirección.
El mismo flujo (Python)
import time, requests
BASE = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": "Bearer sk-<tu key>", "Content-Type": "application/json"}
def create_task():
payload = {
"model": "pixverse-v6/text-to-video",
"input": {
"prompt": "Bajo la luz del sol de la mañana, un zorro rojo trota a través de la nieve recién caída, calidad cinematográfica, cámara lenta",
"aspect_ratio": "16:9",
"duration": 5,
"resolution": "720p",
},
}
r = requests.post(BASE, json=payload, headers=HEADERS, timeout=60)
r.raise_for_status()
data = r.json()
task_id = (data.get("data") or {}).get("taskId")
if not task_id:
raise RuntimeError(f"Error al crear: {data}")
return task_id
def wait_for(task_id, timeout_s=600):
deadline = time.time() + timeout_s
while time.time() < deadline:
try:
r = requests.get(f"{BASE}/{task_id}", headers=HEADERS, timeout=30)
task = (r.json().get("data") or {})
except requests.RequestException:
time.sleep(5) # un fallo de red aislado no debe abortar todo el sondeo
continue
status = task.get("status")
if status == "success":
return task["output"][0]["url"]
if status == "fail":
raise RuntimeError(f"Tarea fallida: {task.get('error')}")
time.sleep(5) # aún en cola/renderizando: sigue sondeando con el mismo task_id
raise TimeoutError(task_id)
task_id = create_task()
url = wait_for(task_id)
video = requests.get(url, timeout=120).content
open("out.mp4", "wb").write(video)
print("out.mp4 guardado")
El bucle de sondeo solo distingue los dos estados terminales success y fail; ante cualquier otro estado sigue esperando. Así, tanto si durante el renderizado se informa handling como archiving, el código no se equivoca. Clave: si una petición de sondeo concreta falla, espera un momento y vuelve a consultar con el mismo task_id guardado; no reenvíes la tarea, porque reenviarla volvería a renderizar y a cobrar.
Los parámetros que pixverse-v6/text-to-video realmente valida
Estos son los campos que el endpoint acepta de verdad. Cada uno lo confirmé enviándole un valor ilegal y leyendo su propio error de validación, y lo contrasté con la documentación del modelo:
| Campo | Tipo | Valores / rango | Valor por defecto | Descripción |
|---|---|---|---|---|
prompt |
string | 1–2048 bytes UTF-8 (1 carácter chino = 3 bytes, aprox. 682 caracteres) | — | Obligatorio. |
aspect_ratio |
enum | 16:9, 4:3, 1:1, 3:4, 9:16, 2:3, 3:2, 21:9 |
16:9 |
Ojo: admite 21:9 (ultrapanorámico) y 2:3/3:2. |
duration |
integer | 1–15 |
5 |
En segundos; debe ser un entero, no una cadena. |
resolution |
enum | 360p, 540p, 720p, 1080p |
720p |
Resolución de salida. |
generate_audio |
boolean | true / false |
false |
Si se genera también música de fondo, efectos de sonido y diálogo; desactivado por defecto, hay que activarlo explícitamente para tener audio. |
negative_prompt |
string | máximo 2048 bytes UTF-8 | — | Opcional; contenido que quieres evitar. |
seed |
integer | cualquier entero | — | Opcional; mismo seed + mismo prompt reproducen el mismo resultado. |
style |
enum | anime, 3d_animation, clay, comic, cyberpunk |
ninguno | Preset de estilo opcional; déjalo vacío para mantener el estilo natural. |
Si quieres usar callback en lugar de sondeo, junto a input puedes incluir un objeto callback: url (dirección de callback HTTPS) + when (por ahora solo final, es decir, callback al alcanzar el estado terminal de la tarea).
Cómo escribirlo en producción
- Es un schema cerrado. Cualquier campo que no esté en la tabla anterior se rechaza directamente con
invalid input: <root>: additional properties '<campo>' not allowed. En realidad es algo bueno: un campo mal escrito o inventado falla de forma ruidosa en el momento del envío en lugar de ignorarse en silencio. durationes un entero, a diferencia de algunos modelos de vídeo de la plataforma que aceptan cadenas ("6"). Enviar"5"produceinvalid input: duration: got string, want integer; un valor fuera de rango te indica el límite directamente, por ejemploduration: maximum: got 999, want 15oduration: minimum: got 0, want 1.- Cada error señala un solo campo, y el envelope es plano:
{"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: aspect_ratio: value must be one of ..."}. Corrige el campo indicado y reenvía; el siguiente campo ilegal (si lo hay) aparecerá en la siguiente llamada. - Cuando una tarea falla, el HTTP sigue siendo 200. La información del fallo está en
data.status: "fail"del cuerpo de la respuesta; lo que hay que evaluar es ese campo status, no el código de estado HTTP. - ¿Sondeo o callback? Para scripts ligeros, el sondeo es lo más simple; si vas a escalar y no quieres mantener conexiones abiertas, usa
callback. En cualquier caso, en cuanto obtengasoutput[0].url, descarga de inmediato: la URL llevaexpireAty, al caducar, devuelve 404.
Registro de la prueba: lo ejecuté de verdad
Este artículo no se limita a sondas de solo lectura: ejecuté el ciclo de vida completo con una petición real, y este es el registro:
- Creación: al enviar la petición anterior de 720p / 5 segundos / 16:9, devolvió de inmediato
{"code":200,"data":{"taskId":"tk-hiapi-01M5AM..."},"message":"success"}. Fíjate en que el taskId es una cadena con prefijotk-hiapi-, no un UUID. - Sondeo: el
data.statusdeGET /v1/tasks/<id>pasó porhandling→archiving→success; el clip de 5 segundos tardó unos 74 segundos desde la creación hasta la finalización. - Salida: en el estado terminal,
data.output[0]era{"type":"video","url":"https://temp.hiapi.ai/....mp4","expireAt":...,"artifactId":"...","storage":"temp"}.expireAtmenos la hora de creación da exactamente 604800 segundos, es decir, 7 días. - Producto: un HEAD a esa URL de salida devolvió
content-type: video/mp4y un tamaño de unos 2,2 MB, lo que confirma que es un archivo mp4 válido.
Hay una corrección sobre el audio que merece una nota aparte: probé varios nombres de campo adyacentes con valores ilegales. generate_audio devolvió got string, want boolean (lo que indica que es un campo booleano válido), mientras que sound y audio devolvieron additional properties not allowed (no existen). Es decir, el audio se controla con el interruptor generate_audio, que por defecto es false; la página de precios tiene en consecuencia dos niveles, «Sound On / Sound Off». En esta prueba no activé generate_audio, así que el producto fue un clip sin sonido.
No hice una valoración subjetiva de la calidad visual (en esta prueba solo verifiqué el contrato de la petición, el ciclo de vida de la tarea y que el producto es un mp4 válido); esa parte te toca juzgarla a ti tras generar con tus propios prompts.
Cómo se calcula el coste
pixverse-v6/text-to-video se factura por segundos (el factor de facturación es duration): cuanto más largo, más caro; a mayor resolución, y con generate_audio activado en el nivel «Sound On», el precio por segundo será mayor. La tarifa por segundo concreta se rige por los datos en tiempo real de la página de precios; échale un vistazo antes de generar en lote.
Enlaces relacionados
- PixVerse V6 texto a video — página del modelo
- PixVerse V6 texto a video — documentación de la API
- Referencia de las interfaces Crear tarea y Consultar detalles de la tarea
- Inicio rápido y Autenticación
- ¿Quieres hacer un vídeo a partir de una imagen? Mira PixVerse V6 imagen a video
- Precios
Preguntas frecuentes
¿duration está en segundos o en fotogramas? En segundos: cualquier entero entre 1 y 15, por defecto 5.
¿Se puede generar vídeo con sonido? Sí. Pon generate_audio en true y el modelo generará también música de fondo, efectos de sonido y diálogo; por defecto es false (sin sonido). Activar el audio pasa a un nivel de facturación más caro; consulta la página de precios.
¿Cuánto puede medir el prompt? El límite es de 2048 bytes UTF-8. Un carácter chino cuenta como 3 bytes, así que el tope es de unos 682 caracteres chinos.
¿En qué se diferencia de imagen a video? Este endpoint (pixverse-v6/text-to-video) parte únicamente de un prompt de texto. Imagen a video es otro model id: recibe una imagen inicial y la anima. Si quieres animar una imagen existente en lugar de generar desde cero, usa ese.
¿Por qué mi petición devuelve "additional properties not allowed"? Has enviado un campo que no está en el schema. La entrada solo acepta prompt, aspect_ratio, duration, resolution, generate_audio, negative_prompt, seed y style.
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