AutomationFlowsSocial Media › Create X and Linkedin Posts From Reddit Threads with Gemini and Human Review

Create X and Linkedin Posts From Reddit Threads with Gemini and Human Review

ByAppStoneLab Technologies LLP @appstonelab on n8n.io

Turn any Reddit thread into polished, platform-optimized social media posts for X (Twitter) and LinkedIn - in minutes, not hours. This workflow reads a Reddit thread, extracts the full discussion (including nested comment threads sorted by score), feeds everything to Google…

Event trigger★★★★☆ complexityAI-powered27 nodesForm TriggerHTTP RequestGoogle GeminiLinkedInTwitter
Social Media Trigger: Event Nodes: 27 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #14007 — we link there as the canonical source.

This workflow follows the Form Trigger → Googlegemini 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 →

Download .json
{
  "id": "a6nwY85e9GX2Y24b",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Reddit Thread \u2192 AI Summary \u2192 Social Media Publisher (X + LinkedIn)",
  "tags": [],
  "nodes": [
    {
      "id": "49e47b33-6ce0-4868-b2ac-46d58de66710",
      "name": "Reddit URL Input",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -336,
        32
      ],
      "parameters": {
        "options": {},
        "formTitle": "Reddit Thread Post Generator",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Reddit Thread URL"
            }
          ]
        },
        "formDescription": "generates social media posts based on reddit thread"
      },
      "typeVersion": 2.5
    },
    {
      "id": "685f7307-665f-4fd5-aeb6-b9f107b73279",
      "name": "Parse Reddit URL",
      "type": "n8n-nodes-base.code",
      "position": [
        -48,
        32
      ],
      "parameters": {
        "jsCode": "// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Parse Reddit Thread URL \u2192 extract subreddit + post ID\n// Supports: reddit.com/r/sub/comments/id, redd.it/id, mobile URLs\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst rawUrl = $input.first().json['Reddit Thread URL'].trim();\n\n// Normalize: remove trailing slash, www, m. prefix\nconst url = rawUrl.replace(/^(https?:\\/\\/)?(www\\.|m\\.)?/, 'https://');\n\n// Match standard thread URL\nconst fullMatch = url.match(/reddit\\.com\\/r\\/([^/]+)\\/comments\\/([a-zA-Z0-9]+)/);\n\nif (!fullMatch) {\n  throw new Error(\n    '\u274c Invalid Reddit URL. Please use a standard thread URL like: ' +\n    'https://www.reddit.com/r/subreddit/comments/postId/title/'\n  );\n}\n\nconst subreddit = fullMatch[1];\nconst postId = fullMatch[2];\nconst apiUrl = `https://www.reddit.com/r/${subreddit}/comments/${postId}.json`;\nconst cleanThreadUrl = `https://www.reddit.com/r/${subreddit}/comments/${postId}/`;\n\nreturn [{\n  json: {\n    originalUrl: rawUrl,\n    subreddit,\n    postId,\n    apiUrl,\n    cleanThreadUrl\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "e8af925a-9bc6-4538-83dc-6e15cbe98386",
      "name": "Fetch Reddit Thread",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        256,
        32
      ],
      "parameters": {
        "url": "={{ $json.apiUrl }}",
        "options": {},
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "limit",
              "value": "100"
            },
            {
              "name": "depth",
              "value": "3"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "User-Agent",
              "value": "n8n-workflow-automation/1.0"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "27751aa7-a183-4791-890a-b0f55352cf87",
      "name": "Extract Thread Content",
      "type": "n8n-nodes-base.code",
      "position": [
        544,
        32
      ],
      "parameters": {
        "jsCode": "// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Extract post + ALL comments from Reddit JSON API response\n// \u2022 Top-level comments sorted by score (highest first)\n// \u2022 Each comment's replies also sorted by score (highest first)\n// \u2022 Full depth traversal \u2014 no depth limit\n// \u2022 Structured output: nested objects + flat string for AI\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst response = $input.first().json;\n\n// \u2500\u2500 Handle both: array-as-single-item OR multiple items from HTTP node \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet postListing, commentsListing;\n\nif (Array.isArray(response)) {\n  postListing     = response[0];\n  commentsListing = response[1];\n} else {\n  const allItems = $input.all();\n  if (allItems.length >= 2) {\n    postListing     = allItems[0].json;\n    commentsListing = allItems[1].json;\n  } else {\n    throw new Error('Unexpected Reddit API response. Could not parse thread data.');\n  }\n}\n\n// \u2500\u2500 Extract post metadata \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst pd = postListing.data.children[0].data;\n\nconst post = {\n  title:       pd.title        || '',\n  body:        pd.selftext     ? pd.selftext.trim() : '',\n  author:      pd.author       || '[deleted]',\n  score:       pd.score        || 0,\n  upvoteRatio: pd.upvote_ratio || null,\n  subreddit:   pd.subreddit    || '',\n  numComments: pd.num_comments || 0,\n  threadUrl:   `https://www.reddit.com${pd.permalink}`,\n  isLinkPost:  pd.is_self === false,\n  externalUrl: pd.is_self === false ? pd.url : null,\n  createdUtc:  pd.created_utc  || null,\n  awards:      pd.total_awards_received || 0,\n  flair:       pd.link_flair_text || null\n};\n\n// \u2500\u2500 Recursively extract comments \u2014 no depth limit, all replies \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Strategy:\n//   1. At each level, sort siblings by score DESC\n//   2. Recurse into every reply thread fully\n//   3. Skip \"more\" stubs (kind: 'more') \u2014 these are collapsed Reddit nodes\n//      that would need additional API calls to expand\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction extractComments(children, depth) {\n  if (!children || children.length === 0) return [];\n\n  // Filter to real comments only (kind 't1'), skip 'more' load stubs\n  const realComments = children.filter(c => c.kind === 't1');\n\n  // Sort by score descending at this level\n  realComments.sort((a, b) => (b.data.score || 0) - (a.data.score || 0));\n\n  const result = [];\n\n  for (const child of realComments) {\n    const c = child.data;\n\n    const body = (c.body || '').replace(/\\n+/g, ' ').trim();\n    if (!body || body === '[deleted]' || body === '[removed]') continue;\n\n    // Build structured comment object\n    const comment = {\n      id:         c.id     || '',\n      author:     c.author || '[deleted]',\n      score:      c.score  || 0,\n      body:       body,\n      depth:      depth,\n      awards:     c.total_awards_received || 0,\n      createdUtc: c.created_utc || null,\n      isEdited:   !!c.edited,\n      replies:    []\n    };\n\n    // Recurse into replies with no depth cap\n    if (\n      c.replies &&\n      c.replies !== '' &&\n      c.replies.data &&\n      c.replies.data.children &&\n      c.replies.data.children.length > 0\n    ) {\n      comment.replies = extractComments(c.replies.data.children, depth + 1);\n    }\n\n    result.push(comment);\n  }\n\n  return result;\n}\n\nconst structuredComments = extractComments(commentsListing.data.children, 0);\n\n// \u2500\u2500 Count totals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction countAll(comments) {\n  let total = 0;\n  for (const c of comments) {\n    total += 1 + countAll(c.replies);\n  }\n  return total;\n}\n\nconst totalExtracted = countAll(structuredComments);\n\n// \u2500\u2500 Build flat string for AI consumption \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Format:\n//   [SCORE] u/author: comment text\n//       \u21b3 [SCORE] u/author: reply text          (depth 1)\n//           \u21b3 [SCORE] u/author: sub-reply text  (depth 2)\n//\n// Indentation visually represents thread nesting depth.\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction buildFlatString(comments, lines = []) {\n  for (const c of comments) {\n    const pad    = '    '.repeat(c.depth);\n    const arrow  = c.depth > 0 ? '\u21b3 ' : '';\n    const awards = c.awards > 0 ? ` \ud83c\udfc6\u00d7${c.awards}` : '';\n    lines.push(`${pad}${arrow}[${c.score} pts${awards}] u/${c.author}: ${c.body}`);\n    if (c.replies.length > 0) {\n      buildFlatString(c.replies, lines);\n    }\n  }\n  return lines;\n}\n\nconst commentLines = buildFlatString(structuredComments);\n\n// \u2500\u2500 Assemble threadContent string for downstream AI nodes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet threadContent = `=== REDDIT THREAD ===\\n`;\nthreadContent += `SUBREDDIT   : r/${post.subreddit}\\n`;\nthreadContent += `TITLE       : ${post.title}\\n`;\nthreadContent += `AUTHOR      : u/${post.author}\\n`;\nthreadContent += `SCORE       : ${post.score.toLocaleString()} upvotes`;\nthreadContent += post.upvoteRatio ? ` (${Math.round(post.upvoteRatio * 100)}% upvoted)\\n` : '\\n';\nthreadContent += `COMMENTS    : ${post.numComments.toLocaleString()} total, ${totalExtracted} extracted\\n`;\nif (post.flair)   threadContent += `FLAIR       : ${post.flair}\\n`;\nif (post.awards)  threadContent += `AWARDS      : ${post.awards}\\n`;\nthreadContent += `URL         : ${post.threadUrl}\\n\\n`;\n\nif (post.body) {\n  threadContent += `POST BODY:\\n${post.body}\\n\\n`;\n} else if (post.isLinkPost) {\n  threadContent += `LINK POST \u2192 ${post.externalUrl}\\n\\n`;\n}\n\nthreadContent += `COMMENTS (sorted by score, highest first \u2014 full depth):\\n`;\nthreadContent += commentLines.join('\\n');\n\nreturn [{\n  json: {\n    // \u2500\u2500 Post metadata\n    ...post,\n\n    // \u2500\u2500 Structured nested comments tree (for any downstream node that needs JSON)\n    structuredComments,\n\n    // \u2500\u2500 Stats\n    totalExtracted,\n    maxDepthFound: structuredComments.length > 0\n      ? Math.max(...commentLines.map((_, i) => {\n          const match = commentLines[i].match(/^(    )*/);\n          return match ? match[0].length / 4 : 0;\n        }))\n      : 0,\n\n    // \u2500\u2500 Flat string ready for AI prompt injection\n    threadContent\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "45a3cf91-1a16-4ff9-a17d-017058aa3de5",
      "name": "Summarize Thread",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        816,
        32
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-3.1-flash-lite-preview",
          "cachedResultName": "models/gemini-3.1-flash-lite-preview"
        },
        "options": {
          "topK": 20,
          "temperature": 0.7,
          "maxToolsIterations": 5
        },
        "messages": {
          "values": [
            {
              "content": "=Analyze the following Reddit thread and generate a comprehensive, structured summary.\n\n{{ $json.threadContent }}\n\n---\n\nPlease return a detailed markdown summary with the following sections:\n\n## \ud83e\uddf5 Thread Overview\nWhat is this thread about? (2\u20133 sentences, include the subreddit context)\n\n## \ud83d\udd11 Key Topics Discussed\nBullet-point list of the main themes and topics in this thread\n\n## \ud83d\udca1 Notable Insights & Perspectives\nThe most interesting, contrarian, or valuable perspectives from commenters. Quote or paraphrase 3\u20135 standout comments.\n\n## \ud83d\udcca Community Sentiment\nOverall sentiment (positive / negative / mixed / neutral) and why. What does the community agree or disagree on?\n\n## \ud83c\udfaf Actionable Takeaways\nList 3\u20135 concrete insights or lessons someone could apply from this discussion.\n\nBe specific and data-driven. Reference actual details from the thread \u2014 usernames, scores, and specific quotes where relevant."
            }
          ]
        },
        "simplify": false,
        "builtInTools": {
          "urlContext": true
        }
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "d2088a1a-1047-4936-880e-dd5dae44286c",
      "name": "Generate Social Posts",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        1200,
        32
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-3.1-flash-lite-preview",
          "cachedResultName": "models/gemini-3.1-flash-lite-preview"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=You are an expert social media content strategist. Based on the Reddit thread summary below, create high-performing, platform-optimized posts for X (Twitter) and LinkedIn.\n\n--- THREAD SUMMARY ---\n{{ $json.candidates[0].content.parts[0].text }}\n--- END SUMMARY ---\n\nCreate two posts:\n\n1. **X (Twitter) Post** \u2014 Max 280 characters. Should be punchy, curiosity-driven, and conversational. Include 2\u20133 relevant hashtags. May include a question or provocative statement to drive engagement. MUST be under 280 characters.\n\n2. **LinkedIn Post** \u2014 Professional tone, 150\u2013300 words. Lead with a strong insight or surprising finding from the thread. Use short paragraphs for readability. Include a clear lesson or takeaway for professionals. End with an engagement question. Include 3\u20135 relevant hashtags on the last line.\n\n\u26a0\ufe0f IMPORTANT: Return ONLY a raw JSON object. No markdown fences, no explanation, no preamble. Exactly this format:\n{\n  \"x_post\": \"your tweet text here\",\n  \"linkedin_post\": \"your linkedin post text here\"\n}"
            }
          ]
        },
        "builtInTools": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "ce5e6e7c-41e3-405b-b771-28039757835e",
      "name": "Parse Social Posts",
      "type": "n8n-nodes-base.code",
      "position": [
        1600,
        32
      ],
      "parameters": {
        "jsCode": "// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Parse Social Posts \u2014 handles Gemini's raw API response structure\n//\n// Gemini returns: array of candidates \u2192 content.parts[0].text\n// Shape: [ { content: { parts: [ { text: '...' } ], role: 'model' }, finishReason, index } ]\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst inputJson = $input.first().json;\n\n// \u2500\u2500 Extract raw text from wherever it lives \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet rawText = '';\n\n// Case 1: Gemini raw API response \u2014 array of candidates\nif (Array.isArray(inputJson) && inputJson[0]?.content?.parts?.[0]?.text) {\n  rawText = inputJson[0].content.parts[0].text;\n\n// Case 2: Single candidate object (not wrapped in array)\n} else if (inputJson?.content?.parts?.[0]?.text) {\n  rawText = inputJson.content.parts[0].text;\n\n// Case 3: LangChain chainLlm wrapper output \u2192 { text: '...' }\n} else if (typeof inputJson?.text === 'string') {\n  rawText = inputJson.text;\n\n// Case 4: Nested under candidates key\n} else if (inputJson?.candidates?.[0]?.content?.parts?.[0]?.text) {\n  rawText = inputJson.candidates[0].content.parts[0].text;\n\n} else {\n  throw new Error(\n    'Could not locate text in Gemini response. Received keys: ' +\n    JSON.stringify(Object.keys(inputJson))\n  );\n}\n\nrawText = rawText.trim();\n\n// \u2500\u2500 Strip markdown code fences if Gemini wrapped the JSON \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst cleaned = rawText\n  .replace(/^```json\\s*/i, '')\n  .replace(/^```\\s*/i, '')\n  .replace(/\\s*```$/i, '')\n  .trim();\n\n// \u2500\u2500 Parse JSON \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet parsed;\n\ntry {\n  parsed = JSON.parse(cleaned);\n} catch (e) {\n  // Fallback: regex extraction if JSON is slightly malformed\n  const xMatch  = cleaned.match(/\"x_post\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/s);\n  const liMatch = cleaned.match(/\"linkedin_post\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/s);\n\n  if (!xMatch || !liMatch) {\n    throw new Error(\n      'AI did not return valid JSON for social posts. Extracted text: ' +\n      rawText.substring(0, 400)\n    );\n  }\n\n  parsed = {\n    x_post:        xMatch[1].replace(/\\\\n/g, '\\n').replace(/\\\\\"/g, '\"'),\n    linkedin_post: liMatch[1].replace(/\\\\n/g, '\\n').replace(/\\\\\"/g, '\"')\n  };\n}\n\nif (!parsed.x_post || !parsed.linkedin_post) {\n  throw new Error(\n    'Parsed JSON is missing x_post or linkedin_post fields. Got: ' +\n    JSON.stringify(Object.keys(parsed))\n  );\n}\n\n// \u2500\u2500 Enforce X's 280-character limit with graceful truncation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet xPost = parsed.x_post.trim();\nif (xPost.length > 280) {\n  xPost = xPost.substring(0, 277) + '...';\n}\n\nconst linkedinPost = parsed.linkedin_post.trim();\n\nreturn [{\n  json: {\n    x_post:        xPost,\n    linkedin_post: linkedinPost,\n    x_char_count:  xPost.length\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "91133954-3028-45b8-a5a6-e07aab3db8ae",
      "name": "Resolve Final Posts",
      "type": "n8n-nodes-base.code",
      "position": [
        2192,
        32
      ],
      "parameters": {
        "jsCode": "// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Resolve final post content\n//\n// After a Wait node resumes, $input only contains the FORM submission data.\n// The AI-generated posts from the earlier node are no longer in $input \u2014\n// we must reference that node explicitly by name.\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// \u2500\u2500 Form submission data (what the user submitted) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst formData = $input.first().json;\n\n// \u2500\u2500 AI-generated posts from the upstream Parse node \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Reference the exact name of your Parse Social Posts code node\nconst aiData = $('Parse Social Posts').item.json;\n\nconst aiXPost        = (aiData.x_post        || '').trim();\nconst aiLinkedinPost = (aiData.linkedin_post  || '').trim();\n\n// \u2500\u2500 Override values typed by the user (empty string if left blank) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst overrideX  = (formData['X Post Override (leave blank to use AI version)']        || '').trim();\nconst overrideLi = (formData['LinkedIn Post Override (leave blank to use AI version)'] || '').trim();\n\n// \u2500\u2500 Final resolved posts: override wins, falls back to AI version \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst finalXPost        = overrideX  || aiXPost;\nconst finalLinkedinPost = overrideLi || aiLinkedinPost;\n\nif (!finalXPost) {\n  throw new Error('No X post content \u2014 AI version was empty and no override was provided.');\n}\nif (!finalLinkedinPost) {\n  throw new Error('No LinkedIn post content \u2014 AI version was empty and no override was provided.');\n}\n\nreturn [{\n  json: {\n    'X Post':        finalXPost,\n    'LinkedIn Post': finalLinkedinPost,\n    usedOverrideX:   !!overrideX,\n    usedOverrideLi:  !!overrideLi\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "a536ab57-8cef-4125-8cef-8b0143fbb9ea",
      "name": "Approved?",
      "type": "n8n-nodes-base.if",
      "position": [
        2480,
        32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-approval-check",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('Human Approval').item.json['Decision'] }}",
              "rightValue": "Approve & Publish"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "bdf05261-fac9-476c-89cf-336396b2aaad",
      "name": "Post to Linkedin",
      "type": "n8n-nodes-base.linkedIn",
      "position": [
        2672,
        16
      ],
      "parameters": {
        "text": "={{ $json['LinkedIn Post'] }}",
        "person": "=ID",
        "additionalFields": {}
      },
      "credentials": {
        "linkedInOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "eb847f8f-8c5d-4a8b-b12a-77abdc5f4f43",
      "name": "Rejected \u2013 End",
      "type": "n8n-nodes-base.set",
      "position": [
        2672,
        208
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "set-rejected-status",
              "name": "status",
              "type": "string",
              "value": "rejected"
            },
            {
              "id": "set-rejected-msg",
              "name": "message",
              "type": "string",
              "value": "Posts were reviewed and rejected by the user. No content was published."
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "9f9afd49-2441-469b-b6de-2c6dce5f12ff",
      "name": "Post to X",
      "type": "n8n-nodes-base.twitter",
      "position": [
        2672,
        -176
      ],
      "parameters": {
        "text": "={{ $json['X Post'] }}",
        "additionalFields": {}
      },
      "credentials": {
        "twitterOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "d1a31113-4edd-46e0-8461-22df915e712a",
      "name": "\ud83d\uddd2\ufe0f Workflow Summary",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1040,
        -256
      ],
      "parameters": {
        "color": 4,
        "width": 604,
        "height": 532,
        "content": "## \ud83d\udd01 Reddit \u2192 Social Media Automation\n\nConverts any Reddit thread into ready-to-publish posts for **X (Twitter)** and **LinkedIn** \u2014 with a human review gate before anything goes live.\n\n\n---\n\n\n**\ud83d\udccc Data Flow:**\nReddit URL \u2192 Parse \u2192 Fetch API \u2192 Extract Content \u2192 AI Summarize \u2192 Generate Posts \u2192 Human Approval \u2192 Publish\n\n\n---\n\n\n**\u2728 Key Features:**\n- Accepts any Reddit thread URL\n- Extracts post + all nested comments (sorted by score)\n- AI-powered thread summarization via Gemini\n- Platform-optimized X + LinkedIn post generation\n- Human-in-the-loop approval with override support\n- Graceful rejection path \u2014 nothing published without approval\n\n---\n\n**\ud83d\udee0\ufe0f Tools Used:**\nGoogle Gemini 3.1 Flash Lite \u00b7 Reddit JSON API \u00b7 X (Twitter) API \u00b7 LinkedIn API"
      },
      "typeVersion": 1
    },
    {
      "id": "ce07e2c1-3244-40fb-8313-5cd50760b059",
      "name": "\ud83c\udf10 Node: Fetch Reddit Thread",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        160,
        -256
      ],
      "parameters": {
        "color": 2,
        "width": 284,
        "height": 528,
        "content": "## \ud83c\udf10 Fetch Reddit Thread\n**Type:** HTTP Request\n\n**Purpose:** Retrieves the full thread via Reddit's public JSON API.\n\n**Input:** `apiUrl` (e.g. `reddit.com/r/sub/comments/id.json`)\n\n**Function:** GET with `limit=100`, `depth=3`.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**Output:** Raw Reddit API response \u2014 post listing + comments listing array."
      },
      "typeVersion": 1
    },
    {
      "id": "fb8c1a4a-84ca-4fc6-9201-7c4e97cb547d",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -416,
        -256
      ],
      "parameters": {
        "color": 3,
        "width": 256,
        "height": 528,
        "content": "## \ud83d\udce5 Reddit URL Input\n**Type:** Form Trigger\n\n**Purpose:** Entry point \u2014 presents a web form to collect a Reddit thread URL from the user.\n\n**Input:** User submits a Reddit thread URL via the form.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**Output:** Raw URL string forwarded to the parse step."
      },
      "typeVersion": 1
    },
    {
      "id": "8c65fe79-b1a0-4541-a1e8-ee780dcbe283",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        448,
        -256
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 528,
        "content": "## \u2699\ufe0f Extract Content\n**Type:** Code (JavaScript)\n\n**Purpose:** Parses raw Reddit JSON into structured, AI-ready content.\n\n**Input:** Raw Reddit API response\n\n**Function:** Extracts post metadata; recursively traverses all comments sorted by score at every depth; builds a flat indented string for AI.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**Output:** `threadContent` (AI prompt string), `structuredComments` (nested JSON), post metadata"
      },
      "typeVersion": 1
    },
    {
      "id": "55e37b0d-7ae1-435e-9dfa-2ef05f1d5af1",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        752,
        -256
      ],
      "parameters": {
        "color": 5,
        "width": 352,
        "height": 528,
        "content": "## \ud83e\udde0 Summarize Thread\n**Type:** Google Gemini AI\n\n**Purpose:** Produces a comprehensive markdown summary of the Reddit thread.\n\n**Input:** `threadContent` from Extract node\n\n**Function:** Gemini 3.1 Flash Lite \u2192 returns Thread Overview, Key Topics, Notable Insights, Community Sentiment, and Actionable Takeaways.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**Output:** Structured markdown summary text"
      },
      "typeVersion": 1
    },
    {
      "id": "3bce0319-33eb-4efb-b421-efe355e3ccdd",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1120,
        -256
      ],
      "parameters": {
        "color": 5,
        "width": 368,
        "height": 528,
        "content": "## \u270d\ufe0f Generate Social Posts\n**Type:** Google Gemini AI\n\n**Purpose:** Creates platform-optimized posts for X and LinkedIn.\n\n**Input:** Thread summary from Summarize node\n\n**Function:** Gemini generates: X post (\u2264280 chars, punchy + hashtags) and LinkedIn post (150\u2013300 words, professional + hashtags). Returns strict JSON only.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**Output:** `{ \"x_post\": \"...\", \"linkedin_post\": \"...\" }`"
      },
      "typeVersion": 1
    },
    {
      "id": "b3beeef6-1f35-4f8c-bd03-4c13b5cabf8c",
      "name": "Human Approval",
      "type": "n8n-nodes-base.wait",
      "position": [
        1904,
        32
      ],
      "parameters": {
        "resume": "form",
        "options": {},
        "formTitle": "\ud83d\udccb Review & Approve Social Media Posts",
        "formFields": {
          "values": [
            {
              "fieldType": "textarea",
              "fieldLabel": "X Post Override (leave blank to use AI version)"
            },
            {
              "fieldType": "textarea",
              "fieldLabel": "LinkedIn Post Override (leave blank to use AI version)"
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Decision",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Approve & Publish"
                  },
                  {
                    "option": "Reject"
                  }
                ]
              },
              "requiredField": true
            }
          ]
        },
        "formDescription": "=## \ud83d\udc26 X (TWITTER) POST  \u00b7  {{ $json.x_char_count }}/280 chars\n\n{{ $json.x_post }}\n\n---\n\n## \ud83d\udcbc LINKEDIN POST\n\n{{ $json.linkedin_post }}\n\n---\n\nThe posts above are ready to publish. You can paste an edited version in the override fields below \u2014 **leave a field blank to publish the AI version as-is**."
      },
      "typeVersion": 1.1
    },
    {
      "id": "067b6314-8cd7-4b22-aeea-9ac90332c687",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1504,
        -256
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 528,
        "content": "## \ud83e\uddf9 Parse Social Posts\n**Type:** Code (JavaScript)\n\n**Purpose:** Safely extracts and validates Gemini's JSON response.\n\n**Input:** Raw Gemini API response\n\n**Function:** Strips markdown fences, parses JSON, falls back to regex if malformed. Enforces 280-char hard limit on X post.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**Output:** `x_post`, `linkedin_post`, `x_char_count`"
      },
      "typeVersion": 1
    },
    {
      "id": "4fe0405c-4b1d-4fde-9a35-4fd611d07095",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1808,
        -256
      ],
      "parameters": {
        "color": 3,
        "width": 272,
        "height": 528,
        "content": "## \ud83d\udc64 Human Approval\n**Type:** Wait (Form)\n\n**Purpose:** Pauses the workflow so a human can review posts.\n\n**Input:** `x_post`, `linkedin_post`, `x_char_count`\n\n**Function:** Displays posts in a form. User can overrides or use AI version. Must select **Approve & Publish** or **Reject**.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**Output:** Form submission with `Decision` + optional override fields."
      },
      "typeVersion": 1
    },
    {
      "id": "d36354a5-bd50-4ed3-90bd-55e0a767d9d2",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2096,
        -256
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 528,
        "content": "## \u2705 Resolve Final Posts\n**Type:** Code (JavaScript)\n\n**Purpose:** Merges human overrides with AI-generated posts.\n\n**Input:** Form data + AI posts (referenced from Parse Social Posts node)\n\n**Function:** Override wins if non-empty; otherwise falls back to the AI version. Throws if both are empty.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**Output:** `X Post`, `LinkedIn Post` (final content ready to publish)"
      },
      "typeVersion": 1
    },
    {
      "id": "b3874ed4-93f9-4dee-af61-67b014f87a3d",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2400,
        -256
      ],
      "parameters": {
        "color": 2,
        "width": 256,
        "height": 528,
        "content": "## \ud83d\udd00 Approved?\n**Type:** IF Condition\n\n**Purpose:** Routes the workflow based on the human's decision.\n\n**Input:** `Decision` field from Human Approval form\n\n**True \u2192** Post to X + Post to LinkedIn\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**False \u2192** Rejected \u2013 End (nothing published)"
      },
      "typeVersion": 1
    },
    {
      "id": "caf3cccc-4cf9-48af-ab39-b97f61fe9ab3",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2784,
        -256
      ],
      "parameters": {
        "color": 6,
        "width": 384,
        "height": 208,
        "content": "## \ud83d\udc26 Post to X\n**Type:** X (Twitter)\n\n**Purpose:** Publishes the final post to X (Twitter).\n\n\n**Input:** `X Post` (\u2264280 chars) from Resolve Final Posts\n\n**Output:** Live tweet on the connected X account."
      },
      "typeVersion": 1
    },
    {
      "id": "551598a4-3eb2-44a7-b14c-5ac1ab8e097e",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2784,
        -32
      ],
      "parameters": {
        "color": 6,
        "width": 384,
        "height": 192,
        "content": "## \ud83d\udcbc Post to LinkedIn\n**Type:** LinkedIn\n\n**Purpose:** Publishes the final post to LinkedIn.\n\n**Input:** `LinkedIn Post` from Resolve Final Posts\n\n**Output:** Live post on the connected LinkedIn account."
      },
      "typeVersion": 1
    },
    {
      "id": "19a0bf37-09c1-4a61-b1c8-b6bd41e6d166",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2784,
        176
      ],
      "parameters": {
        "color": 6,
        "width": 384,
        "height": 176,
        "content": "## \ud83d\udeab Rejected \u2013 End\n**Type:** Set\n\n**Purpose:** Graceful termination \u2014 nothing is published.\n\n**Input:** Rejection path from Approved? node\n\n**Function:** Sets `status: 'rejected'` and a descriptive message.\n\n**Output:** Terminal state \u2014 workflow ends here."
      },
      "typeVersion": 1
    },
    {
      "id": "87f9573d-43dd-4bee-b744-efd26711ffd5",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -144,
        -256
      ],
      "parameters": {
        "color": 7,
        "width": 288,
        "height": 528,
        "content": "## \ud83d\udd0d Parse Reddit URL\n**Type:** Code (JavaScript)\n\n**Purpose:** Validates and deconstructs the submitted Reddit URL.\n\n**Input:** Raw URL string\n\n**Function:** Regex extraction of subreddit + post ID. Handles standard, mobile (`m.`) and short `redd.it` links.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n**Output:** `subreddit`, `postId`, `apiUrl`, `cleanThreadUrl`"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "6173be76-a99e-4a57-800c-510df745cce5",
  "connections": {
    "Approved?": {
      "main": [
        [
          {
            "node": "Post to X",
            "type": "main",
            "index": 0
          },
          {
            "node": "Post to Linkedin",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Rejected \u2013 End",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Human Approval": {
      "main": [
        [
          {
            "node": "Resolve Final Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Reddit URL": {
      "main": [
        [
          {
            "node": "Fetch Reddit Thread",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reddit URL Input": {
      "main": [
        [
          {
            "node": "Parse Reddit URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Summarize Thread": {
      "main": [
        [
          {
            "node": "Generate Social Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Social Posts": {
      "main": [
        [
          {
            "node": "Human Approval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Reddit Thread": {
      "main": [
        [
          {
            "node": "Extract Thread Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Resolve Final Posts": {
      "main": [
        [
          {
            "node": "Approved?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Social Posts": {
      "main": [
        [
          {
            "node": "Parse Social Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Thread Content": {
      "main": [
        [
          {
            "node": "Summarize Thread",
            "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.

Pro

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

About this workflow

Turn any Reddit thread into polished, platform-optimized social media posts for X (Twitter) and LinkedIn - in minutes, not hours. This workflow reads a Reddit thread, extracts the full discussion (including nested comment threads sorted by score), feeds everything to Google…

Source: https://n8n.io/workflows/14007/ — 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 workflow is an end-to-end, enterprise-grade content engine. It automates the entire lifecycle of a brand's content marketing: capturing business context via an n8n Form Trigger to generate a mast

Form Trigger, Google Drive, HTTP Request +6
Social Media

✨🤖Automated AI Powered Social Media Content Factory for X + Facebook + Instagram + LinkedIn. Uses outputParserStructured, lmChatGoogleGemini, lmChatOpenAi, httpRequest. Event-driven trigger; 57 nodes

Output Parser Structured, Google Gemini Chat, OpenAI Chat +11
Social Media

Social Media Managers and Digital Marketers seeking to streamline content production across 7+ platforms (X/Twitter, Instagram, LinkedIn, Facebook, TikTok, Threads, YouTube Shorts) using AI-powered au

Output Parser Structured, Google Gemini Chat, OpenAI Chat +11
Social Media

This workflow turns a single YouTube video into multiple content formats and publishes them across different platforms with an optional human approval step.

HTTP Request, Stop And Error, OpenAI +7
Social Media

This AI-driven n8n workflow automates social media content creation and publishing across LinkedIn, Instagram, Facebook, and Twitter (X). It generates engaging, platform-optimized posts using Google G

Form Trigger, Agent, Google Gemini Chat +6