API Pública - Videomaker
Documentación de los endpoints públicos de la API de Videomaker que requieren autenticación mediante API Token.
Autenticación
Todas las APIs públicas requieren un token de autenticación en el header Authorization:
Authorization: Bearer {TU_API_TOKEN}
Obtener un API Token
- Inicia sesión en el panel de administración:
/admin - Ve a la sección Tokens:
/admin/tokens - Crea un nuevo token con un nombre descriptivo
- Copia el token generado (solo se muestra una vez)
1. Crear Video
Crea un nuevo video a partir de una configuración JSON.
Endpoint: POST /api/videos
Request
POST /api/videos
Authorization: Bearer {API_TOKEN}
Content-Type: application/json
Body:
{
"title": "Mi Video",
"description": "Descripción del video",
"webhookUrl": "https://tu-servidor.com/webhook",
"output": {
"fps": 30,
"format": "mp4",
"width": 1920,
"height": 1080,
"codec": "h264"
},
"timeline": [
{
"asset": {
"type": "video",
"src": "https://example.com/video.mp4",
"muted": true,
"loop": true
},
"start": 0,
"length": 10
},
{
"asset": {
"type": "text",
"text": "Hola Mundo",
"style": {
"fontSize": 60,
"color": "#ffffff",
"fontWeight": "bold"
}
},
"start": 2,
"length": 5,
"position": "center"
}
]
}
Parámetros:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
title |
string | Sí | Título del video |
description |
string | No | Descripción del video |
webhookUrl |
string | No | URL para recibir notificaciones de eventos |
output |
object | Sí | Configuración de salida del video |
output.fps |
number | Sí | Frames por segundo (30, 60, etc.) |
output.format |
string | Sí | Formato del video ("mp4", "mov", "webm") |
output.width |
number | Sí | Ancho en píxeles |
output.height |
number | Sí | Alto en píxeles |
output.codec |
string | Sí | Códec de video ("h264", "h265", etc.) |
timeline |
array | Sí | Array de clips del video |
Para más detalles sobre el formato del JSON, consulta VIDEO_JSON_DOCUMENTATION.md.
Response (201 Created)
{
"id": "cmg123456789",
"title": "Mi Video",
"description": "Descripción del video",
"status": "PENDING",
"fps": 30,
"width": 1920,
"height": 1080,
"format": "mp4",
"codec": "h264",
"webhookUrl": "https://tu-servidor.com/webhook",
"createdAt": "2025-10-02T10:30:00.000Z",
"message": "Video creado exitosamente. Procesando clips..."
}
Webhooks
Si proporcionas un webhookUrl, recibirás notificaciones de los siguientes eventos:
Evento: clips.ready
Se envía cuando todos los clips han sido procesados y el video está listo para renderizar.
{
"event": "clips.ready",
"timestamp": "2025-10-02T10:35:00.000Z",
"video": {
"id": "cmg123456789",
"title": "Mi Video",
"status": "READY",
"duration": 45.5,
"clipsProcessingTime": 120.5
},
"json": { /* JSON procesado completo */ },
"stats": {
"totalClips": 10,
"readyClips": 10,
"failedClips": 0
}
}
Evento: video.completed
Se envía cuando el video ha sido completamente renderizado.
{
"event": "video.completed",
"timestamp": "2025-10-02T10:40:00.000Z",
"video": {
"id": "cmg123456789",
"title": "Mi Video",
"status": "COMPLETED",
"duration": 45.5,
"videoUrl": "https://storage.example.com/videos/final.mp4"
},
"timing": {
"clipsProcessingTime": 120.5,
"renderingTime": 300.2,
"totalProcessingTime": 420.7
}
}
Evento: video.failed
Se envía si el procesamiento o renderizado falla.
{
"event": "video.failed",
"timestamp": "2025-10-02T10:35:00.000Z",
"video": {
"id": "cmg123456789",
"title": "Mi Video",
"status": "FAILED"
},
"error": {
"message": "Error al procesar clip de TTS",
"failedClips": [
{
"id": "clip123",
"order": 2,
"assetType": "tts",
"error": "AWS Polly error"
}
]
}
}
Errores
| Código | Descripción |
|---|---|
400 |
JSON inválido o campos requeridos faltantes |
401 |
API token inválido o faltante |
500 |
Error interno del servidor |
2. Listar Videos
Obtiene la lista de todos los videos del usuario.
Endpoint: GET /api/videos
Request
GET /api/videos
Authorization: Bearer {API_TOKEN}
Response (200 OK)
{
"videos": [
{
"id": "cmg123456789",
"title": "Mi Video",
"description": "Descripción del video",
"status": "COMPLETED",
"fps": 30,
"width": 1920,
"height": 1080,
"format": "mp4",
"codec": "h264",
"duration": 45.5,
"videoUrl": "https://storage.example.com/videos/final.mp4",
"thumbnailUrl": "https://storage.example.com/thumbnails/thumb.jpg",
"createdAt": "2025-10-02T10:30:00.000Z",
"updatedAt": "2025-10-02T10:40:00.000Z",
"processedAt": "2025-10-02T10:40:00.000Z",
"progress": 100,
"stats": {
"total": 10,
"pending": 0,
"processing": 0,
"ready": 10,
"failed": 0,
"skipped": 0
},
"clipsProcessingTime": 120.5,
"renderingTime": 300.2,
"totalProcessingTime": 420.7
}
]
}
Estados del Video
| Estado | Descripción |
|---|---|
PENDING |
Video creado, esperando procesamiento de clips |
PROCESSING |
Clips siendo procesados |
READY |
Clips procesados, listo para renderizar |
RENDERING |
Video siendo renderizado |
COMPLETED |
Video completado y disponible |
FAILED |
Error en procesamiento o renderizado |
3. Obtener Estado de Video
Consulta el estado actual de un video específico.
Endpoint: GET /api/videos/:id/status
Request
GET /api/videos/cmg123456789/status
Authorization: Bearer {API_TOKEN}
Response (200 OK)
{
"id": "cmg123456789",
"title": "Mi Video",
"status": "PROCESSING",
"progress": 65,
"duration": 45.5,
"videoUrl": null,
"thumbnailUrl": null,
"createdAt": "2025-10-02T10:30:00.000Z",
"processedAt": null,
"clips": {
"total": 10,
"pending": 0,
"processing": 3,
"ready": 7,
"failed": 0,
"skipped": 0
},
"clipsDetails": [
{
"id": "clip1",
"order": 0,
"assetType": "video",
"status": "READY",
"start": 0,
"length": 10,
"processedAt": "2025-10-02T10:32:00.000Z"
},
{
"id": "clip2",
"order": 1,
"assetType": "tts",
"status": "PROCESSING",
"start": 10,
"length": 5.5,
"processedAt": null
}
]
}
Polling
Para hacer polling del estado, consulta este endpoint cada 2-5 segundos hasta que el estado sea COMPLETED o FAILED.
async function waitForVideo(videoId, apiToken) {
while (true) {
const response = await fetch(`/api/videos/${videoId}/status`, {
headers: { 'Authorization': `Bearer ${apiToken}` }
});
const data = await response.json();
if (data.status === 'COMPLETED') {
console.log('Video listo:', data.videoUrl);
return data;
}
if (data.status === 'FAILED') {
throw new Error('Video falló');
}
console.log(`Progreso: ${data.progress}%`);
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
4. Buscar en Pexels
Busca fotos y/o videos en Pexels con soporte para múltiples keywords.
Endpoint: POST /api/pexels-search
Request
POST /api/pexels-search
Authorization: Bearer {API_TOKEN}
Content-Type: application/json
Body:
{
"type": "all",
"search": "hotel moderno, oficina trabajo, tecnología",
"locale": "es-ES",
"orientation": "landscape",
"per_page": 10,
"page": 1
}
Parámetros:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
type |
string | Sí | Tipo de búsqueda: "photo", "video" o "all" |
search |
string | Sí | Keywords separadas por coma |
locale |
string | No | Idioma de búsqueda (default: "es-ES") |
orientation |
string | No | Orientación: "landscape", "portrait" o "square" |
per_page |
number | No | Resultados por página (1-80, default: 10) |
page |
number | No | Número de página (default: 1) |
Response (200 OK)
{
"success": true,
"data": {
"photos": [
{
"id": 123456,
"photographer": "John Doe",
"photographer_url": "https://pexels.com/@johndoe",
"url": "https://pexels.com/photo/123456",
"alt": "Modern hotel lobby",
"width": 1920,
"height": 1080,
"avg_color": "#4A5568",
"src": {
"original": "https://images.pexels.com/.../original.jpg",
"large": "https://images.pexels.com/.../large.jpg",
"large2x": "https://images.pexels.com/.../large2x.jpg",
"medium": "https://images.pexels.com/.../medium.jpg",
"small": "https://images.pexels.com/.../small.jpg",
"portrait": "https://images.pexels.com/.../portrait.jpg",
"landscape": "https://images.pexels.com/.../landscape.jpg",
"tiny": "https://images.pexels.com/.../tiny.jpg"
}
}
],
"videos": [
{
"id": 789012,
"user": {
"id": 456,
"name": "Jane Smith",
"url": "https://pexels.com/@janesmith"
},
"url": "https://pexels.com/video/789012",
"duration": 15.5,
"width": 1920,
"height": 1080,
"thumbnail": "https://images.pexels.com/.../thumbnail.jpg",
"best_file": {
"quality": "hd",
"width": 1920,
"height": 1080,
"fps": 30,
"link": "https://videos.pexels.com/.../video.mp4",
"file_type": "video/mp4"
},
"video_files": [
{
"id": 1,
"quality": "hd",
"width": 1920,
"height": 1080,
"fps": 30,
"link": "https://videos.pexels.com/.../hd.mp4",
"file_type": "video/mp4"
},
{
"id": 2,
"quality": "sd",
"width": 1280,
"height": 720,
"fps": 30,
"link": "https://videos.pexels.com/.../sd.mp4",
"file_type": "video/mp4"
}
]
}
],
"stats": {
"total_photos": 1250,
"total_videos": 450,
"returned_photos": 10,
"returned_videos": 10,
"keywords_searched": ["hotel moderno", "oficina trabajo", "tecnología"]
}
}
}
Interleaving de Resultados
Cuando buscas con múltiples keywords, los resultados se entrelazan para garantizar diversidad:
Ejemplo: Búsqueda de "hotel, oficina, tecnología" con per_page: 3
Orden de resultados:
- Primera foto de "hotel"
- Primera foto de "oficina"
- Primera foto de "tecnología"
- Segunda foto de "hotel"
- Segunda foto de "oficina"
- Segunda foto de "tecnología"
- Tercera foto de "hotel"
- Tercera foto de "oficina"
- Tercera foto de "tecnología"
Límites de Pexels
- 200 solicitudes por hora (límite de API gratuita)
- 80 resultados máximos por página
- Atribución requerida: Debes incluir el nombre del fotógrafo/videógrafo al usar el contenido
Errores
| Código | Error | Descripción |
|---|---|---|
400 |
INVALID_SEARCH |
Campo search faltante o vacío |
400 |
INVALID_TYPE |
El campo type debe ser photo, video o all |
400 |
INVALID_PER_PAGE |
El per_page debe estar entre 1 y 80 |
401 |
UNAUTHORIZED |
API token inválido o faltante |
429 |
RATE_LIMIT |
Límite de Pexels excedido (200/hora) |
500 |
PEXELS_AUTH_ERROR |
Error de autenticación con Pexels |
Códigos de Error Comunes
401 Unauthorized
{
"error": "Invalid API key"
}
Solución: Verifica que tu API token sea correcto y esté activo.
400 Bad Request
{
"error": "Missing required fields: title, output, timeline"
}
Solución: Asegúrate de enviar todos los campos requeridos.
404 Not Found
{
"error": "Video not found"
}
Solución: El ID del video no existe o no pertenece a tu usuario.
429 Rate Limit
{
"success": false,
"error": {
"code": "RATE_LIMIT",
"message": "Límite de rate de Pexels excedido (200 solicitudes/hora)"
}
}
Solución: Espera una hora antes de hacer más solicitudes a Pexels.
Ejemplos de Uso
cURL
# Crear video
curl -X POST http://localhost:5173/api/videos \
-H "Authorization: Bearer tu_api_token_aqui" \
-H "Content-Type: application/json" \
-d @video.json
# Consultar estado
curl http://localhost:5173/api/videos/cmg123/status \
-H "Authorization: Bearer tu_api_token_aqui"
# Buscar en Pexels
curl -X POST http://localhost:5173/api/pexels-search \
-H "Authorization: Bearer tu_api_token_aqui" \
-H "Content-Type: application/json" \
-d '{"type":"all","search":"hotel,oficina","per_page":5}'
JavaScript/Node.js
const API_TOKEN = 'tu_api_token_aqui';
const API_URL = 'http://localhost:5173/api';
// Crear video
async function createVideo(videoConfig) {
const response = await fetch(`${API_URL}/videos`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(videoConfig)
});
return await response.json();
}
// Buscar en Pexels
async function searchPexels(keywords) {
const response = await fetch(`${API_URL}/pexels-search`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'all',
search: keywords,
per_page: 10
})
});
return await response.json();
}
// Uso
const video = await createVideo({
title: "Mi Video",
output: { fps: 30, format: "mp4", width: 1920, height: 1080, codec: "h264" },
timeline: [/* clips */]
});
const pexelsResults = await searchPexels("hotel moderno, oficina");
Python
import requests
API_TOKEN = 'tu_api_token_aqui'
API_URL = 'http://localhost:5173/api'
headers = {
'Authorization': f'Bearer {API_TOKEN}',
'Content-Type': 'application/json'
}
# Crear video
video_config = {
'title': 'Mi Video',
'output': {'fps': 30, 'format': 'mp4', 'width': 1920, 'height': 1080, 'codec': 'h264'},
'timeline': []
}
response = requests.post(f'{API_URL}/videos', headers=headers, json=video_config)
video = response.json()
# Buscar en Pexels
search_config = {
'type': 'all',
'search': 'hotel moderno, oficina',
'per_page': 10
}
response = requests.post(f'{API_URL}/pexels-search', headers=headers, json=search_config)
results = response.json()
Rate Limiting
Las APIs tienen los siguientes límites:
- Videomaker API: Sin límite específico (usa con responsabilidad)
- Pexels API: 200 solicitudes por hora (límite de Pexels gratuita)
Soporte
Para más información o ayuda:
- Documentación del formato JSON: VIDEO_JSON_DOCUMENTATION.md
- Documentación de servicios: docs/services/
- Issues: GitHub repository