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

  1. Inicia sesión en el panel de administración: /admin
  2. Ve a la sección Tokens: /admin/tokens
  3. Crea un nuevo token con un nombre descriptivo
  4. 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 Título del video
description string No Descripción del video
webhookUrl string No URL para recibir notificaciones de eventos
output object Configuración de salida del video
output.fps number Frames por segundo (30, 60, etc.)
output.format string Formato del video ("mp4", "mov", "webm")
output.width number Ancho en píxeles
output.height number Alto en píxeles
output.codec string Códec de video ("h264", "h265", etc.)
timeline array 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 Tipo de búsqueda: "photo", "video" o "all"
search string 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:

  1. Primera foto de "hotel"
  2. Primera foto de "oficina"
  3. Primera foto de "tecnología"
  4. Segunda foto de "hotel"
  5. Segunda foto de "oficina"
  6. Segunda foto de "tecnología"
  7. Tercera foto de "hotel"
  8. Tercera foto de "oficina"
  9. 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: