This workflow corresponds to n8n.io template #12696 — we link there as the canonical source.
This workflow follows the Emailsend → Google Sheets 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": "zkDtESTU1mMRsiQN",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "RedditEmailSender - Template",
"tags": [],
"nodes": [
{
"id": "f4d0ce16-97da-4a58-b05d-bf70fda27342",
"name": "Schedule",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
160,
0
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 7
}
]
}
},
"typeVersion": 1.3
},
{
"id": "b3aaf843-45a8-451a-b92b-1503e12569d0",
"name": "Read Sources",
"type": "n8n-nodes-base.googleSheets",
"position": [
352,
0
],
"parameters": {
"options": {},
"filtersUI": {
"values": [
{
"lookupValue": "true",
"lookupColumn": "enabled"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/[docid]/edit#gid=0",
"cachedResultName": "Sources"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "[yourdocid]",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/[docid]/edit?usp=drivesdk",
"cachedResultName": "RedditMonitor"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "35983a34-4ad4-4461-abf0-1701de205fc0",
"name": "Build Feed URL",
"type": "n8n-nodes-base.set",
"position": [
560,
0
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "22daf8ac-4b6f-412f-b0cd-e705fa6ec1af",
"name": "rss_url",
"type": "string",
"value": "=https://www.reddit.com/r/{{$json.subreddit}}/{{$json.feed_type}}/.rss"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "583ee73f-2969-4320-a2c3-b13653121de1",
"name": "Fetch Feed XML1",
"type": "n8n-nodes-base.httpRequest",
"position": [
704,
0
],
"parameters": {
"url": "={{$json.rss_url}}\n",
"options": {},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "Mozilla/5.0 (compatible; RedditListener/1.0)"
}
]
}
},
"typeVersion": 4.3
},
{
"id": "9bec2a3f-8952-4fbf-adec-ec29566aaf39",
"name": "Split Atom Entries",
"type": "n8n-nodes-base.code",
"position": [
976,
0
],
"parameters": {
"jsCode": "// Split Atom Entries (Run once for all items)\n// Input: many items, each item is parsed XML JSON for ONE subreddit feed\n// Output: one item per post entry, across ALL feeds\n\nconst nowIso = new Date().toISOString();\nconst out = [];\n\nfor (const item of $input.all()) {\n const j = item.json;\n\n // Atom feeds from Reddit usually look like { feed: { ... entry: [...] } }\n const feed = j.feed ?? j.rss ?? j;\n const entriesRaw = feed?.entry ?? [];\n\n const entries = Array.isArray(entriesRaw) ? entriesRaw : [entriesRaw];\n\n // Try to infer subreddit name\n const subreddit =\n feed?.category?.label?.replace(/^r\\//, \"\") ||\n feed?.title?.replace(/^newest submissions :\\s*/i, \"\") ||\n j.subreddit ||\n j.subreddit_key ||\n \"unknown\";\n\n for (const e of entries) {\n // Link can be object or array depending on parser\n let url = \"\";\n if (typeof e?.link === \"string\") url = e.link;\n else if (e?.link?.href) url = e.link.href;\n else if (Array.isArray(e?.link)) {\n url =\n e.link.find((l) => l?.rel === \"alternate\")?.href ||\n e.link.find((l) => l?.href)?.href ||\n \"\";\n }\n\n out.push({\n json: {\n subreddit,\n subreddit_key: subreddit,\n title: e?.title ?? \"\",\n url,\n published: e?.published ?? e?.updated ?? \"\",\n author: e?.author?.name ?? \"\",\n post_id: e?.id ?? \"\",\n date_found: nowIso,\n // keep raw entry if you want for later debugging\n // _raw: e,\n },\n });\n }\n}\n\nreturn out;\n"
},
"typeVersion": 2
},
{
"id": "d2c0a2a9-08cf-4a5e-a017-5c0167409c16",
"name": "Parse XML",
"type": "n8n-nodes-base.xml",
"position": [
848,
0
],
"parameters": {
"options": {}
},
"typeVersion": 1
},
{
"id": "d1082b91-9a76-468b-9dfe-9a8f409f557a",
"name": "Normalize Post Fields",
"type": "n8n-nodes-base.set",
"position": [
1136,
0
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "acae7211-b8ae-4b7d-9d8a-d15f5f6d47f3",
"name": "date_found",
"type": "string",
"value": "={{$now}}"
},
{
"id": "413146fd-98d3-4b0a-b8b3-fb1c78839f1e",
"name": "subreddit",
"type": "string",
"value": "={{ $json.subreddit }}"
},
{
"id": "a2886b8a-570b-47c5-afbf-2cfddca7f829",
"name": "title",
"type": "string",
"value": "={{$json.title}}"
},
{
"id": "8eed9c4a-3c18-4449-9652-287b5beea705",
"name": "url",
"type": "string",
"value": "={{ $json.url }}"
},
{
"id": "22ce382c-aec7-41ea-981d-f759cbae72d0",
"name": "published",
"type": "string",
"value": "={{$json.published}}"
},
{
"id": "e72464e1-9a41-4acb-b1c6-45501d4135f2",
"name": "author",
"type": "string",
"value": "={{ $json.author }}"
},
{
"id": "74800356-a09a-4a4e-93dd-e08979364a0d",
"name": "post_id",
"type": "string",
"value": "={{ $json.post_id }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "4bb91a2c-9f68-4ece-954c-4072b4f8a4c6",
"name": "Read Keywords",
"type": "n8n-nodes-base.googleSheets",
"position": [
1344,
128
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 1446769317,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/[docid]/edit#gid=1446769317",
"cachedResultName": "Keywords"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "[docid]",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/[docid]/edit?usp=drivesdk",
"cachedResultName": "RedditMonitor"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "990b5c40-3dce-402b-a473-f8b4bb7e22fb",
"name": "Scoring",
"type": "n8n-nodes-base.code",
"position": [
1680,
-48
],
"parameters": {
"jsCode": "function norm(s) {\n return (s ?? '')\n .toString()\n .toLowerCase()\n .replace(/[\\u2010-\\u2015]/g, '-') // normalize dash variants\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nconst all = $input.all().map(i => i.json);\n\n// Keyword rows have include/exclude columns\nconst keywordRows = all.filter(r => ('include' in r) || ('exclude' in r));\nconst postsRaw = all.filter(r => !('include' in r) && !('exclude' in r));\n\nconst includeList = keywordRows.map(r => norm(r.include)).filter(Boolean);\nconst excludeList = keywordRows.map(r => norm(r.exclude)).filter(Boolean);\n\nfunction findHits(text, list) {\n const hits = [];\n for (const k of list) if (k && text.includes(k)) hits.push(k);\n return hits;\n}\n\nconst painPhrases = [\n \"losing track\",\n \"too many messages\",\n \"forgot to follow up\",\n \"missed follow up\",\n \"fell through the cracks\",\n \"keep forgetting\",\n \"hard to keep track\",\n \"overwhelmed\",\n];\n\nconst W = {\n includeHit: 12,\n titleHitBonus: 8,\n questionBonus: 6,\n painBonus: 10,\n};\n\n// Try to derive subreddit/source key from a few common shapes\nfunction getSubKey(p) {\n // preferred: explicit field you add in Normalize\n const direct =\n p.subreddit ||\n p.subreddit_name ||\n p.source ||\n p.source_name ||\n p.category_label ||\n p.category?.label ||\n p.feed_title ||\n '';\n\n let s = (direct ?? '').toString().trim();\n\n // if it's like \"r/SaaS\", keep that\n if (s) return s;\n\n // last resort: try parsing from URL\n const href = p.url || p.link?.href || '';\n const m = href.toString().match(/reddit\\.com\\/r\\/([^/]+)\\//i);\n if (m && m[1]) return `r/${m[1]}`;\n\n return 'unknown';\n}\n\nfunction parseTime(p) {\n // prefer published then updated\n const t = p.published || p.updated || p.date || p.date_found || '';\n const d = new Date(t);\n return isNaN(d.getTime()) ? 0 : d.getTime();\n}\n\n// Score posts\nconst scored = postsRaw.map(p => {\n const titleRaw = (p.title ?? '').toString();\n const title = norm(p.title);\n const body = norm(p.content_html || p.content || '');\n const text = `${title} ${body}`.trim();\n\n // hard exclude\n const excludeHits = findHits(text, excludeList);\n if (excludeHits.length > 0) return null;\n\n const includeHits = findHits(text, includeList);\n\n let score = 0;\n score += includeHits.length * W.includeHit;\n\n for (const k of includeHits) if (title.includes(k)) score += W.titleHitBonus;\n if (titleRaw.includes('?')) score += W.questionBonus;\n\n for (const pz of painPhrases) {\n if (text.includes(pz)) {\n score += W.painBonus;\n break;\n }\n }\n\n const subKey = getSubKey(p);\n const ts = parseTime(p);\n\n return {\n ...p,\n score,\n include_hits: includeHits.join(', '),\n exclude_hits: excludeHits.join(', '),\n subreddit_key: subKey,\n sort_ts: ts,\n };\n}).filter(Boolean);\n\n// Start permissive; tighten later if needed\nconst MIN_SCORE = 1;\nconst candidates = scored.filter(p => p.score >= MIN_SCORE);\n\n// Group by subreddit_key\nconst groups = new Map();\nfor (const p of candidates) {\n const k = p.subreddit_key || 'unknown';\n if (!groups.has(k)) groups.set(k, []);\n groups.get(k).push(p);\n}\n\n// Take top 5 per subreddit (sort by score desc, then recency desc)\nconst TOP_N = 5;\nconst out = [];\nfor (const [k, arr] of groups.entries()) {\n arr.sort((a, b) => {\n if (b.score !== a.score) return b.score - a.score;\n return (b.sort_ts || 0) - (a.sort_ts || 0);\n });\n out.push(...arr.slice(0, TOP_N));\n}\n\n// Optional: sort final list by score/recency so your digest looks good\nout.sort((a, b) => {\n if (b.score !== a.score) return b.score - a.score;\n return (b.sort_ts || 0) - (a.sort_ts || 0);\n});\n\n// Clean up helper fields if you don't want them in Output\n// (leave subreddit_key if you want to group in your sheet)\nreturn out.map(p => {\n const { sort_ts, ...rest } = p;\n return { json: rest };\n});\n"
},
"typeVersion": 2
},
{
"id": "f8aa0916-b771-4370-86d4-50f86d3636c9",
"name": "Collect Candidates",
"type": "n8n-nodes-base.code",
"position": [
1840,
-48
],
"parameters": {
"jsCode": "const candidates = $input.all().map(i => i.json);\nreturn [{ json: { candidates } }];\n"
},
"typeVersion": 2
},
{
"id": "ae8a88d4-ecee-4c6f-9bdc-b26f6a1ac7f2",
"name": "Read Seen",
"type": "n8n-nodes-base.googleSheets",
"position": [
1984,
-48
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 25579448,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/[docid]/edit#gid=25579448",
"cachedResultName": "Seen"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "[docid]",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/[docid]/edit?usp=drivesdk",
"cachedResultName": "RedditMonitor"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7,
"alwaysOutputData": true
},
{
"id": "e3c8cbfb-9e21-4c89-981a-9a76be0b1888",
"name": "Filter New Only",
"type": "n8n-nodes-base.code",
"position": [
2128,
-48
],
"parameters": {
"jsCode": "const seenRows = $input.all().map(i => i.json);\nconst candidates = $node[\"Collect Candidates\"].json.candidates || [];\n\nconst seen = new Set(\n seenRows.map(r => (r.post_id ?? '').toString().trim()).filter(Boolean)\n);\n\nconst fresh = candidates.filter(c => {\n const id = (c.post_id ?? '').toString().trim();\n return id && !seen.has(id);\n});\n\nreturn fresh.map(x => ({ json: x }));\n"
},
"typeVersion": 2
},
{
"id": "f725d452-1d63-4fcc-aae7-6ec2fb92f1ac",
"name": "Append Seen",
"type": "n8n-nodes-base.googleSheets",
"position": [
2304,
-48
],
"parameters": {
"columns": {
"value": {
"title": "={{ $json.title }}",
"post_id": "={{ $json.post_id }}",
"post_url": "={{ $json.url }}",
"subreddit": "={{ $json.subreddit }}",
"first_seen_utc": "={{new Date().toISOString()}}"
},
"schema": [
{
"id": "post_id",
"type": "string",
"display": true,
"required": false,
"displayName": "post_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "first_seen_utc",
"type": "string",
"display": true,
"required": false,
"displayName": "first_seen_utc",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "subreddit",
"type": "string",
"display": true,
"required": false,
"displayName": "subreddit",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "title",
"type": "string",
"display": true,
"required": false,
"displayName": "title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "post_url",
"type": "string",
"display": true,
"required": false,
"displayName": "post_url",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 25579448,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/[docid]edit#gid=25579448",
"cachedResultName": "Seen"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "[docid]",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/[docid]/edit?usp=drivesdk",
"cachedResultName": "RedditMonitor"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "e44e8a88-8852-4b27-8b89-d7603e67fb97",
"name": "Build Email Digest",
"type": "n8n-nodes-base.code",
"position": [
2352,
208
],
"parameters": {
"jsCode": "/**\n * n8n Code node (Run once for all items)\n * Builds ONE HTML email grouped by subreddit.\n *\n * Expected fields on each input item:\n * - title (string)\n * - post_url (string) <-- REQUIRED for clickable title\n * - subreddit (string)\n * - score (number)\n * Optional:\n * - published_local (string)\n * - include_hits (string)\n */\n\nfunction fmtChicago(iso) {\n const d = new Date(iso);\n if (isNaN(d.getTime())) return \"\";\n return d.toLocaleString(\"en-US\", {\n timeZone: \"America/Chicago\",\n weekday: \"short\",\n month: \"short\",\n day: \"2-digit\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n}\n\nfunction safeText(v) {\n // Basic HTML escaping so weird titles don't break the email\n const s = (v ?? \"\").toString();\n return s\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n}\n\nconst posts = $input.all().map((i) => i.json);\n\nconst nowIso = new Date().toISOString();\nconst dateLabel = fmtChicago(nowIso);\n\n// If no posts, still send a friendly email\nif (posts.length === 0) {\n return [\n {\n json: {\n subject: `[Subject] Reddit Digest \u2014 ${dateLabel} (no new posts)`,\n html: `\n<div style=\"font-family: Arial, sans-serif; line-height: 1.4;\">\n <h2 style=\"margin:0 0 8px;\">\ud83d\udcec [Your company] Reddit Digest</h2>\n <p style=\"margin:0 0 12px; color:#444;\">${safeText(dateLabel)}</p>\n <p style=\"margin:0; color:#444;\">No new matching posts today.</p>\n</div>`,\n },\n },\n ];\n}\n\n// Group by subreddit\nconst groups = {};\nfor (const p of posts) {\n const sub = (p.subreddit || \"unknown\").toString();\n if (!groups[sub]) groups[sub] = [];\n groups[sub].push(p);\n}\n\n// Sort each group by score (desc)\nfor (const sub of Object.keys(groups)) {\n groups[sub].sort((a, b) => (b.score || 0) - (a.score || 0));\n}\n\n// Sort subreddits by best score (desc)\nconst subredditKeys = Object.keys(groups).sort((a, b) => {\n const aTop = groups[a][0]?.score || 0;\n const bTop = groups[b][0]?.score || 0;\n return bTop - aTop;\n});\n\n// Build HTML\nlet html = `\n<div style=\"font-family: Arial, sans-serif; line-height: 1.4;\">\n <h2 style=\"margin:0 0 6px;\">\ud83d\udcec [Your Company] Reddit Digest</h2>\n <p style=\"margin:0 0 14px;color:#444;\">${safeText(dateLabel)} \u00b7 ${posts.length} new posts</p>\n <hr style=\"border:none;border-top:1px solid #eee;margin:12px 0;\" />\n`;\n\n// Build each subreddit section\nfor (const sub of subredditKeys) {\n html += `<h3 style=\"margin:16px 0 8px;\">r/${safeText(sub)}</h3>`;\n html += `<ul style=\"padding-left:18px;margin:0;\">`;\n\n for (const p of groups[sub]) {\n const title = safeText(p.title || \"(no title)\");\n const url = (p.url || p.post_url || p.link?.href || '').toString().trim(); // REQUIRED for clickable title\n const score = p.score ?? \"\";\n const when = p.published_local ? safeText(p.published_local) : \"\";\n const hits = p.include_hits ? safeText(p.include_hits) : \"\";\n\n // Fallback if URL missing (still show title, but not clickable)\n const titleHtml = url\n ? `<a href=\"${url}\" target=\"_blank\" style=\"text-decoration:none;color:#000;\"><strong>${title}</strong></a>`\n : `<strong>${title}</strong>`;\n\n\n html += `\n <li style=\"margin:0 0 12px;\">\n ${titleHtml}<br/>\n <span style=\"color:#666;font-size:12px;\">\n score: ${safeText(score)}\n ${when ? ` \u00b7 ${when}` : ``}\n ${hits ? ` \u00b7 hits: ${hits}` : ``}\n </span>\n </li>\n `;\n }\n\n html += `</ul>`;\n html += `<hr style=\"border:none;border-top:1px solid #eee;margin:14px 0;\" />`;\n}\n\nhtml += `</div>`;\n\nreturn [\n {\n json: {\n subject: `[Your Company] Reddit Digest \u2014 ${dateLabel} (${posts.length} new)`,\n html,\n },\n },\n];\n"
},
"typeVersion": 2
},
{
"id": "0c28a96a-aec0-470a-8206-8e303497a1e0",
"name": "Send email",
"type": "n8n-nodes-base.emailSend",
"position": [
2528,
208
],
"parameters": {
"html": "={{ $json.html }}",
"options": {},
"subject": "={{ $json.subject }}",
"toEmail": "[youremail]@info.com",
"fromEmail": "[youremail]@info.com"
},
"credentials": {
"smtp": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "d50f0eaf-09d6-4f2f-aa16-7867fd2f3ec9",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-528,
-320
],
"parameters": {
"width": 560,
"height": 656,
"content": "## SCORE REDDIT POSTS AND EMAIL THEM TO YOURSELF EVERY MORNING TO RESPOND\n\n### How it works\n1. Set up Google sheets with configuration (Subreddits and keywords) \n2. Pull Reddit via RSS \n3. Cleanup/Normalize Posts \n4. Scan each post for keywords\n5. Score posts \n6. Update Google Sheets with posts seen for tracking \n7. Email top scoring posts\n\n\n### Setup steps\n1. Create google sheet with subreddit lists \n2. Create separate google sheet with one column of include keywords and one column with exclude keywords \n3. Set up Google Sheets credentials \n4. Set up SMTP credentials "
},
"typeVersion": 1
},
{
"id": "048b489f-5923-495e-a86b-672be8475205",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
304,
-96
],
"parameters": {
"color": 7,
"width": 192,
"height": 256,
"content": "### Read in sources"
},
"typeVersion": 1
},
{
"id": "024f1fed-9721-476f-80f6-2aff2218f46d",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
-96
],
"parameters": {
"color": 7,
"width": 272,
"height": 272,
"content": "### Build XML feed"
},
"typeVersion": 1
},
{
"id": "9295b2f9-d13f-4c77-afad-13bf5a8c4b15",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
832,
-96
],
"parameters": {
"color": 7,
"width": 400,
"height": 272,
"content": "### Clean and normalize posts"
},
"typeVersion": 1
},
{
"id": "a97c1a62-f39d-4fab-9994-937c8b8af3da",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1296,
-112
],
"parameters": {
"color": 7,
"width": 304,
"height": 400,
"content": "### Keywords for every pos"
},
"typeVersion": 1
},
{
"id": "bca34b32-0c24-4371-8490-e503f6c4fa25",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"position": [
1472,
-32
],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "e3dd9cff-835a-467c-bad8-fa223d086a4f",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
1664,
-112
],
"parameters": {
"color": 7,
"width": 288,
"height": 256,
"content": "### Score and filter posts"
},
"typeVersion": 1
},
{
"id": "a4e4fda2-d724-45bc-bae3-f8cdc8a2b547",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
1968,
-112
],
"parameters": {
"color": 7,
"width": 448,
"height": 256,
"content": "### Keep only new posts"
},
"typeVersion": 1
},
{
"id": "b6e1e0dd-697f-4a23-80ef-c6a638cac2e2",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
2304,
160
],
"parameters": {
"color": 7,
"width": 400,
"height": 256,
"content": "### Build and Send email"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "327ecc2f-8c83-4947-baf8-c8a17d53b9a7",
"connections": {
"Merge": {
"main": [
[
{
"node": "Scoring",
"type": "main",
"index": 0
}
]
]
},
"Scoring": {
"main": [
[
{
"node": "Collect Candidates",
"type": "main",
"index": 0
}
]
]
},
"Schedule": {
"main": [
[
{
"node": "Read Sources",
"type": "main",
"index": 0
}
]
]
},
"Parse XML": {
"main": [
[
{
"node": "Split Atom Entries",
"type": "main",
"index": 0
}
]
]
},
"Read Seen": {
"main": [
[
{
"node": "Filter New Only",
"type": "main",
"index": 0
}
]
]
},
"Read Sources": {
"main": [
[
{
"node": "Build Feed URL",
"type": "main",
"index": 0
}
]
]
},
"Read Keywords": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Build Feed URL": {
"main": [
[
{
"node": "Fetch Feed XML1",
"type": "main",
"index": 0
}
]
]
},
"Fetch Feed XML1": {
"main": [
[
{
"node": "Parse XML",
"type": "main",
"index": 0
}
]
]
},
"Filter New Only": {
"main": [
[
{
"node": "Append Seen",
"type": "main",
"index": 0
},
{
"node": "Build Email Digest",
"type": "main",
"index": 0
}
]
]
},
"Build Email Digest": {
"main": [
[
{
"node": "Send email",
"type": "main",
"index": 0
}
]
]
},
"Collect Candidates": {
"main": [
[
{
"node": "Read Seen",
"type": "main",
"index": 0
}
]
]
},
"Split Atom Entries": {
"main": [
[
{
"node": "Normalize Post Fields",
"type": "main",
"index": 0
}
]
]
},
"Normalize Post Fields": {
"main": [
[
{
"node": "Read Keywords",
"type": "main",
"index": 0
},
{
"node": "Merge",
"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.
googleSheetsOAuth2Apismtp
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This is for founders, service providers and anyone who wants to do more social listening but doesn't want to pay for an expensive tool.
Source: https://n8n.io/workflows/12696/ — 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.
This enterprise-grade n8n workflow automates competitor monitoring on Instagram — from post fetching to AI-driven strategy alerts — using Claude AI, Instagram API, and multi-channel notifications. It
This n8n workflow provides automated monitoring of YouTube channels and sends real-time notifications to RocketChat when new videos are published. It supports all YouTube URL formats, uses dual-source
This enterprise-grade n8n workflow automates the Instagram complaint handling process — from detection to resolution — using Claude AI, dynamic ticket assignment, and SLA enforcement. It converts cust
This workflow automatically mirrors your YouTube to TikTok and Instagram, so you don’t have to manually download and re-upload your content across platforms.
This enterprise-grade n8n workflow automates influencer contract compliance for Instagram campaigns — from deadline tracking to breach detection — using Claude AI, Instagram API, and smart reminders.