AutomationFlowsSocial Media › Aggregate Reddit India & Career Subs via RSS

Aggregate Reddit India & Career Subs via RSS

Original n8n title: Reddit — India + Student Career Subs (rss Aggregator)

Reddit — India + student career subs (RSS aggregator). Uses httpRequest. Scheduled trigger; 12 nodes.

Cron / scheduled trigger★★★★☆ complexity12 nodesHTTP Request
Social Media Trigger: Cron / scheduled Nodes: 12 Complexity: ★★★★☆ Added:

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 →

Download .json
{
  "name": "Reddit \u2014 India + student career subs (RSS aggregator)",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 12
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        0,
        0
      ],
      "id": "a0red005-0000-4000-8000-000000000001",
      "name": "Schedule Trigger"
    },
    {
      "parameters": {
        "jsCode": "// === Reddit subreddit aggregator ===\n// Fetches Atom feed (.rss endpoint returns Atom XML) for each subreddit\n// and flattens posts into a single item list. Reddit gates by User-Agent;\n// without a real-looking UA it returns 429s.\n//\n// Heavy keyword filter is applied later via the IF node; community posts\n// are noisy.\n\nconst SUBREDDITS = [\n  { slug: 'developersIndia',         displayName: 'r/developersIndia' },\n  { slug: 'cscareerquestionsIndia',  displayName: 'r/cscareerquestionsIndia' },\n  { slug: 'csMajors',                displayName: 'r/csMajors' }\n];\n\n// Per-subreddit cap so one busy sub doesn't dominate the run.\nconst PER_SUB_LIMIT = 10;\n\nfunction unescapeEntities(s) {\n  if (!s) return '';\n  return String(s)\n    .replace(/&lt;/g, '<')\n    .replace(/&gt;/g, '>')\n    .replace(/&amp;/g, '&')\n    .replace(/&quot;/g, '\"')\n    .replace(/&#39;/g, \"'\")\n    .replace(/&nbsp;/g, ' ');\n}\n\nfunction stripHtml(s) {\n  if (!s) return '';\n  return unescapeEntities(String(s))\n    .replace(/<[^>]*>/g, ' ')\n    .replace(/\\s+/g, ' ')\n    .trim();\n}\n\n// Parse Atom <entry> blocks via regex \u2014 Reddit's feeds are simple.\nfunction parseAtomEntries(xml) {\n  const entries = [];\n  const entryRe = /<entry\\b[\\s\\S]*?<\\/entry>/g;\n  let match;\n  while ((match = entryRe.exec(xml)) !== null) {\n    const block = match[0];\n    const title = (block.match(/<title[^>]*>([\\s\\S]*?)<\\/title>/) || [])[1] || '';\n    const link = (block.match(/<link[^>]*href=\"([^\"]+)\"/) || [])[1] || '';\n    const content = (block.match(/<content[^>]*>([\\s\\S]*?)<\\/content>/) || [])[1] || '';\n    const updated = (block.match(/<updated>([^<]+)<\\/updated>/) || [])[1] || '';\n    entries.push({\n      title: unescapeEntities(title.trim()),\n      link,\n      content: unescapeEntities(content),\n      updated,\n    });\n  }\n  return entries;\n}\n\n// Negative-keyword pre-filter: skip obvious meta/discussion posts before we\n// burn AI tokens trying to extract a non-existent opportunity. Tuned from\n// failures observed in production (megathreads, weekly threads, rants).\nconst SKIP_PHRASES = [\n  'megathread',\n  'monthly thread',\n  'weekly thread',\n  'who\\'s looking',\n  'who is looking',\n  'who\\'s hiring? -',\n  'who is hiring? -',\n  'discussion thread',\n  'general discussion',\n  'rant',\n  'meme',\n  'shitpost',\n  'salary thread',\n  'compensation thread',\n  'interview experience',\n  'career advice',\n  'doubt',\n  'help needed',\n];\n\nfunction looksLikeMeta(title) {\n  const t = String(title || '').toLowerCase();\n  if (!t) return true;\n  return SKIP_PHRASES.some((p) => t.includes(p));\n}\n\nconst out = [];\nconst errors = [];\n\nfor (const sub of SUBREDDITS) {\n  try {\n    const xml = await this.helpers.httpRequest({\n      method: 'GET',\n      url: `https://www.reddit.com/r/${sub.slug}/.rss`,\n      headers: {\n        Accept: 'application/atom+xml, application/xml',\n        'User-Agent': 'opportunity-os/1.0 (RSS aggregator; https://opportunity-os-eight.vercel.app)',\n      },\n    });\n    const entries = parseAtomEntries(String(xml)).slice(0, PER_SUB_LIMIT);\n    for (const e of entries) {\n      if (!e.link) continue;\n      if (looksLikeMeta(e.title)) continue; // drop megathreads, rants, etc.\n      out.push({\n        json: {\n          title: e.title,\n          link: e.link,\n          contentSnippet: stripHtml(e.content).slice(0, 1000),\n          content: e.content,\n          subreddit: sub.displayName,\n          source_name: `Reddit: ${sub.displayName}`,\n          updated_at: e.updated,\n        }\n      });\n    }\n  } catch (e) {\n    errors.push(`${sub.slug}: ${(e && e.message) || e}`);\n  }\n}\n\nif (out.length === 0) {\n  throw new Error('Reddit aggregator returned 0 usable posts. Errors: ' + (errors.join(' | ') || 'none'));\n}\nreturn out;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        208,
        0
      ],
      "id": "a0red005-0000-4000-8000-000000000002",
      "name": "Fetch Reddit Subreddits"
    },
    {
      "parameters": {
        "maxItems": 15
      },
      "type": "n8n-nodes-base.limit",
      "typeVersion": 1,
      "position": [
        416,
        0
      ],
      "id": "a0red005-0000-4000-8000-000000000003",
      "name": "Limit"
    },
    {
      "parameters": {
        "options": {
          "reset": false
        }
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        624,
        0
      ],
      "id": "a0red005-0000-4000-8000-000000000004",
      "name": "Loop Over Items"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "k1",
              "leftValue": "={{ ($json.title + ' ' + ($json.contentSnippet || '')).toLowerCase() }}",
              "rightValue": "hiring",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            },
            {
              "id": "k2",
              "leftValue": "={{ ($json.title + ' ' + ($json.contentSnippet || '')).toLowerCase() }}",
              "rightValue": "intern",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            },
            {
              "id": "k3",
              "leftValue": "={{ ($json.title + ' ' + ($json.contentSnippet || '')).toLowerCase() }}",
              "rightValue": "fellowship",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            },
            {
              "id": "k4",
              "leftValue": "={{ ($json.title + ' ' + ($json.contentSnippet || '')).toLowerCase() }}",
              "rightValue": "scholarship",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            },
            {
              "id": "k5",
              "leftValue": "={{ ($json.title + ' ' + ($json.contentSnippet || '')).toLowerCase() }}",
              "rightValue": "hackathon",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            },
            {
              "id": "k6",
              "leftValue": "={{ ($json.title + ' ' + ($json.contentSnippet || '')).toLowerCase() }}",
              "rightValue": "openings",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            },
            {
              "id": "k7",
              "leftValue": "={{ ($json.title + ' ' + ($json.contentSnippet || '')).toLowerCase() }}",
              "rightValue": "freshers",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            },
            {
              "id": "k8",
              "leftValue": "={{ ($json.title + ' ' + ($json.contentSnippet || '')).toLowerCase() }}",
              "rightValue": "referral",
              "operator": {
                "type": "string",
                "operation": "contains"
              }
            }
          ],
          "combinator": "or"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        832,
        0
      ],
      "id": "a0red005-0000-4000-8000-000000000005",
      "name": "Filter Keywords"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://host.docker.internal:3000/api/ingest/check-exists",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Ingest-Secret",
              "value": "REPLACE_WITH_YOUR_INGEST_SHARED_SECRET"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "source_url",
              "value": "={{ $json.link }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1040,
        0
      ],
      "id": "a0red005-0000-4000-8000-000000000006",
      "name": "Check Exists"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "n1",
              "leftValue": "={{ $json.exists }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1248,
        0
      ],
      "id": "a0red005-0000-4000-8000-000000000007",
      "name": "If New"
    },
    {
      "parameters": {
        "amount": 7
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1456,
        -64
      ],
      "id": "a0red005-0000-4000-8000-000000000008",
      "name": "Wait"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://host.docker.internal:3000/api/ai/extract",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Ingest-Secret",
              "value": "REPLACE_WITH_YOUR_INGEST_SHARED_SECRET"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "text",
              "value": "={{ ($('Loop Over Items').item.json.title + '\\n\\n' + ($('Loop Over Items').item.json.contentSnippet || '')).slice(0, 4000) }}"
            },
            {
              "name": "source_url",
              "value": "={{ $('Loop Over Items').item.json.link }}"
            },
            {
              "name": "source_name",
              "value": "={{ $('Loop Over Items').item.json.source_name }}"
            },
            {
              "name": "hint",
              "value": "={{ 'Reddit post from ' + $('Loop Over Items').item.json.subreddit + ' \u2014 community-shared opportunity. May be a referral, internship, or hiring announcement; expect informal phrasing.' }}"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          },
          "timeout": 60000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1648,
        -64
      ],
      "id": "a0red005-0000-4000-8000-000000000009",
      "name": "AI Extract",
      "retryOnFail": true,
      "waitBetweenTries": 1000
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://host.docker.internal:3000/api/ingest/upsert",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Ingest-Secret",
              "value": "REPLACE_WITH_YOUR_INGEST_SHARED_SECRET"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "opportunity",
              "value": "={{ $json.opportunity }}"
            },
            {
              "name": "source_url",
              "value": "={{ $('Loop Over Items').item.json.link }}"
            },
            {
              "name": "source_name",
              "value": "={{ $('Loop Over Items').item.json.source_name }}"
            }
          ]
        },
        "options": {
          "timeout": 15000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1888,
        -64
      ],
      "id": "a0red005-0000-4000-8000-000000000010",
      "name": "Upsert Opportunity"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://host.docker.internal:3000/api/log",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Ingest-Secret",
              "value": "REPLACE_WITH_YOUR_INGEST_SHARED_SECRET"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "status",
              "value": "skipped_filtered"
            },
            {
              "name": "source_url",
              "value": "={{ $json.link }}"
            },
            {
              "name": "source_name",
              "value": "={{ $json.source_name }}"
            },
            {
              "name": "reason",
              "value": "no opportunity-related keyword in title/content"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1136,
        272
      ],
      "id": "a0red005-0000-4000-8000-000000000011",
      "name": "Log Filtered"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://host.docker.internal:3000/api/log",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Ingest-Secret",
              "value": "REPLACE_WITH_YOUR_INGEST_SHARED_SECRET"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "status",
              "value": "skipped_duplicate"
            },
            {
              "name": "source_url",
              "value": "={{ $('Loop Over Items').item.json.link }}"
            },
            {
              "name": "source_name",
              "value": "={{ $('Loop Over Items').item.json.source_name }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1520,
        272
      ],
      "id": "a0red005-0000-4000-8000-000000000012",
      "name": "Log Duplicate"
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Fetch Reddit Subreddits",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Reddit Subreddits": {
      "main": [
        [
          {
            "node": "Limit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Limit": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Filter Keywords",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Keywords": {
      "main": [
        [
          {
            "node": "Check Exists",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Filtered",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Exists": {
      "main": [
        [
          {
            "node": "If New",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If New": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Duplicate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "AI Extract",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Extract": {
      "main": [
        [
          {
            "node": "Upsert Opportunity",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upsert Opportunity": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Filtered": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Duplicate": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "tags": []
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Reddit — India + student career subs (RSS aggregator). Uses httpRequest. Scheduled trigger; 12 nodes.

Source: https://github.com/krishnagahlod/opportunity-os/blob/399dbb38874ec13c22922b64013cbc57a2845dc0/n8n-workflows/10-rss-reddit-india.json — original creator credit. Request a take-down →

More Social Media workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Social Media

This template is for advanced users, content teams, and data analysts who need a robust, automated system for capturing YouTube transcripts. It’s ideal for those who monitor multiple channels and want

Supabase, Stop And Error, RSS Feed Read +1
Social Media

You are in the bad habit of always checking your feed to see if there are new videos? This workflow will help you get rid of this habit by delivering an email notification for each new video posted fr

YouTube, RSS Feed Read, HTTP Request +2
Social Media

This workflow demonstrates how to combine trend harvesting, channel intelligence, and AI scoring to select the best daily content ideas for short-form videos (YouTube Shorts / TikTok).

YouTube, OpenRouter Chat, RSS Feed Read +4
Social Media

This workflow contains community nodes that are only compatible with the self-hosted version of n8n.

HTTP Request, Google Sheets, RSS Feed Read +3
Social Media

Multi Platform Content Generator from YouTube using AI & RSS. Uses httpRequest, googleSheets, rssFeedRead, lmChatOpenRouter. Scheduled trigger; 37 nodes.

HTTP Request, Google Sheets, RSS Feed Read +3