Un descuento en los planes premium al registrarteConsigue tu descuento

blog/developers·3 sept 2026·7 min·por el equipo de eroq

Escalar la generación de video con IA: colas y reintentos

Cómo generar video con IA a gran volumen: trabajos asíncronos, cola propia, concurrencia y límites, reembolsos, varias claves y webhooks firmados.


Un clip es una demo. Mil clips a la semana son un problema de operaciones, y lo que se rompe nunca es lo que aparece en la ficha del modelo. Es el proceso que se reinició a mitad de un render, el cliente al que se le cobró un trabajo que falló y el lote que se comió el límite de solicitudes que necesitaba tu producto en vivo.

Así es un pipeline de video que aguanta el volumen. Las formas son las de eroq, documentadas en /docs/video, pero el razonamiento sirve para cualquier API de medios asíncrona.

Envía, no esperes

Un render de video tarda de uno a cinco minutos. Ninguna solicitud HTTP debería quedarse abierta tanto tiempo, así que el endpoint es asíncrono: la llamada cobra, lanza el render y responde 202 al instante.

{
  "id": "b7e6c2d4-…",
  "object": "video.generation",
  "status": "processing",
  "created": 1756118400,
  "model": "eroq-motion-one",
  "duration": "5s",
  "poll": "/v1/videos/generations/b7e6c2d4-…",
  "usage": { "credits_spent": 100, "credits_remaining": 887 }
}

Guarda ese id antes de hacer cualquier otra cosa: antes de responder a quien te llamó, antes de escribir en el log. El id del trabajo es lo único que te conecta con un render que ya pagaste, y todo lo que sigue da por hecho que está en tu base de datos y no en una variable de una máquina que podría reiniciarse.

Una cola propia

Necesitas una, y no debería ser la cola de la API.

La demanda de tus usuarios llega a ráfagas; la capacidad de render, no. Sin un búfer te quedan dos malas opciones: rechazar trabajo en el pico o lanzar solicitudes en paralelo hasta que el limitador las rechace. Una cola convierte las dos en una decisión de planificación.

La tabla mínima viable tiene cinco columnas que importan:

  • el id del trabajo que devuelve el envío, y el estado tal como lo viste por última vez
  • el payload de la solicitud, para que un reenvío sea exactamente el mismo render
  • el número de intentos, para que un trabajo que siempre falla se detenga tras tres intentos en lugar de seguir para siempre
  • el cliente o la campaña a la que pertenece, para que el costo caiga en la cuenta correcta
  • los créditos gastados, copiados de usage.credits_spent en el momento del envío

Después, un worker que vacía la cola a un ritmo fijo, y un segundo bucle que consulta GET /v1/videos/generations/{id} para todo lo que siga en proceso.

Para recuperarte de una caída hay un endpoint de listado. GET /v1/videos/generations devuelve todos los trabajos que el espacio de trabajo envió en las últimas 24 horas (incluidos los renders de tus compañeros de equipo), con limit y un filtro status. Nunca incluye medios en línea, así que recorrerlo en bucle sale barato.

# what is still in flight right now
curl "https://eroq.ai/v1/videos/generations?status=processing&limit=50" \
  -H "Authorization: Bearer $EROQ_API_KEY"

Si tu base de datos y esa lista no coinciden, la lista tiene razón.

Dimensionar la concurrencia según los límites de solicitudes

Son dos números distintos, y confundirlos es el error habitual.

El ritmo de envío lo limita la API: 6 solicitudes de video por minuto y por clave, multiplicadas según tu plan (2× Creator, 4× Studio, 6× Team, 8× Agency). Modélalo con un token bucket en tu worker.

Los renders en curso no los limita nada salvo tu paciencia y tu bolsillo. Ponles un tope fijo de todos modos: un techo de renders en curso es lo que impide que un bucle de reintentos desbocado gaste un mes de créditos en una tarde, y te da un número sobre el que poner alertas.

Ajusta el bucket justo por debajo del límite efectivo y deja que la cola absorba la diferencia: gestionar un 429 importa menos cuando casi nunca lo provocas.

Reintentos, reembolsos y lo que no hay que reintentar

El modelo de cobro hace que reintentar sea seguro: los créditos se cobran al enviar, y un render que falla (o que no termina en diez minutos) se reembolsa solo. Concilias clips entregados, no intentos.

Dos garantías más. Una solicitud que bloquea la política de contenido devuelve content_blocked y nunca se cobra. Y las comprobaciones de acceso ocurren antes del cargo: un modelo que tu plan no incluye responde 403 plan_required y un motor que no está disponible en ese momento responde 503 model_unavailable, los dos antes de que se mueva un solo crédito, y ninguno pasa en silencio a otro motor. Obtienes el modelo que pediste o un error: el único comportamiento que mantiene tu salida coherente con tus logs.

Así que la política de reintentos se escribe sola:

  • Reintenta con backoff: 429, y los fallos de red en los que nunca llegaste a ver una respuesta.
  • Reintenta una vez y luego cambia de motor: 503 model_unavailable. Ten en la configuración un id de motor de respaldo, elegido por ti, del catálogo de /models.
  • No reintentes, muéstralo: 402 insufficient_credits, 403 plan_required, 403 role_forbidden y cualquier rechazo content_blocked. Ninguno cambia por sí solo dentro de una ventana de reintento.
  • No reintentes automáticamente un render fallido más de dos veces. Un prompt que falla dos veces suele fallar una tercera, y con los reembolsos el costo es la latencia, que es justo lo que está mirando tu cliente.

Un reenvío es un trabajo nuevo con un id nuevo, no la resurrección del anterior: enlaza los dos ids en tu tabla o tu contabilidad de costos por cliente se irá desviando.

Varias claves, no una

Los límites de solicitudes se cuentan por clave, así que las claves son la frontera natural del radio de impacto. Los planes incluyen varias justo para esto: 10 claves en Hobby, 20 en Creator, 50 en Studio, 100 en Team y 200 en Agency.

Un reparto sensato:

  • una clave por entorno (producción, staging, local),
  • una clave por pipeline en producción: la ruta interactiva en la que esperan los usuarios, la ruta de lotes nocturnos y la de herramientas internas,
  • y, si revendes generación, una por cada cliente grande, para que su uso se lea con claridad en el registro de solicitudes.

Las claves también heredan el rol en el espacio de trabajo del miembro que las tiene, así que dar de baja a alguien es un cambio de rol y no una auditoría de claves. La pestaña Miembros está en /dashboard/workspace; las cuentas del costo por cliente están en precios por créditos para APIs de IA.

Webhooks en lugar de polling

El polling está bien con diez renders por hora y es un desperdicio con mil. Registra un endpoint y recibe video.generation.succeeded y video.generation.failed en su lugar.

El payload es un sobre pequeño y firmado, sin megabytes de base64 atravesando tu balanceador de carga:

{
  "id": "evt_9f21…",
  "type": "video.generation.succeeded",
  "created": 1756118400,
  "data": {
    "id": "b7e6c2d4-…",
    "model": "eroq-motion-one",
    "duration": "5s",
    "content_type": "video/mp4",
    "result_url": "https://store.eroq.ai/…/clip.mp4"
  }
}

Cada entrega lleva una cabecera eroq-signature al estilo de Stripe con la forma t=<unix>,v1=<hex>, donde el hex es un HMAC-SHA256 de timestamp.body con el secreto del endpoint, que se muestra una sola vez al crearlo. Verifícala con una comparación en tiempo constante antes de fiarte de un solo byte, y rechaza las marcas de tiempo con más de unos minutos de antigüedad para acabar con los ataques de repetición.

Tres reglas para el handler que te ahorran incidentes:

  1. Responde 2xx rápido, trabaja después. Confirma, encola, devuelve. Un handler que genera una miniatura en línea acabará agotando el tiempo de espera y hará que la entrega parezca fallida.
  2. Trata la entrega como «al menos una vez». Indexa el procesamiento por el id del trabajo y hazlo idempotente.
  3. Mantén el polling como red mínima. Un barrido de los trabajos que tu base de datos todavía cree en proceso atrapa lo que se le haya escapado a un webhook. Y result_url es null cuando un clip volvió en línea en lugar de almacenado, así que contempla esa rama.

Lotes: deja que la secuencia sea un recurso

Si renderizas secuencias en lugar de piezas sueltas, modela la secuencia en el servidor en vez de orquestar doce envíos. El recurso films de eroq guarda un storyboard, y POST /v1/films/{id}/render rueda todas las escenas en una sola llamada: cada escena se factura por separado, la cola es secuencial (así que un saldo vacío detiene la ejecución limpiamente) y la respuesta indica trabajo o error para cada escena. Los detalles, en /docs/films.

Prueba el pipeline con un motor barato antes que con uno caro: Seedance 1.0 Lite, a 120 créditos por cinco segundos, hace asequible un ensayo de principio a fin, y tu cola no nota la diferencia. Lo que incluye cada plan está en /pricing.

Preguntas frecuentes

¿Pago por los renders de video con IA que fallan?

No. Los créditos se cobran al enviar, y un render que falla, o que no termina en diez minutos, se reembolsa solo. Las solicitudes que bloquea la política de contenido devuelven content_blocked y nunca se cobran.

¿Cómo recupero los trabajos de video después de que se reinicie mi servidor?

Llama a GET /v1/videos/generations, que lista con su estado todos los trabajos que el espacio de trabajo envió en las últimas 24 horas. Concílialos con tu propia tabla y trata la lista de la API como la fuente de verdad.

¿Hago polling o uso webhooks para generar video?

Haz polling mientras construyes y con poco volumen, y pasa a los webhooks video.generation.succeeded y video.generation.failed cuando renderices sin parar. En cualquier caso, mantén un barrido de polling lento como red de seguridad.

Construye primero la cola y después el pipeline: la referencia del endpoint está en /docs/video.

Etiquetasvideo-apiqueueswebhooksproductionscaling

Crea esto con los modelos detrás del artículo: empieza con 50 créditos gratis o explora todos los motores y sus precios.