¿Qué hace este workflow?
Este workflow de n8n extrae transcripciones limpias y estructuradas de vídeos de YouTube usando la YouTube Data API oficial de Google. Está diseñado específicamente para trabajar con vídeos de tu propio canal, proporcionando acceso nativo a los subtítulos mediante autenticación OAuth2.
El workflow ejecuta automáticamente estas acciones:
- Recibe parámetros de entrada:
youtubeVideoIdypreferredLanguage(ej: es, en, fr) - Lista todas las pistas de subtítulos disponibles usando el endpoint
captions.listde YouTube API - Selecciona inteligentemente el idioma: prioriza tu idioma preferido o hace fallback a la primera pista disponible
- Descarga el archivo VTT (WebVTT format) con los subtítulos completos
- Limpia y procesa el texto: elimina timestamps, etiquetas WEBVTT, marcas como [Música], palabras duplicadas y espacios innecesarios
- Devuelve JSON estructurado: texto plano listo para usar con metadatos (idioma, conteo de palabras, caracteres, status)
Casos de uso y beneficios
Este workflow está optimizado para creadores de contenido, equipos de marketing y desarrolladores que gestionan su propio canal de YouTube y necesitan automatizar la extracción de transcripciones con máxima calidad y control.
Casos de uso principales:
- Generación automática de contenido: Crea resúmenes, posts de blog, threads de Twitter o newsletters a partir de tus vídeos de YouTube. Ideal para reutilizar contenido de video en múltiples formatos sin transcribir manualmente.
- Base de datos de contenido indexable: Almacena transcripciones en Supabase, Notion o Google Sheets para crear un sistema de búsqueda semántica de todo tu catálogo de vídeos. Perfecto para equipos que necesitan encontrar referencias específicas en cientos de horas de contenido.
- Pipelines de IA y análisis: Alimenta modelos de lenguaje (ChatGPT, Claude) con transcripciones limpias para clasificación automática, extracción de keywords, generación de embeddings o análisis de sentimiento de tu contenido.
- Accesibilidad y SEO: Genera subtítulos corregidos o mejora los automáticos de YouTube. Crea páginas de transcripciones completas para mejorar el SEO de tu sitio web con contenido de video transcrito.
- Documentación de cursos y webinars: Convierte automáticamente tus vídeos educativos en documentación de texto estructurada, ideal para crear wikis internas o recursos de aprendizaje complementarios.
Beneficio clave vs. soluciones externas: Al usar la YouTube Data API oficial, tienes acceso garantizado a los subtítulos de tu canal sin depender de servicios de terceros, límites de rate o costes adicionales por API. La calidad es óptima porque proviene directamente de YouTube.
⚠️ Limitación importante: Este workflow solo funciona con vídeos de tu propio canal (el canal autenticado con OAuth2). Para vídeos externos o de terceros, obtendrás errores 403 (Forbidden) porque la API de Google no permite acceso a captions de canales ajenos. Si necesitas transcribir vídeos públicos de otros canales, consulta el workflow con YouTube Transcript API que está diseñado específicamente para ese caso de uso.
Requisitos previos
- Canal de YouTube propio: El vídeo debe pertenecer al canal autenticado con OAuth2. No funciona con vídeos de terceros debido a restricciones de permisos de la YouTube Data API.
- Credenciales OAuth2 de Google: Configuradas en n8n con tipo
youTubeOAuth2Api. Requiere los scopes:youtube.force-sslyoutube.readonlyoyoutube
- Proyecto en Google Cloud Console: Debes tener la YouTube Data API v3 habilitada en tu proyecto de Google Cloud. Habilitar YouTube Data API v3
- Vídeos con subtítulos: El vídeo debe tener al menos una pista de subtítulos (automáticos o manuales). Si no hay captions, el workflow devuelve un error controlado.
- Nodos n8n requeridos:
- Execute Workflow Trigger (para usar como sub-workflow)
- Set (variables)
- HTTP Request (con autenticación YouTube OAuth2)
- IF (condicional)
- Code (JavaScript para procesamiento)
- Extract from File
- Stop and Error
- Versión mínima de n8n: 1.0.0 (todos los nodos son nativos)
- Conocimientos básicos: Entender el flujo de autenticación OAuth2, uso de expresiones n8n (
{{ $json.field }}) y llamadas entre workflows
Cómo configurar el workflow paso a paso
Paso 1: Configura el trigger de sub-workflow
Añade el nodo When Executed by Another Workflow como punto de entrada:
- Workflow Inputs: Define dos parámetros de entrada:
youtubeVideoId(String) – El ID del vídeo de YouTube (ej: «nxub8Bmia68»)preferredLanguage(String) – Código de idioma preferido (ej: «es», «en», «fr»)
Ejemplo de payload para ejecutar este workflow desde otro:
{
"youtubeVideoId": "nxub8Bmia68",
"preferredLanguage": "es"
}
Este enfoque permite usar el workflow como módulo reutilizable desde cualquier otro flujo de n8n.
Paso 2: Establece variables con Set node
Añade un nodo Set para normalizar las variables de entrada:
- Assignment 1:
- Name:
youtubeVideoId - Value:
={{ $json.youtubeVideoId }}
- Name:
- Assignment 2:
- Name:
preferredLanguage - Value:
={{ $json.preferredLanguage }}
- Name:
Esto facilita referenciar estos valores en nodos posteriores usando $('Set Variables').item.json.youtubeVideoId.
Paso 3: Lista pistas de subtítulos con YouTube API
Añade un nodo HTTP Request para consultar las pistas disponibles:
- Method:
GET - URL:
https://www.googleapis.com/youtube/v3/captions?part=snippet&videoId={{ $('Set Variables').item.json.youtubeVideoId }} - Authentication:
Predefined Credential Type→youTubeOAuth2Api - Credential: Selecciona tu credencial de YouTube OAuth2 configurada previamente
Respuesta esperada: La API devuelve un objeto con array items[] conteniendo todas las pistas de subtítulos disponibles, cada una con metadata de idioma, tipo (automática/manual), y caption ID único.
⚠️ Nota crítica: Que obtengas una lista de captions no garantiza que puedas descargarlas. Si el vídeo no es de tu canal autenticado, el siguiente paso (descarga) devolverá 403 Forbidden.
Paso 4: Valida existencia de subtítulos con IF node
Añade un nodo IF para verificar si hay captions disponibles:
- Condition: Number → Greater Than
- Value 1:
={{ $json.items.length }} - Value 2:
0
Flujo TRUE: Continúa al selector de idioma si hay captions
Flujo FALSE: Redirige al nodo de error controlado
Paso 5: Selecciona idioma con lógica de fallback
Añade un nodo Code (JavaScript) que implementa la selección inteligente de idioma:
// Extract caption ID with preferred language priority or first available
const captionList = $input.first().json;
const captions = captionList.items || [];
const preferredLanguage = $('Set Variables').item.json.preferredLanguage || 'es';
let captionId = null;
let language = null;
// Search preferred language first
const preferredCaption = captions.find(caption => caption.snippet.language === preferredLanguage);
if (preferredCaption) {
captionId = preferredCaption.id;
language = preferredCaption.snippet.language;
} else {
// Fallback: first available caption
if (captions.length > 0) {
captionId = captions[0].id;
language = captions[0].snippet.language;
}
}
return [{
json: {
captionId,
language,
videoId: captionList.items[0]?.snippet.videoId,
totalCaptions: captions.length,
status: captionId ? 'found' : 'no_captions',
preferredLanguage: preferredLanguage
}
}];
Lógica implementada:
- Busca coincidencia exacta con
preferredLanguage - Si no existe, toma la primera pista disponible (fallback)
- Devuelve
captionIdpara descarga + metadata útil
Paso 6: Descarga el archivo VTT
Añade otro nodo HTTP Request para descargar la pista seleccionada:
- Method:
GET - URL:
https://www.googleapis.com/youtube/v3/captions/{{ $('Caption Language Selector').item.json.captionId }} - Authentication:
Predefined Credential Type→youTubeOAuth2Api - Response Format: File (por defecto descarga como binario)
La respuesta es un archivo VTT (WebVTT format) que contiene timestamps y texto de los subtítulos.
Paso 7: Extrae texto del archivo con Extract from File
Añade un nodo Extract from File:
- Operation:
Extract text from file - Destination Key:
content
Este nodo convierte el binario VTT en string de texto accesible desde $json.content.
Paso 8: Limpia y estructura el texto con Code node
Añade un nodo Code final que limpia el VTT y genera output estructurado:
// Get subtitle content from previous node
const subtitleContent = $('Caption File Conversion').first().json.content;
// Split into lines
const lines = subtitleContent.split('\n');
// Function to detect timestamp lines (VTT format)
const isTimestampLine = (line) => {
const timestampRegex = /^(\d{1,2}:\d{2}:\d{2}\.\d{3},)+\d{1,2}:\d{2}:\d{2}\.\d{3}/;
return timestampRegex.test(line.trim());
};
// Function to detect WEBVTT headers
const isHeaderLine = (line) => {
const headerRegex = /^(WEBVTT|Kind:|Language:)/i;
return headerRegex.test(line.trim());
};
// Filter: exclude timestamps, headers and empty lines
const textLines = lines.filter(line => {
return !isTimestampLine(line) && !isHeaderLine(line) && line.trim() !== '';
});
// Clean text: remove [Música], normalize whitespace
const cleanText = (line) => {
let cleaned = line;
// Remove non-text elements like [Música]
cleaned = cleaned.replace(/\[\w+\]/g, '');
// Remove repeated words
cleaned = cleaned.replace(/\b(\w+)\s+\1\b/g, '$1');
return cleaned.trim();
};
const cleanedLines = textLines.map(cleanText);
// Join ALL text into single string, remove all \n and normalize spaces
const plainText = cleanedLines.join(' ').replace(/\s+/g, ' ').trim();
return [{
json: {
videoId: $('Set Variables').item.json.youtubeVideoId,
language: $('Caption Language Selector').item.json.language,
text: plainText,
wordCount: plainText.split(' ').length,
charCount: plainText.length,
status: 'success',
source: 'youtube_captions'
}
}];
Procesamiento aplicado:
- Elimina timestamps (00:01:23.456 → 00:01:25.789)
- Remueve headers WEBVTT, Kind, Language
- Filtra etiquetas de sonido [Música], [Aplausos]
- Elimina palabras duplicadas consecutivas
- Normaliza espacios en blanco
- Genera estadísticas (word count, char count)
Paso 9: Gestiona errores con rama de fallback
En la rama FALSE del IF, añade:
Nodo Code (No Captions Fallback):
// No captions available - return structured error
return [{
json: {
videoId: $('Set Variables').item.json.youtubeVideoId,
language: $('Set Variables').item.json.preferredLanguage,
error: 'No captions available for this video',
status: 'no_captions',
suggestion: 'Use Whisper AI fallback',
source: 'youtube_api'
}
}];
Nodo Stop and Error:
- Error Message:
"There's no captions in YouTube Video"
Esto permite que el workflow padre capture el error y tome acciones alternativas (ej: usar Whisper AI para transcribir el audio).
Notas técnicas importantes
Limitaciones de la YouTube Data API:
- Solo canal propio: La API
captions.downloadsolo funciona con vídeos de tu canal autenticado. Para vídeos externos obtendrás403 Forbidden. Esta es una restricción de Google, no de n8n. - Quota de API: YouTube Data API tiene un límite de 10,000 unidades/día por proyecto de forma gratuita. Cada llamada a
captions.listconsume ~50 unidades, ycaptions.downloadotras ~200 unidades. Con este workflow, puedes procesar aproximadamente 40 vídeos/día sin costo adicional. - Formato VTT vs SRT: YouTube devuelve por defecto formato VTT (WebVTT). Si necesitas SRT, puedes añadir el parámetro
tfmt=srta la URL de descarga, aunque el código de limpieza actual está optimizado para VTT. - Subtítulos automáticos vs manuales: YouTube genera subtítulos automáticos para muchos idiomas. La calidad de estos es variable – los manuales son siempre más precisos. El workflow no diferencia entre ambos; simplemente toma la pista disponible según tu criterio de idioma.
Troubleshooting común:
- Error 403 Forbidden al descargar: El vídeo no pertenece a tu canal autenticado. Verifica que el
youtubeVideoIdcorresponde a un vídeo de tu propio canal. Alternativa: usa YouTube Transcript API para vídeos externos. - Error 401 Unauthorized: Las credenciales OAuth2 expiraron o no tienen los scopes correctos. Re-autentica la credencial en n8n asegurándote de incluir
youtube.readonlyoyoutubescope. - Lista vacía de captions: El vídeo no tiene subtítulos (ni automáticos ni manuales). YouTube no genera automáticamente captions para todos los vídeos – depende del idioma y la calidad del audio. Solución: sube manualmente un archivo SRT en YouTube Studio o usa Whisper AI para transcribir.
- Idioma preferido no encontrado: El workflow hace fallback a la primera pista disponible. Si esto no es deseado, modifica el código del nodo «Caption Language Selector» para devolver error en lugar de fallback.
- Texto con caracteres raros: Algunos subtítulos automáticos incluyen entidades HTML ( , "). Añade en el nodo «Clean Transcript» una línea adicional:
cleaned = cleaned.replace(/&#?\w+;/g, '');para eliminarlas.
Consideraciones de rendimiento:
- Tiempo de ejecución: Para un vídeo típico de 10 minutos, el workflow tarda 3-5 segundos en completarse (2 llamadas API + procesamiento de texto).
- Tamaño de transcripciones: Un vídeo de 1 hora genera aproximadamente 8,000-12,000 palabras de texto limpio (~50-70KB). Asegúrate de que tu sistema downstream puede manejar estos volúmenes si procesas vídeos largos en batch.
Mejoras y personalizaciones
Optimizaciones del workflow:
- Soporte multi-idioma simultáneo: Modifica el nodo «Caption Language Selector» para devolver múltiples captionIds en lugar de uno solo, y procesa cada idioma en paralelo con Split In Batches. Útil si necesitas transcripciones en español e inglés del mismo vídeo.
- Cache de transcripciones: Añade un nodo que verifique en una base de datos (Supabase/PostgreSQL) si ya existe la transcripción del videoId antes de llamar a la API. Reduce consumo de quota y mejora velocidad en ejecuciones repetidas.
- Formato con timestamps preservados: En lugar de eliminar timestamps en «Clean Transcript», conviértelos en marcadores de párrafo: cada bloque de 30 segundos se convierte en un párrafo separado. Útil para indexación temporal con embeddings.
- Detección de speaker diarization: Si tus subtítulos manuales incluyen identificadores de speaker (ej: «John: …»), añade lógica en el Code node para extraer y estructurar por speaker:
{ speaker: "John", text: "..." }.
Integraciones adicionales:
- Post-procesamiento con IA: Conecta la salida directamente a OpenAI o Claude para:
- Generar resumen ejecutivo
- Extraer bullet points clave
- Clasificar por temática/categoría
- Generar título SEO-optimizado
- Embeddings para búsqueda semántica: Añade un nodo que envíe el texto a OpenAI Embeddings API y almacene los vectores en Pinecone o Supabase pgvector para búsqueda semántica avanzada en tu catálogo de vídeos.
- Publicación automática: Crea una variante que publique automáticamente la transcripción como:
- Post de WordPress (custom post type «Transcripción»)
- Página de Notion
- Documento de Google Docs
- Thread de Twitter usando la API v2
- Batch processing de canal completo: Combina este workflow con un nodo que liste todos los vídeos de tu canal (YouTube API
channels.list+playlistItems.list) y procesa transcripciones en batch con Split In Batches + Wait entre lotes para respetar rate limits.
Alternativas para vídeos externos:
- YouTube Transcript API: Para vídeos públicos de otros canales, usa este workflow alternativo que utiliza una API de terceros especializada en extracción de transcripciones de vídeos públicos.
- Whisper AI fallback: Crea un flujo híbrido que intente primero YouTube Data API (para vídeos propios) y, si falla con 403, descargue el audio del vídeo con yt-dlp y lo transcriba con Whisper AI (OpenAI o local con n8n-nodes-whisper).
Descargar gratis este workflow
Importa este workflow directamente en tu instancia de n8n. Descarga el archivo JSON y luego ve a Workflows → Import from File.
Recuerda que si te suscribes gratuitamente tendrás acceso a todos los workflows y además podrás disfrutar de todo el resto de contenido de joelcantero.com.

