The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "YouTube RAG Pipeline - Ingestion & Vectorization",
"nodes": [
{
"parameters": {},
"id": "trigger-manual",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
0,
300
]
},
{
"parameters": {
"videoId": "={{ $json.videoId }}"
},
"id": "get-metadata",
"name": "Get Metadata",
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [
220,
300
]
},
{
"parameters": {
"resource": "commentThread",
"videoId": "={{ $json.id }}",
"limit": 20
},
"id": "get-comments",
"name": "Get Comments",
"type": "n8n-nodes-base.youTube",
"typeVersion": 1,
"position": [
440,
300
]
},
{
"parameters": {
"jsCode": "const inputItem = $input.first().json;\nlet videoId = inputItem.id;\n\nif (!videoId) {\n return [{ json: { error: \"No video ID found\" } }];\n}\n\nconst youtubePageUrl = `https://www.youtube.com/watch?v=${videoId}`;\n\ntry {\n const response = await fetch(youtubePageUrl);\n if (!response.ok) throw new Error(\"Failed to fetch page\");\n const body = await response.text();\n\n const playerResponseMatch = body.match(/ytInitialPlayerResponse = ({.*?});/);\n if (!playerResponseMatch) throw new Error(\"No player response found\");\n\n const ytInitialPlayerResponse = JSON.parse(playerResponseMatch[1]);\n const captionTracks = ytInitialPlayerResponse?.captions?.playerCaptionsTracklistRenderer?.captionTracks;\n\n if (!captionTracks || captionTracks.length === 0) {\n return [{ json: { ...inputItem, transcript: \"[No Captions Available]\" } }];\n }\n\n const preferredTrack = captionTracks.find(track => track.languageCode === 'en') || captionTracks[0];\n const transcriptResponse = await fetch(preferredTrack.baseUrl + '&fmt=json3');\n const transcriptData = await transcriptResponse.json();\n\n const transcript = transcriptData.events\n .filter(event => event.segs)\n .map(event => event.segs.map(seg => seg.utf8).join(' ')\n .join(' ')\n .replace(/[\\u200B-\\u200D\\uFEFF]/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n\n return [{ json: { ...inputItem, transcript } }];\n\n} catch (error) {\n return [{ json: { ...inputItem, transcript: \"[Error Fetching Transcript: \" + error.message + \"]\" } }];\n}"
},
"id": "fetch-transcript",
"name": "Fetch Transcript (Code)",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
660,
300
]
},
{
"parameters": {
"mode": "manual",
"assignments": {
"assignments": [
{
"id": "content",
"name": "content",
"value": "={{ $json.snippet.title }}\n\n{{ $json.snippet.description }}\n\nTRANSCRIPT:\n{{ $json.transcript }}\n\nCOMMENTS:\n{{ $json.comments }}",
"type": "string"
}
]
}
},
"id": "format-document",
"name": "Format Document",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
880,
300
]
},
{
"parameters": {
"chunkSize": 1000,
"chunkOverlap": 200
},
"id": "text-splitter",
"name": "Text Splitter",
"type": "n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
"typeVersion": 1,
"position": [
1100,
200
]
},
{
"parameters": {
"model": "text-embedding-3-small"
},
"id": "embeddings",
"name": "OpenAI Embeddings",
"type": "n8n-nodes-langchain.embeddingsOpenAi",
"typeVersion": 1,
"position": [
1100,
400
]
},
{
"parameters": {
"mode": "insert",
"collection": "youtube_knowledge_base",
"options": {}
},
"id": "qdrant-store",
"name": "Qdrant Vector Store",
"type": "n8n-nodes-langchain.vectorStoreQdrant",
"typeVersion": 1,
"position": [
1100,
0
]
}
],
"connections": {
"Manual Trigger": {
"main": [
[
{
"node": "Get Metadata",
"type": "main",
"index": 0
}
]
]
},
"Get Metadata": {
"main": [
[
{
"node": "Get Comments",
"type": "main",
"index": 0
}
]
]
},
"Get Comments": {
"main": [
[
{
"node": "Fetch Transcript (Code)",
"type": "main",
"index": 0
}
]
]
},
"Fetch Transcript (Code)": {
"main": [
[
{
"node": "Format Document",
"type": "main",
"index": 0
}
]
]
},
"Format Document": {
"main": [
[
{
"node": "Qdrant Vector Store",
"type": "main",
"index": 0
}
]
]
},
"Text Splitter": {
"ai_textSplitter": [
[
{
"node": "Qdrant Vector Store",
"type": "ai_textSplitter",
"index": 0
}
]
]
},
"OpenAI Embeddings": {
"ai_embedding": [
[
{
"node": "Qdrant Vector Store",
"type": "ai_embedding",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
YouTube RAG Pipeline - Ingestion & Vectorization. Uses youTube, n8n-nodes-langchain. Event-driven trigger; 8 nodes.
Source: https://github.com/wranngle/n8n_showcase/blob/main/workflows/knowledge_management/youtube-rag-pipeline/workflow.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
YT Processing UPDATED. Uses googleSheets, youTube, httpRequest, googleDrive. Event-driven trigger; 44 nodes.
Api Schema Extractor. Uses manualTrigger, httpRequest, splitOut, textSplitterRecursiveCharacterTextSplitter. Event-driven trigger; 88 nodes.
Wait Splitout. Uses manualTrigger, httpRequest, splitOut, textSplitterRecursiveCharacterTextSplitter. Event-driven trigger; 88 nodes.
This workflow automates the process of discovering and extracting APIs from various services, followed by generating custom schemas. It works in three distinct stages: research, extraction, and schema
Inquiry-Agent. Uses @digitalocean/n8n-nodes-digitalocean-gradient-serverless-inference, stopAndError, googleDocs, gmail. Event-driven trigger; 88 nodes.