Tutorial
API vidu-q3-ad reference-to-video: curl, Python y una solicitud que funciona
Qué vas a construir
Vas a hacer una llamada de reference-to-video sobre la API asíncrona /v1/tasks de hiapi, usando de 1 a 7 imágenes de producto para convertirlas en un anuncio corto con audio nativo. vidu-q3-ad/reference-to-video es el nivel de Vidu Q3 orientado a publicidad: subes tus imágenes de referencia del producto, escribes una descripción de la toma y el modelo devuelve un MP4 de 3 a 15 segundos con montaje inteligente, movimiento de cámara y efectos de sonido generados; no hay una pista de audio aparte que tengas que aportar.
Solo tienes que enviar un POST, luego hacer polling (o recibir un callback) hasta que la tarea termine y descargar el MP4. Primero obtén tu clave en la consola de hiapi; todas las solicitudes siguientes usan Authorization: Bearer sk-....
Tres datos que debes copiar tal cual:
- Endpoint de envío:
POST https://api.hiapi.ai/v1/tasks - Endpoint de polling:
GET https://api.hiapi.ai/v1/tasks/{taskId} - ID del modelo:
vidu-q3-ad/reference-to-video: ni la barra ni el sufijo-adson opcionales;vidu-q3-mixyvidu-q4-previewson endpoints distintos.
Esquema de entrada (obtenido mediante sondeo en vivo)
| Campo | ¿Obligatorio? | Tipo | Descripción |
|---|---|---|---|
prompt |
Sí | string | Descripción de la toma; usa números de orden para referirte a los productos, por ejemplo "el reloj de la imagen de referencia 1" |
reference_image_urls |
Sí | array, 1–7 | URL pública de la imagen estática del producto (la descarga el servidor) |
duration |
Sí | integer, 3–15 | Número de segundos; debe ser entero (4.5 se rechaza) |
size |
No | enum (10 valores) | Determina a la vez la relación de aspecto y el nivel 720/1080; por defecto 1280*720 |
El validador es muy estricto: cualquier campo fuera de la lista anterior se rechaza directamente. Hay dos tipos de rechazo que conviene señalar, porque hacen tropezar a todo el que migra desde otros modelos de vídeo de hiapi:
aspect_ratioyresolutionse rechazan ambos. Casi todos los demás endpoints de vídeo de hiapi usan estos dos campos; q3-ad no. El tamaño solo se controla consize.reference_audio_urlstambién se rechaza. A diferencia del nivel reference-to-video de Vidu Q4 (que puede recibir fragmentos de voz), q3-ad no tiene entrada de audio; su banda sonora se genera de forma nativa. (Cómo lo confirmé, en la sección de verificación más abajo.)
Cómo elegir size
size es una cadena width*height de un conjunto fijo. Puedes leerlo como una cuadrícula de cinco relaciones de aspecto × dos niveles de calidad:
| Relación de aspecto | Nivel 720 | Nivel 1080 |
|---|---|---|
| 16:9 horizontal | 1280*720 |
1920*1080 |
| 9:16 vertical | 720*1280 |
1080*1920 |
| 1:1 cuadrado | 1280*1280 |
1808*1808 |
| 4:3 | 1280*960 |
1920*1440 |
| 3:4 | 960*1280 |
1440*1920 |
Elige la fila según dónde se vaya a publicar (vertical 720*1280 / 1080*1920 para Reels y TikTok, fila horizontal para anuncios in-stream de YouTube) y la columna según el equilibrio entre calidad y presupuesto. La facturación se calcula por segundos de salida y el nivel determina el precio por segundo; consulta precios para más detalles; la facturación mínima es un clip de 3 segundos.
Solicitud mínima (curl)
Dos pasos: enviar y luego hacer polling.
# 1) Enviar: devuelve un taskId
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "vidu-q3-ad/reference-to-video",
"input": {
"prompt": "Un anuncio del smartwatch de titanio de la imagen de referencia 1: gira lentamente sobre un pedestal oscuro y reflectante mientras una franja de luz dura barre la caja cepillada de izquierda a derecha, la pantalla permanece apagada, la cámara mantiene un ángulo de héroe bajo. Iluminación comercial, alto contraste, detalle macro.",
"reference_image_urls": [
"https://your-cdn.com/watch-front.png",
"https://your-cdn.com/watch-side.png"
],
"duration": 5,
"size": "1280*720"
}
}'
# -> {"data":{"taskId":"..."}}
# 2) Hacer polling hasta que data.status sea "success" o "fail"
curl -s https://api.hiapi.ai/v1/tasks/THE_TASK_ID \
-H "Authorization: Bearer sk-your-key"
# Éxito -> data.status == "success", y data.output[0].url es el MP4
Las URL de las imágenes de referencia deben ser de acceso público: la tarea las descarga en el servidor, así que localhost o URL firmadas que caducan a los pocos segundos no sirven.
Python: enviar, hacer polling, descargar
import time
import requests # o usa urllib/subprocess para mantener cero dependencias
API = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": "Bearer sk-your-key", "Content-Type": "application/json"}
def submit():
payload = {
"model": "vidu-q3-ad/reference-to-video",
"input": {
"prompt": "Toma publicitaria de la zapatilla de la imagen de referencia 1 girando sobre un plato giratorio iluminado, "
"acercamiento lento, luz de contorno suave, encuadre de héroe de producto.",
"reference_image_urls": ["https://your-cdn.com/sneaker.png"],
"duration": 5,
"size": "1080*1920", # vertical 1080p, para Reels / TikTok
},
}
body = requests.post(API, headers=HEADERS, json=payload, timeout=60).json()
task_id = (body.get("data") or {}).get("taskId")
if not task_id:
raise RuntimeError(f"envío rechazado: {body}") # un 400 acaba aquí
return task_id
def wait(task_id, timeout_s=600, interval=5):
deadline = time.time() + timeout_s
while time.time() < deadline:
data = requests.get(f"{API}/{task_id}", headers=HEADERS, timeout=30).json().get("data") or {}
status = data.get("status")
if status == "success":
return data["output"][0]["url"]
if status == "fail":
raise RuntimeError(f"tarea fallida: {data.get('error')}")
time.sleep(interval)
raise TimeoutError(task_id)
url = wait(submit())
mp4 = requests.get(url, timeout=120).content # descarga de inmediato: la URL de salida caduca
open("ad.mp4", "wb").write(mp4)
El renderizado de Q3-ad suele completarse en uno o dos minutos; un intervalo de polling de 5 segundos es más que suficiente. La URL de salida es temporal (lleva expireAt), así que descarga los bytes en cuanto la tarea tenga éxito, en lugar de enlazarla en caliente en una página.
Patrón para producción
Usa callbacks en lugar de polling. En un escenario de servidor, configura un callback de nivel superior para no tener que mantener un bucle de polling:
{
"model": "vidu-q3-ad/reference-to-video",
"input": { "prompt": "...", "reference_image_urls": ["..."], "duration": 5 },
"callback": { "url": "https://your-app.com/hooks/hiapi", "when": "final" }
}
when solo admite "final": el hook se dispara una vez cuando la tarea entra en estado terminal y su cuerpo de solicitud es el mismo payload que devuelve GET /v1/tasks/{taskId}. El hook puede reintentarse, así que deduplica por taskId y mantén un polling lento como red de seguridad por si se pierde alguna entrega.
Bifurca según el cuerpo de la respuesta, no según el código de estado HTTP. Un fallo de renderizado sigue devolviendo HTTP 200; la información del fallo está en data.status == "fail" y los detalles en data.error. Solo los rechazos a nivel de solicitud (campos incorrectos, clave ausente, no autenticado) usan 4xx y devuelven {"code":400,"error_code":"INVALID_REQUEST","message":"...","data":null}.
Idempotencia. Cada envío crea una nueva tarea facturable, así que guarda el taskId en el instante en que lo recibas y, al reintentar, vuelve a hacer polling de ese ID. Nunca reenvíes por que una sola solicitud de polling agote el tiempo de espera.
Qué verifiqué
Sondeé el endpoint en vivo con valores no válidos, que se rechazan gratis en la fase de validación: no se crea ninguna tarea y no se factura nada (los valores no válidos fallan antes de que empiece cualquier renderizado, lo que es más seguro que omitir un campo). Las respuestas reales son el esquema:
- Cuerpo de solicitud casi vacío →
reference_image_urls: missing required field ...; duration: missing required field .... Por tanto,prompt,reference_image_urlsydurationson obligatorios. duration: 99999→maximum: got 99,999, want 15;duration: 1→minimum: got 1, want 3;duration: 4.5→got number, want integer. Duration es un entero de 3 a 15.reference_image_urlscon 8 elementos →maxItems: got 8, want 7. El máximo es 7.size: "9000p"→value must be one of '1280*720', '1280*960', '1280*1280', '960*1280', '720*1280', '1920*1080', '1920*1440', '1808*1808', '1440*1920', '1080*1920'.aspect_ratioyresolution→ ambos devuelvenadditional properties ... not allowed. No son campos de q3-ad; usasize.reference_audio_urlsenviado como un array con formato correcto →additional properties 'reference_audio_urls' not allowed. (Si se envía como algo que no es un array, primero se dispara una comprobación de medios aparte: "must be an array", lo cual es engañoso: el campo realmente no está en el esquema). Este nivel no tiene entrada de referencia de voz; el audio se genera de forma nativa.
Esta es una referencia de integración, así que no incluye afirmaciones sobre la calidad de imagen: no ejecuté ningún renderizado de pago para este artículo. Sobre cómo es un output[0].url exitoso, la página del modelo más abajo aloja el ejemplo propio de este endpoint: un MP4 real con pista de audio.
Guías relacionadas
- Página del modelo Vidu Q3 Ad reference-to-video: especificaciones, prompts de anuncio de ejemplo y un cuadro de prueba en línea.
- Guía de la API Vidu Q4 reference-to-video: nivel superior: hasta 4K, 1–15 imágenes de referencia y el campo de voz
reference_audio_urlsque q3-ad no tiene. - Guía de la API Seedance 2.5 reference-to-video: otro endpoint reference-to-video, útil para comparar modelos.
- Prompts de movimientos de cámara para vídeo con IA: lenguaje de cámara útil para el campo
prompt. - Inicio rápido de hiapi y precios.
Preguntas frecuentes
¿Cómo hago que el clip sea vertical u horizontal? Con size, no con aspect_ratio. Para vertical usa 720*1280 o 1080*1920; para horizontal, 1280*720 o 1920*1080.
¿Puedo aportar una voz en off o una referencia de sonido? En este nivel no: reference_audio_urls se rechaza. Q3-ad genera su propio audio nativo. Si necesitas usar un fragmento de referencia para guiar el audio, usa el nivel reference-to-video de Vidu Q4.
¿Cuántas imágenes de producto puedo enviar? De una a siete, en reference_image_urls, y referéncialas por número en el prompt ("la botella de la imagen de referencia 2").
Mi tarea devolvió 200 pero no hay vídeo, ¿por qué? Mira data.status. "fail" devuelve HTTP 200 y el motivo está en data.error; solo las solicitudes mal formadas reciben 4xx.
¿Cuánto cuesta? Se factura por segundos de salida y el nivel de size (nivel 720 vs nivel 1080) determina el precio por segundo. El mínimo es un clip de 3 segundos. Consulta las tarifas actuales en precios.
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