This workflow corresponds to n8n.io template #15175 — we link there as the canonical source.
This workflow follows the Agent → Form Trigger recipe pattern — see all workflows that pair these two integrations.
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 →
{
"id": "",
"meta": {
"templateCredsSetupCompleted": false
},
"name": "Generate AI YouTube comments and replies from a video URL with GPT-4 and Google Sheets",
"tags": [],
"nodes": [
{
"id": "f3dba414-0563-4e09-8a52-96caf10f43d2",
"name": "Video URL Input Form",
"type": "n8n-nodes-base.formTrigger",
"position": [
384,
336
],
"parameters": {
"options": {
"respondWithOptions": {
"values": {
"formSubmittedText": "Processing your video... Check Google Sheets for results!"
}
}
},
"formTitle": "YouTube Video Comment Generator",
"formFields": {
"values": [
{
"fieldLabel": "Video URL",
"placeholder": "https://www.youtube.com/watch?v=HdafI0t3sEY",
"requiredField": true
},
{
"fieldType": "number",
"fieldLabel": "Max Comments to Process",
"placeholder": "10"
}
]
},
"formDescription": "Enter a YouTube video URL to generate engagement comments and replies"
},
"typeVersion": 2.2
},
{
"id": "9e1cb342-818f-4974-9e20-acafd9b35bf6",
"name": "Extract Video ID from URL",
"type": "n8n-nodes-base.code",
"position": [
624,
336
],
"parameters": {
"jsCode": "// Extract video ID from a single YouTube URL\nconst videoUrl = $input.first().json['Video URL'];\nconst maxComments = $input.first().json['Max Comments to Process'] || 10;\n\nlet videoId = '';\n\n// Handle different possible YouTube URL types\nif (videoUrl.includes('youtube.com/watch?v=')) {\n // Example: https://www.youtube.com/watch?v=Aw7IjqKAX2k\n videoId = videoUrl.split('v=')[1].split('&')[0];\n} else if (videoUrl.includes('youtu.be/')) {\n // Example: https://youtu.be/Aw7IjqKAX2k\n videoId = videoUrl.split('youtu.be/')[1].split('?')[0];\n} else if (videoUrl.includes('youtube.com/embed/')) {\n // Example: https://www.youtube.com/embed/Aw7IjqKAX2k\n videoId = videoUrl.split('/embed/')[1].split('?')[0];\n} else if (videoUrl.includes('youtube.com/v/')) {\n // Example: https://www.youtube.com/v/Aw7IjqKAX2k\n videoId = videoUrl.split('/v/')[1].split('?')[0];\n} else {\n // Assume user directly provided the video ID\n videoId = videoUrl.trim();\n}\n\n// Clean up possible extra parameters\nvideoId = videoId.split('&')[0].split('#')[0];\n\n// \u2705 Return a single structured JSON object\nreturn [{\n json: {\n videoId,\n videoUrl: `https://www.youtube.com/watch?v=${videoId}`,\n maxComments,\n inputType: 'Direct Link'\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "9ecea677-4bc7-4098-9e2c-3cb7733f9eda",
"name": "Get Video Statistics",
"type": "n8n-nodes-base.httpRequest",
"position": [
864,
336
],
"parameters": {
"url": "=https://www.googleapis.com/youtube/v3/videos?part=statistics,snippet&id={{ $json.videoId }}",
"options": {},
"authentication": "predefinedCredentialType",
"nodeCredentialType": "youTubeOAuth2Api"
},
"credentials": {
"youTubeOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.3
},
{
"id": "ec751168-1993-4744-a4a5-6924d54c7806",
"name": "Format Video Data",
"type": "n8n-nodes-base.code",
"position": [
1104,
336
],
"parameters": {
"jsCode": "// Get video data from API response\nconst response = $input.first().json;\nconst videoData = response.items && response.items[0];\nconst prevData = $('Extract Video ID from URL').first().json;\n\nif (!videoData) {\n throw new Error('Video not found. Please check the URL.');\n}\n\nconst stats = videoData.statistics;\nconst snippet = videoData.snippet;\n\nreturn [{\n json: {\n items: videoData,\n videoId: videoData.id,\n videoUrl: prevData.videoUrl,\n maxComments: prevData.maxComments,\n viewCount: parseInt(stats.viewCount || 0),\n engagementScore: parseInt(stats.likeCount || 0) + parseInt(stats.commentCount || 0),\n selectionType: 'Direct Link Input'\n }\n}];"
},
"typeVersion": 2
},
{
"id": "e7561725-217d-4fcc-8c4e-d4523efb5700",
"name": "Get Video Comments",
"type": "n8n-nodes-base.httpRequest",
"position": [
1504,
192
],
"parameters": {
"url": "=https://www.googleapis.com/youtube/v3/commentThreads?part=snippet&videoId={{ $json.videoId }}&maxResults={{ $json.maxComments }}",
"options": {},
"authentication": "predefinedCredentialType",
"nodeCredentialType": "youTubeOAuth2Api"
},
"credentials": {
"youTubeOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.3
},
{
"id": "ecdcf25e-b56a-473b-aef5-42b8925a5316",
"name": "Extract Comments",
"type": "n8n-nodes-base.set",
"position": [
1744,
192
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-1",
"name": "items",
"type": "array",
"value": "={{ $json.items }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "2207e462-5298-4eb5-989c-805e495c9a26",
"name": "Process Each Comment",
"type": "n8n-nodes-base.code",
"position": [
1984,
192
],
"parameters": {
"jsCode": "// Get the items array from the Extract Comments node\nconst commentsData = $input.first().json.items;\n\n// Create an array to hold individual comment items\nconst outputItems = [];\n\n// Loop through each comment in the items array\nfor (let i = 0; i < commentsData.length; i++) {\n const comment = commentsData[i];\n \n // Create an output item for each comment with its details\n outputItems.push({\n json: {\n commentId: comment.snippet.topLevelComment.id,\n textDisplay: comment.snippet.topLevelComment.snippet.textDisplay,\n textOriginal: comment.snippet.topLevelComment.snippet.textOriginal,\n authorDisplayName: comment.snippet.topLevelComment.snippet.authorDisplayName,\n authorChannelUrl: comment.snippet.topLevelComment.snippet.authorChannelUrl,\n videoId: comment.snippet.videoId,\n channelId: comment.snippet.channelId,\n likeCount: comment.snippet.topLevelComment.snippet.likeCount,\n publishedAt: comment.snippet.topLevelComment.snippet.publishedAt,\n canReply: comment.snippet.canReply,\n totalReplyCount: comment.snippet.totalReplyCount\n }\n });\n}\n\n// Return all comment items\nreturn outputItems;"
},
"typeVersion": 2
},
{
"id": "8e792844-e6c6-46f8-88ea-52a99906ecf6",
"name": "Select Top 10 Comments",
"type": "n8n-nodes-base.code",
"position": [
2224,
192
],
"parameters": {
"jsCode": "// Get all comment items from the input\nconst comments = $input.all().map(item => item.json);\n\n// Sort by newest (publishedAt descending)\nconst newestComments = [...comments].sort((a, b) => {\n return new Date(b.publishedAt) - new Date(a.publishedAt);\n}).slice(0, 5);\n\n// Calculate engagement score and sort by it\nconst commentsByEngagement = [...comments].map(comment => ({\n ...comment,\n engagementScore: (comment.likeCount || 0) + (comment.totalReplyCount || 0)\n})).sort((a, b) => b.engagementScore - a.engagementScore);\n\n// Get top 5 by engagement\nconst topEngagementComments = commentsByEngagement.slice(0, 5);\n\n// Combine and remove duplicates based on commentId\nconst selectedComments = [...newestComments];\nconst selectedIds = new Set(newestComments.map(c => c.commentId));\n\nfor (const comment of topEngagementComments) {\n if (!selectedIds.has(comment.commentId)) {\n selectedComments.push(comment);\n selectedIds.add(comment.commentId);\n }\n}\n\n// Return as n8n items\nreturn selectedComments.map(comment => ({\n json: comment\n}));"
},
"typeVersion": 2
},
{
"id": "5edce8bd-c1ae-4729-8965-0d4ddc4831df",
"name": "Comment Response Generator",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
2464,
192
],
"parameters": {
"text": "=Comment text: {{ $json.textDisplay }}\n\nVideo ID: {{ $json.videoId }}\n\nComment author: {{ $json.authorDisplayName }}",
"options": {
"systemMessage": "You are a real person commenting on YouTube, not an AI. You are engaging as a thoughtful viewer who watched this video and wants to join the conversation with other commenters. Your task is to:\n\n1. Read the comment and respond as a fellow viewer, not as the channel owner\n2. Share your own perspective or agree/build on their point\n3. Reference something from the video to show you actually watched it\n4. Keep it natural and conversational - like chatting with someone in comments\n5. Keep responses concise (1-2 sentences max)\n6. Return the response in the specified JSON format\n\nGuidelines:\n- You're a peer viewer, NOT the video creator - never say \"thanks for watching\" or \"glad you liked my video\"\n- Be genuine and add to the discussion like a real person would\n- Ask a follow-up question or share a related thought\n- You can politely disagree or offer a different angle\n- Mention your own experience or interest in the topic when relevant\n- Sound human - use casual language, not corporate-speak\n\nWrite like a normal human:\n- simple words\n- natural tone\n- short (1-2 sentences)\n- no formal or corporate language\n\nAvoid:\n- sounding like AI\n- being promotional\n- saying \"great video\" or generic phrases\n- talking like a company\n- emojis (or max 1 if really natural)\n\nExamples:\n- \"Yeah I noticed that too, especially the part about chunking.\"\n- \"Interesting take, I actually had a different experience with that.\"\n- \"Good point, I think this becomes tricky when you have multiple docs involved.\"\n"
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 3
},
{
"id": "87e33765-2bdd-4ced-bb73-164d5d20cf52",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
2464,
416
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {},
"builtInTools": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "c077e85d-093f-498b-9db0-5bc1b4017180",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
2608,
416
],
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"commentReply\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"description\": \"The crafted reply to post as a comment response\"\n\t\t}\n\t}\n}"
},
"typeVersion": 1.3
},
{
"id": "147bbd84-4d47-4531-9d8b-9d19dead6363",
"name": "Add VideoId to Replies",
"type": "n8n-nodes-base.set",
"position": [
2864,
192
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "vid-1",
"name": "videoId",
"type": "string",
"value": "={{ $('Select Top 10 Comments').item.json.videoId }}"
},
{
"id": "7358e49c-158e-44c6-901c-5a7ee0abb409",
"name": "commentText",
"type": "string",
"value": "={{ $('Select Top 10 Comments').item.json.textDisplay }}"
},
{
"id": "44e618e3-0ed4-4847-b45a-99219601ca9c",
"name": "commentAuthor",
"type": "string",
"value": "={{ $('Select Top 10 Comments').item.json.authorDisplayName }}"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "ea49599f-bbd0-4d01-8c70-840637b3048f",
"name": "Video Comment Generator",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1504,
480
],
"parameters": {
"text": "=Video Title: {{ $json.items.snippet.title }}\n\nVideo Description: {{ $json.items.snippet.description }}",
"options": {
"systemMessage": "You are a real person leaving a top-level comment on a YouTube video you just watched. You are NOT an AI and NOT the channel owner - you are a genuine viewer sharing your thoughts.\n\nBased on the video's title and description, write ONE natural comment as if you just finished watching.\n\nYour comment should:\n1. Sound like a real viewer, not a bot\n2. Reference something specific from the title or description\n3. Share an insight, question, or personal reaction\n4. Be short (1-2 sentences max)\n5. Return the result in the specified JSON format\n\nWrite like a normal human:\n- simple words\n- natural tone\n- short (1-2 sentences)\n- no formal or corporate language\n\nAvoid:\n- sounding like AI\n- being promotional\n- saying \"great video\" or generic compliments\n- talking like a company\n- emojis (or max 1 if really natural)\n\nExamples:\n- \"The part about evaluation really clicked for me, been struggling with the same thing.\"\n- \"Curious how this holds up with bigger datasets - anyone tried?\"\n- \"Didn't expect the comparison with embeddings, makes a lot of sense now.\"\n"
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 3
},
{
"id": "f5e4c781-7c3e-4382-bfb7-22953ff3039a",
"name": "Video Comment Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
1568,
704
],
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"videoComment\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"description\": \"The crafted comment to post on the video\"\n\t\t}\n\t}\n}"
},
"typeVersion": 1.3
},
{
"id": "002e81ce-8a76-402a-9e21-057c6614d2d6",
"name": "Add VideoId to VideoComments",
"type": "n8n-nodes-base.set",
"position": [
1904,
624
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "vid-2",
"name": "videoId",
"type": "string",
"value": "={{ $('Format Video Data').first().json.items.id }}"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "a32771a0-b038-4139-89b0-4cf3de37cd37",
"name": "Merge Wait for Video Comment",
"type": "n8n-nodes-base.merge",
"position": [
3104,
480
],
"parameters": {
"mode": "combine",
"options": {},
"fieldsToMatchString": "=videoId"
},
"typeVersion": 3.1
},
{
"id": "c85a77f5-5fc4-4085-9458-001a22a4169f",
"name": "Prepare Sheet Data",
"type": "n8n-nodes-base.set",
"position": [
3344,
480
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "id-11",
"name": "videoId",
"type": "string",
"value": "={{ $json.videoId }}"
},
{
"id": "id-1",
"name": "videoUrl",
"type": "string",
"value": "=https://www.youtube.com/watch?v={{ $json.videoId }}"
},
{
"id": "id-2",
"name": "videoTitle",
"type": "string",
"value": "={{ $('Format Video Data').first().json.items.snippet.title || '' }}"
},
{
"id": "id-3",
"name": "commentAuthor",
"type": "string",
"value": "={{ $json.commentAuthor || '' }}"
},
{
"id": "id-4",
"name": "commentText",
"type": "string",
"value": "={{ $json.commentText }}"
},
{
"id": "id-5",
"name": "myReply",
"type": "string",
"value": "={{ $json.output?.commentReply || '' }}"
},
{
"id": "id-6",
"name": "myVideoComment",
"type": "string",
"value": "={{ $json.output?.videoComment || '' }}"
},
{
"id": "id-7",
"name": "selectionType",
"type": "string",
"value": "=Direct Link Input"
},
{
"id": "id-8",
"name": "videoTags",
"type": "string",
"value": "={{ ($('Format Video Data').first().json.items.snippet.tags || []).join(', ') }}"
},
{
"id": "id-9",
"name": "viewCount",
"type": "number",
"value": "={{ $('Format Video Data').first().json.viewCount || 0 }}"
},
{
"id": "id-10",
"name": "engagementScore",
"type": "number",
"value": "={{ $('Format Video Data').first().json.engagementScore || 0 }}"
},
{
"id": "id-12",
"name": "videoDescription",
"type": "string",
"value": "={{ $('Format Video Data').first().json.items.snippet.description || '' }}"
},
{
"id": "id-13",
"name": "likeCount",
"type": "number",
"value": "={{ $('Format Video Data').first().json.items.statistics.likeCount || 0 }}"
},
{
"id": "id-14",
"name": "commentCount",
"type": "number",
"value": "={{ $('Format Video Data').first().json.items.statistics.commentCount || 0 }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "748dba0f-bdc1-4f7c-8526-4757b31238ea",
"name": "Save to Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
3584,
480
],
"parameters": {
"columns": {
"value": {},
"schema": [
{
"id": "videoId ",
"type": "string",
"display": true,
"required": false,
"displayName": "videoId ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "videoUrl ",
"type": "string",
"display": true,
"required": false,
"displayName": "videoUrl ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "videoTitle",
"type": "string",
"display": true,
"required": false,
"displayName": "videoTitle",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "commentAuthor",
"type": "string",
"display": true,
"required": false,
"displayName": "commentAuthor",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "commentText ",
"type": "string",
"display": true,
"required": false,
"displayName": "commentText ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "myReply ",
"type": "string",
"display": true,
"required": false,
"displayName": "myReply ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "myVideoComment",
"type": "string",
"display": true,
"required": false,
"displayName": "myVideoComment",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "selectionType",
"type": "string",
"display": true,
"required": false,
"displayName": "selectionType",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "videoTags ",
"type": "string",
"display": true,
"required": false,
"displayName": "videoTags ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "viewCount",
"type": "string",
"display": true,
"required": false,
"displayName": "viewCount",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "engagementScore",
"type": "string",
"display": true,
"required": false,
"displayName": "engagementScore",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": " videoDescription",
"type": "string",
"display": true,
"required": false,
"displayName": " videoDescription",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "likeCount ",
"type": "string",
"display": true,
"required": false,
"displayName": "likeCount ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "commentCount",
"type": "string",
"display": true,
"required": false,
"displayName": "commentCount",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "autoMapInputData",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultUrl": "",
"cachedResultName": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultUrl": "",
"cachedResultName": ""
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "9fae5247-3dc3-4aaa-a4cc-0ebe1fc3fefc",
"name": "Sticky Note 0001",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1024,
304
],
"parameters": {
"color": 7,
"width": 1240,
"height": 460,
"content": "## AI YouTube Comment & Reply Generator\n\nSubmit a YouTube URL -> get AI-generated **replies to top comments** + **one original video comment**, all saved to Google Sheets for easy review and posting.\n\n### Credentials required\n- **YouTube Data API (OAuth2)** - for fetching video stats and comments\n- **OpenAI API** - for the GPT-4 agents\n- **Google Sheets (OAuth2)** - for saving results\n\n### Quick setup (5-10 min)\n1. Connect the three credentials above\n2. In **Save to Google Sheets** -> pick your destination sheet (column names are listed in the sticky note near that node)\n3. Optional: tweak the system prompts in both AI agents to match your niche and tone\n4. Activate the workflow and submit a video URL via the form\n\n### Tips\n- Works with any YouTube URL format (watch, youtu.be, embed, or plain ID)\n- Uses gpt-4.1-mini by default - swap for any OpenAI model you prefer\n- The **Max Comments to Process** form field lets you cap the API cost per run\n"
},
"typeVersion": 1
},
{
"id": "fc55f55f-b4cf-41fb-8883-61325e8aa24b",
"name": "Sticky Note 0002",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
-16
],
"parameters": {
"color": 4,
"width": 560,
"height": 248,
"content": "## Step 1 - Input & Extract Video ID\n\nA **Form Trigger** accepts any YouTube URL:\n- youtube.com/watch?v=ID\n- youtu.be/ID\n- youtube.com/embed/ID\n- Or just the plain VIDEO_ID\n\nThe **Code** node normalises the URL and extracts a clean video ID for downstream API calls."
},
"typeVersion": 1
},
{
"id": "f727fb07-60e1-4039-a105-577bb9efb589",
"name": "Sticky Note 0003",
"type": "n8n-nodes-base.stickyNote",
"position": [
832,
32
],
"parameters": {
"color": 3,
"width": 500,
"height": 200,
"content": "## Step 2 - Fetch Video Stats\n\nCalls **YouTube Data API v3** (/videos endpoint) to pull:\n- Title, description, tags\n- View / like / comment counts\n\n**Format Video Data** restructures the response for the rest of the flow."
},
"typeVersion": 1
},
{
"id": "bd8cf657-7d0d-466a-9fc0-6fed20103077",
"name": "Sticky Note 0004",
"type": "n8n-nodes-base.stickyNote",
"position": [
1408,
-176
],
"parameters": {
"color": 6,
"width": 900,
"height": 220,
"content": "## Step 3 - Get & Rank Top Comments\n\n1. Fetches comments via commentThreads (capped by the form's Max Comments field)\n2. Flattens each comment into its own item\n3. **Select Top 10 Comments** merges two lists, deduped by commentId:\n - 5 **newest** comments (by publishedAt)\n - 5 **highest-engagement** comments (likes + replies)\n\nYou always get ~10 unique, high-value comments worth responding to."
},
"typeVersion": 1
},
{
"id": "6b88a98b-a4f5-4662-b8aa-e3bee51fcc9d",
"name": "Sticky Note 0005",
"type": "n8n-nodes-base.stickyNote",
"position": [
2432,
-160
],
"parameters": {
"color": 5,
"width": 700,
"height": 248,
"content": "## Step 4a - AI Replies to Each Comment\n\n**Comment Response Generator** writes a natural, human-sounding reply per comment.\n\n- Responds as a fellow viewer, never as the creator\n- Uses GPT-4.1-mini (cheap + fast) - swap models freely\n- **Structured Output Parser** guarantees a clean commentReply field\n- Style rules live in the system prompt: short, casual, no corporate tone\n\nEdit the system prompt to match your niche, voice, or brand."
},
"typeVersion": 1
},
{
"id": "de20262a-1fd1-4e85-b20c-6a999ca1b403",
"name": "Sticky Note 0006",
"type": "n8n-nodes-base.stickyNote",
"position": [
1488,
864
],
"parameters": {
"color": 5,
"width": 540,
"height": 380,
"content": "## Step 4b - AI Writes a Video Comment\n\nRuns in parallel. **Video Comment Generator** writes ONE original top-level comment from the video's title + description.\n\nShares the OpenAI model with the reply agent. Merged with the replies downstream."
},
"typeVersion": 1
},
{
"id": "1a6de9b2-7972-415f-9ba1-d9d32a731cb7",
"name": "Sticky Note 0007",
"type": "n8n-nodes-base.stickyNote",
"position": [
3360,
-96
],
"parameters": {
"color": 3,
"width": 780,
"height": 280,
"content": "## Step 5 - Merge & Save to Google Sheets\n\n- **Merge** joins each reply with the video comment by videoId\n- **Prepare Sheet Data** normalises the final row shape\n- **Save to Google Sheets** appends one row per reply\n\n### Required columns in your sheet\nvideoId, videoUrl, videoTitle, commentAuthor, commentText, myReply, myVideoComment, selectionType, videoTags, viewCount, engagementScore, videoDescription, likeCount, commentCount\n\nIMPORTANT: After importing, open this node and select your own Sheet + tab."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "",
"connections": {
"Extract Comments": {
"main": [
[
{
"node": "Process Each Comment",
"type": "main",
"index": 0
}
]
]
},
"Format Video Data": {
"main": [
[
{
"node": "Get Video Comments",
"type": "main",
"index": 0
},
{
"node": "Video Comment Generator",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "Comment Response Generator",
"type": "ai_languageModel",
"index": 0
},
{
"node": "Video Comment Generator",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Get Video Comments": {
"main": [
[
{
"node": "Extract Comments",
"type": "main",
"index": 0
}
]
]
},
"Prepare Sheet Data": {
"main": [
[
{
"node": "Save to Google Sheets",
"type": "main",
"index": 0
}
]
]
},
"Get Video Statistics": {
"main": [
[
{
"node": "Format Video Data",
"type": "main",
"index": 0
}
]
]
},
"Process Each Comment": {
"main": [
[
{
"node": "Select Top 10 Comments",
"type": "main",
"index": 0
}
]
]
},
"Video Comment Parser": {
"ai_outputParser": [
[
{
"node": "Video Comment Generator",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Video URL Input Form": {
"main": [
[
{
"node": "Extract Video ID from URL",
"type": "main",
"index": 0
}
]
]
},
"Add VideoId to Replies": {
"main": [
[
{
"node": "Merge Wait for Video Comment",
"type": "main",
"index": 0
}
]
]
},
"Select Top 10 Comments": {
"main": [
[
{
"node": "Comment Response Generator",
"type": "main",
"index": 0
}
]
]
},
"Video Comment Generator": {
"main": [
[
{
"node": "Add VideoId to VideoComments",
"type": "main",
"index": 0
}
]
]
},
"Structured Output Parser": {
"ai_outputParser": [
[
{
"node": "Comment Response Generator",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Extract Video ID from URL": {
"main": [
[
{
"node": "Get Video Statistics",
"type": "main",
"index": 0
}
]
]
},
"Comment Response Generator": {
"main": [
[
{
"node": "Add VideoId to Replies",
"type": "main",
"index": 0
}
]
]
},
"Add VideoId to VideoComments": {
"main": [
[
{
"node": "Merge Wait for Video Comment",
"type": "main",
"index": 1
}
]
]
},
"Merge Wait for Video Comment": {
"main": [
[
{
"node": "Prepare Sheet Data",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googleSheetsOAuth2ApiopenAiApiyouTubeOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
A form trigger accepts any YouTube URL: youtube.com/watch?v=ID, youtu.be/ID, youtube.com/embed/ID, or just a plain video ID A code node normalises the URL and extracts a clean video ID for the YouTube Data API v3 Calls the API to fetch video stats (title, description, tags,…
Source: https://n8n.io/workflows/15175/ — 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.
🎯 Create viral TikToks, Shorts, Reels, podcasts, and ASMR videos in minutes — all on autopilot.
Digistars - Scrape & Crawl. Uses httpRequest, n8n-nodes-firecrawl-scraper, googleSheets, lmChatOpenAi. Event-driven trigger; 63 nodes.
🧠 Automate end-to-end SEO blog creation and WordPress publishing using a GPT-5 multi-agent workflow with real-time research, metadata generation, and optional featured images.
The workflow runs every hour with a randomized delay of 5–20 minutes to help distribute load. It records the exact date and time a lead is emailed so you can track outreach. Follow-ups are automatical
Transform your manual hiring process into an intelligent evaluation system that saves 15-20 minutes per candidate! This workflow automates the entire candidate assessment pipeline - from CSV/XLSX uplo