Tutorial
API de imagen a video de PixVerse V6: ejemplos de curl y Python listos para ejecutar
- Modelo
- pixverse-v6
Animar una imagen estática con pixverse-v6/image-to-video en hiapi requiere una sola llamada POST /v1/tasks más un sondeo: exactamente el mismo patrón de tareas asíncronas que usan todos los modelos generativos de la plataforma. Se diferencia del endpoint de texto a video en la entrada: proporcionas un fotograma inicial (image_url) junto con un prompt que describe cómo debe moverse. Esta guía ofrece ejemplos de curl y Python listos para copiar y pegar, los campos de entrada que este endpoint valida realmente y una sorpresa que hace tropezar a quienes vienen del texto a video.
Qué necesitas
- Una clave de API de hiapi. Créala en la consola y envíala mediante
Authorization: Bearer sk-<your-key>. Consulta la documentación de autenticación para el formato de la cabecera. - El ID de modelo
pixverse-v6/image-to-video(conserva la barra: forma parte del ID, no es una ruta de URL). Es un ID de modelo distinto depixverse-v6/text-to-video. - Una URL de imagen de acceso público para el primer fotograma. La plataforma obtiene esa imagen en el servidor, así que los enlaces a
localhosto a buckets privados no funcionarán: aloja el primer fotograma en una ubicación accesible por GET para la API. - Con curl no hace falta instalar nada. Para el ejemplo de Python solo necesitas la biblioteca estándar y
requests.
PixVerse V6 imagen a video toma la imagen estática que proporcionas como fotograma inicial y la anima en un clip de 1 a 15 segundos. Funciona a través de la API unificada de tareas asíncronas: envías la tarea, sondeas el resultado y luego descargas la URL de salida antes de que caduque.
Solicitud mínima funcional (curl)
Envía la tarea a POST /v1/tasks. Tanto image_url como prompt son obligatorios:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "pixverse-v6/image-to-video",
"input": {
"image_url": "https://cdn.example.com/your-first-frame.jpg",
"prompt": "Slow push-in; the subject turns toward the camera and smiles, soft breeze in the hair",
"duration": 5,
"resolution": "720p"
}
}'
La respuesta devuelve un ID de tarea:
{ "code": 200, "data": { "taskId": "a1b2c3d4-...." } }
Luego sondea GET /v1/tasks/<taskId> hasta que data.status sea success:
curl -s https://api.hiapi.ai/v1/tasks/a1b2c3d4-.... \
-H "Authorization: Bearer sk-<your-key>"
En caso de éxito, la URL del video terminado está en data.output[0].url:
{
"code": 200,
"data": {
"status": "success",
"output": [{ "url": "https://temp.hiapi.ai/....mp4", "expireAt": 1760000000 }]
}
}
Esa URL de salida es temporal: incluye una marca de tiempo expireAt. Descarga el .mp4 de inmediato y guárdalo en tu propio bucket; no lo enlaces directamente en caliente.
El mismo flujo en Python
import time, requests
BASE = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": "Bearer sk-<your-key>", "Content-Type": "application/json"}
def create_task():
payload = {
"model": "pixverse-v6/image-to-video",
"input": {
"image_url": "https://cdn.example.com/your-first-frame.jpg",
"prompt": "Slow push-in; the subject turns toward the camera and smiles",
"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"create failed: {data}") # los 400 repiten el campo incorrecto exacto
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 transitorio 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"task failed: {task.get('error')}")
time.sleep(5) # aún pendiente: conserva el MISMO task_id y vuelve a sondear
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("saved out.mp4")
El bucle de sondeo comprueba explícitamente success y fail, y sigue esperando ante cualquier otro estado, por lo que maneja con robustez cualquier etiqueta de estado intermedio que la tarea reporte durante el renderizado. Si una solicitud de sondeo falla, duerme y vuelve a sondear el mismo task_id guardado en lugar de reenviar: reenviar genera un segundo costo de renderizado.
Parámetros que pixverse-v6/image-to-video valida realmente
Estos son los campos que el endpoint en producción acepta realmente. Los confirmé uno por uno enviando valores no válidos y leyendo los errores de validación, y luego los contrasté con la documentación del modelo:
| Campo | Tipo | Valores / rango | Descripción |
|---|---|---|---|
image_url |
string | Una URL de imagen pública | Obligatorio. Primer fotograma. Una sola cadena de URL, no un array. |
prompt |
string | — | Obligatorio. Describe el movimiento aplicado a ese fotograma. |
duration |
integer | 1–15, predeterminado 5 |
En segundos. Debe ser un entero, no una cadena. |
resolution |
string | 360p, 540p, 720p, 1080p |
Resolución de salida. |
negative_prompt |
string | — | Opcional; contenido que hay que evitar. |
seed |
integer | — | Opcional; reutilízalo para obtener resultados reproducibles. |
style |
string | anime, 3d_animation, clay, comic, cyberpunk |
Estilo predefinido opcional. |
generate_audio |
boolean | true / false, predeterminado false |
Opcional; activa o desactiva la pista de audio/ambiente generada. |
Aquí no hay campo aspect_ratio. Esta es la única diferencia real con las solicitudes de texto a video que quizá hayas visto, y conviene tenerla presente (ver más abajo).
Consideraciones para producción
- No hay
aspect_ratio: la geometría de salida sigue a tu imagen de entrada. El endpoint de texto a video acepta una enumeraciónaspect_ratio; este endpoint la rechaza. Como proporcionas el primer fotograma, el clip hereda las dimensiones de ese fotograma. Si necesitas un clip vertical 9:16, recorta o rellenaimage_urla 9:16 antes de enviar: no hay ningún control de proporción que lo sobrescriba. - La estructura de la solicitud es cerrada. Cualquier campo fuera de la tabla anterior se rechaza con el error
invalid input: <root>: additional properties '<field>' not allowed. Los campos mal escritos o inventados fallan ruidosamente al enviar, en lugar de ignorarse en silencio. Campos comofirst_frame,image,input_image,motion_modeycamera_movementse rechazan: la imagen inicial va enimage_urly el movimiento de cámara se escribe en el texto deprompt. durationes un entero, a diferencia de otros modelos de video de la plataforma que aceptan cadenas ("6"). Enviar"5"devuelveinvalid input: duration: got string, want integer. Los valores fuera de rango reportan directamente el límite, por ejemploduration: maximum: got 999, want 15.- Los errores se devuelven de un campo a la vez, con un sobre plano:
{"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: image_url: missing required field \"image_url\""}. Corrige el campo reportado y vuelve a enviar; el siguiente campo no válido (si lo hay) aparecerá en la siguiente llamada. - Una tarea fallida sigue devolviendo HTTP 200. La información del fallo está en el cuerpo de la respuesta, como
data.status: "fail": comprueba el campo de estado, no solo el código de estado HTTP. Una URL de imagen que la plataforma no puede obtener es una causa habitual, así que confirma primero que tu primer fotograma es público. - La facturación es por segundo y crece con la resolución y la duración, por lo que un clip de 1080p/15s cuesta mucho más que uno de 540p/3s. Consulta las tarifas actuales en la página de precios antes de generar en lote.
Qué verifiqué (y qué no)
Deduje el contrato de entrada enviando valores deliberadamente no válidos al endpoint /v1/tasks en producción de pixverse-v6/image-to-video y leyendo las respuestas de validación de la propia API: una sonda de esquema de solo lectura, sin callback ni costo de renderizado. Hallazgos concretos que conviene recordar:
image_urles el campo obligatorio del primer fotograma y es una sola cadena, no un array. Enviar un número devuelveimage_url: got number, want string; omitirlo devuelvemissing required field "image_url". Los campos alternativos que podrías suponer (first_frame,image,input_image) se rechazan como propiedades desconocidas.- Este endpoint rechaza
aspect_ratio(additional properties 'aspect_ratio' not allowed), aunque sea válido enpixverse-v6/text-to-video. Esta es la razón concreta por la que una solicitud de imagen a video se ve distinta de una de texto a video: la forma de la salida la determina tu fotograma, no un parámetro. generate_audioes un verdadero interruptor booleano (predeterminadofalse): enviar una cadena devuelvegenerate_audio: got string, want boolean. Si quieres audio, ponlo entrue; no se fuerza a activado ni a desactivado.
En esta sonda no rendericé ningún clip, así que esta guía no afirma nada sobre la calidad del movimiento ni sobre cuánto respeta el modelo el fotograma de entrada: solo documenta el contrato de solicitud exacto que aplica este endpoint.
Contenido relacionado
- PixVerse V6 imagen a video: página del modelo
- PixVerse V6 imagen a video: documentación de la API
- Referencia de crear tarea y obtener detalles de la tarea
- Autenticación
- Precios
- Otros tutoriales de imagen a video en hiapi: hailuo-2.3 y Grok Imagine 1.5
Preguntas frecuentes
¿Por qué la imagen a video no tiene aspect_ratio? Porque el clip comienza con tu fotograma de image_url y hereda sus dimensiones. Para controlar la proporción de salida, cambia el tamaño o recorta la imagen de entrada antes de enviar: el endpoint rechaza aspect_ratio como campo desconocido.
¿image_url acepta varias imágenes? No. En este endpoint es una sola cadena de URL; pasar un array provoca un fallo de validación. Solo establece el fotograma inicial.
¿duration se mide en segundos o en fotogramas? En segundos: cualquier entero de 1 a 15, predeterminado 5. Debe ser entero; la cadena "5" se rechaza.
¿En qué se diferencia del endpoint de texto a video? pixverse-v6/image-to-video anima el fotograma que proporcionas (image_url + prompt, sin aspect_ratio). pixverse-v6/text-to-video es un ID de modelo aparte, genera solo a partir del prompt y sí acepta aspect_ratio. Usa imagen a video cuando ya tengas la imagen fija inicial exacta que quieres animar.
¿Por qué falla mi solicitud con "additional properties not allowed"? Enviaste un campo que no existe en el esquema. La entrada solo acepta image_url, prompt, duration, resolution, negative_prompt, seed, style y generate_audio.
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