HiAPI
Blog

Tutorial

API de texto a video PixVerse V6: un ejemplo curl y Python que funciona

Oct 20, 2026
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.
  • duration es un entero, a diferencia de algunos modelos de vídeo de la plataforma que aceptan cadenas ("6"). Enviar "5" produce invalid input: duration: got string, want integer; un valor fuera de rango te indica el límite directamente, por ejemplo duration: maximum: got 999, want 15 o duration: 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 obtengas output[0].url, descarga de inmediato: la URL lleva expireAt y, 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 prefijo tk-hiapi-, no un UUID.
  • Sondeo: el data.status de GET /v1/tasks/<id> pasó por handling → 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"}. expireAt menos 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/mp4 y 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

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 →

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