The short version
Integra el flujo REST en unos diez minutos y trata el render como un trabajo asíncrono durable.
Este tutorial convierte una URL pública en un trabajo de motion graphics de 30 segundos y 16:9. Upload, create, status y download son fáciles de integrar. El render es asíncrono y no está garantizado en diez minutos. Nuestra prueba tardó mucho más, por eso el código persiste el ID y tolera fallos de red.
Review TapVid API and MCP access
01
Qué significa API de texto a video aquí
Text to video puede ser clip de prompt, animación de imagen, avatar o montaje. TapVid transforma fuentes existentes en videos informativos estructurados de varias escenas. La página y el brief aportan hechos, audiencia, duración y límites. Una escena puede regenerarse sin reconstruir el resto.
“En 10 minutos” describe integrar las cuatro operaciones REST, no garantiza el render. Create devuelve `202 Accepted`, el estado puede tardar y el export continúa después. El cliente guarda el ID y no duplica el trabajo si pierde conexión.
El video insertado es un ejemplo terminado de TapVid procedente de otro flujo verificado. No es el resultado final de esta prueba REST, porque el poller perdió la conexión antes de observar un estado terminal.
02
Conoce el contrato antes del código
El gateway es `https://api.tapvid.ai/api/public/v1`. Usa Bearer sobre HTTPS. Crea la clave en API Keys y léela en servidor con `process.env.TAPVID_API_KEY`. Nunca la pongas en frontend o repositorio.
- Usa REST base por HTTPS.
- Lee `process.env.TAPVID_API_KEY` en servidor.
- Envía URL o archivo, no ambos.
- Persiste `materialId` y `videoId` tras 202.
- Consulta según `pollAfterSeconds` con timeout reanudable.
| Campo | Límite | Nota |
|---|---|---|
| Archivo | 100 MB | multipart |
| URL | HTTPS, 4.096 caracteres | sin hosts internos |
| Materiales | 30 | guardar IDs |
| Prompt | 12.000 caracteres | fiel a fuente |
| Formato | `16:9` o `9:16` | enum exacto |
| Duración | `30s` a `5m` | enum exacto |
03
Paso 1: carga URL o archivo
Para URL envía JSON HTTPS; para archivo usa multipart hasta 100 MB. La URL admite 4.096 caracteres. Guarda `materialId` como estado privado.
export TAPVID_API_KEY="replace-with-your-local-secret"
curl -X POST https://api.tapvid.ai/api/public/v1/materials \
-H "Authorization: Bearer ${TAPVID_API_KEY}" \
-H "Content-Type: application/json" \
-d '{ "url": "https://tapvid.ai/api-mcp" }'El ejemplo cURL es local. En CI usa secret store. `payload_too_large` exige cambiar el material; una URL rechazada exige revisar HTTPS, longitud, acceso y redirects.
04
Paso 2: crea el trabajo asíncrono
Crea solo tras upload correcto. Define audiencia, fuente, 30 segundos, 16:9, inglés, secuencia y prohibiciones. HTTP 202 con `videoId` significa aceptado, no terminado.
curl -X POST https://api.tapvid.ai/api/public/v1/video/create \
-H "Authorization: Bearer ${TAPVID_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"materialIds": ["MATERIAL_ID_FROM_UPLOAD"],
"userPrompt": "Create a concise 30-second English explainer for developers. Stay faithful to the supplied source and do not invent claims.",
"title": "TapVid API developer explainer",
"aspectRatio": "16:9",
"duration": "30s",
"language": "en"
}'
Persiste `videoId` antes de otra llamada. Si el proceso cae sin ID, no puede reanudar y puede duplicar gasto. La respuesta usa `videoId` y la query usa `video_id`.
05
Paso 3: persiste, consulta y descarga
Consulta status según `pollAfterSeconds`. Progress no es tiempo restante. Tras completed puede tardar creditsUsed. Pide después la URL firmada, que expira en cerca de una hora.
import { writeFile } from 'node:fs/promises'
const apiKey = process.env.TAPVID_API_KEY
if (!apiKey) throw new Error('TAPVID_API_KEY is required')
const base = 'https://api.tapvid.ai/api/public/v1'
const headers = { Authorization: `Bearer ${apiKey}` }
async function request(path, init = {}, { attempts = 1 } = {}) {
let lastError
for (let attempt = 1; attempt <= attempts; attempt += 1) {
try {
const response = await fetch(`${base}${path}`, {
...init,
headers: { ...headers, ...init.headers },
})
const data = await response.json()
if (response.ok) return { response, data }
if (response.status !== 429 && response.status < 500) {
const error = new Error(`HTTP ${response.status}: ${data.code ?? 'unknown'}`)
error.retryable = false
throw error
}
lastError = new Error(`retryable HTTP ${response.status}`)
} catch (error) {
if (error?.retryable === false) throw error
lastError = error
}
if (attempt === attempts) break
await new Promise((resolve) => setTimeout(resolve, attempt * 1000))
}
throw lastError
}
const { data: material } = await request('/materials', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://tapvid.ai/api-mcp' }),
})
const { response: createResponse, data: video } = await request('/video/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
materialIds: [material.materialId],
userPrompt: 'Create a concise 30-second English explainer for developers. Stay faithful to the source.',
title: 'TapVid API developer explainer',
aspectRatio: '16:9',
duration: '30s',
language: 'en',
}),
})
if (createResponse.status !== 202) throw new Error('Expected 202 Accepted')
// Persist before the next network call. A lost poll must not cause a duplicate create.
await writeFile('.tapvid-job.json', JSON.stringify({ videoId: video.videoId }))
let status
for (let poll = 0; poll < 180; poll += 1) {
const result = await request(
`/video/status?video_id=${encodeURIComponent(video.videoId)}`,
{},
{ attempts: 4 },
)
status = result.data
if (status.status === 'completed' || status.status === 'failed') break
await new Promise((resolve) =>
setTimeout(resolve, Math.max(status.pollAfterSeconds ?? 5, 5) * 1000),
)
}
if (!status) throw new Error('No status received')
if (status.status === 'failed') throw new Error(status.error?.code ?? 'generation_failed')
if (status.status !== 'completed') throw new Error('Polling timeout; resume with the saved videoId')
const query = new URLSearchParams({
video_id: video.videoId,
resolution: '1080P',
watermark: 'true',
subtitle: 'false',
})
const { data: download } = await request(`/video/download?${query}`, {}, { attempts: 4 })
console.log({ status: download.status, expiresAt: download.expiresAt })El ejemplo Node guarda el ID y reintenta lecturas con límites. Una base de datos reemplaza el JSON local. 400 y 401 no mejoran con tiempo; 429, servidor y red usan backoff.
06
Qué mostró la prueba REST
La prueba cargó en 1,4 segundos y creó en 0,3 con HTTP 202. Running 50% apareció a 81 segundos y seguía a 331. Entonces el poller terminó con `ECONNRESET` antes de TLS.

La primera versión conservó el ID solo en memoria y no pudo reanudar. No creamos otro job. El uso total llegó a 180 credits, 90 por create. No afirmamos un video final en diez minutos.
07
Gestiona errores sin duplicar gasto
Responde según el error: unauthorized corrige clave, invalid_request body, insufficient_credits detiene, rate_limited respeta Retry-After y red reintenta lecturas seguras. No recrees tras perder una respuesta.
| Condición | ¿Retry? | Acción |
|---|---|---|
| unauthorized | No | Corregir clave |
| invalid_request | No | Corregir body |
| insufficient_credits | No | Detener |
| rate_limited | Sí | Retry-After |
| Red o servidor | Limitado | Reintentar lectura |
| Respuesta create perdida | No recrear | Reanudar por ID |
08
Checklist de producción
Producción requiere secretos en servidor, IDs atómicos, latencias separadas, timeout reanudable, logs seguros, URLs renovables y revisión humana de hechos, duración, derechos y marca.
- Clave solo en servidor.
- IDs atómicos.
- Latencias separadas.
- Intervalo y timeout.
- Separar reads y writes.
- Renovar URL firmada.
- Registrar credits sin secretos.
- Persona revisa y publica.
09
API para explainers completos, no clips aleatorios
Usa la API para un explainer coherente desde contenido aprobado, no para prometer clips cinematográficos aleatorios. Para diálogo consulta el tutorial MCP y para control la visión general API/MCP.
10
Preguntas frecuentes
¿Siempre termina en 10 minutos?
No. La integración es rápida, pero render y export son asíncronos. La prueba MCP tardó unos 28 minutos hasta completed.
¿Qué significa HTTP 202?
El trabajo fue aceptado. Guarda videoId y consulta status.
¿Cada cuánto consultar?
Usa pollAfterSeconds, reintentos limitados y timeout reanudable.
¿Cuánto dura la URL?
La documentación indica aproximadamente una hora.
¿Puedo quitar watermark?
Está activo por defecto y quitarlo requiere suscripción.
Turn them into a clear, publishable video
Keep reading
Related stories

Cómo crear motion graphics con Claude y TapVid MCP
Tutorial práctico con una fuente pública, cinco herramientas MCP reales, un brief de 30 segundos y una prueba honesta.
Aug 7, 2026

Generación de vídeo con Claude: ¿Seedance 2.5 o TapVid?
La generación de vídeo de Claude necesita una herramienta de renderización. Aprende cuándo emparejar a Claude con Seedance 2.5 o TapVid, con indicaciones y un flujo de trabajo práctico.
Aug 8, 2026

Técnicas de animación de texto: más movimiento sin ruido visual
Un framework práctico de animación de texto para equipos que necesitan mensajes de video legibles y de alto impacto.
Apr 16, 2026

