This workflow corresponds to n8n.io template #17800 — we link there as the canonical source.
This workflow follows the Chainllm → 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": "ZQClVhHHcR5vgeb0",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Auto-post blog articles as Twitter threads and LinkedIn posts",
"tags": [],
"nodes": [
{
"id": "3cfbc38a-8edc-4a18-986a-2846bc894030",
"name": "Template Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
1632,
2128
],
"parameters": {
"color": 4,
"width": 700,
"height": 840,
"content": "## \ud83e\uddf5 Auto-Post Blog Articles as Twitter Threads and LinkedIn Posts\n\n## Who's it for\nBloggers, founders, and content creators who publish via RSS (WordPress, Ghost, Substack, etc.) and want every new post automatically turned into a Twitter/X thread and a LinkedIn update, without manually rewriting the article into shorter chunks each time.\n\n## How it works\nThe workflow checks your RSS feed on a schedule and compares it against a log of already-posted articles, so only genuinely new posts get processed. For each new article, it fetches the full page and extracts clean text, then AI breaks it into a numbered Twitter thread and a separate, longer-form LinkedIn post. Every tweet is checked against the character limit before anything gets posted, if any tweet is too long, the whole thread is held back and logged for manual review instead of posting something broken. Valid threads are posted tweet by tweet, each one replying to the last so they chain into a real thread, then a LinkedIn post goes out separately. Every processed article is logged either way, so it's never picked up twice.\n\n## How to set up\n1. Connect your Google Sheets credentials and create the two sheets described in the sticky notes (Processed log and NeedsReview log).\n2. Connect your Groq or Gemini API credentials to the Chat Model node.\n3. Connect your Twitter/X credentials to both tweet-posting nodes.\n4. Connect your LinkedIn credentials to the LinkedIn node.\n5. Open the Set Configuration node and fill in your RSS feed URL, Sheet ID, max tweets per thread, and hashtags.\n6. Set your preferred check frequency in the Schedule Trigger.\n7. Activate the workflow.\n\n## Requirements\n- An RSS feed\n- Groq or Gemini API key\n- Twitter/X API access (posting permissions)\n- LinkedIn API access\n- Google Sheets account\n\n## How to customize\nEdit the thread-generation prompt to change tone or how aggressively the article gets condensed. Adjust max_tweets_in_thread in Set Configuration to cap how long a thread can get."
},
"typeVersion": 1
},
{
"id": "8a4f8da1-1c0f-4c72-a3bb-584dd277f4a4",
"name": "Section - Trigger, Config, Fetch Feed",
"type": "n8n-nodes-base.stickyNote",
"position": [
2368,
2576
],
"parameters": {
"color": 7,
"width": 684,
"height": 380,
"content": "## 1\ufe0f\u20e3 2\ufe0f\u20e3 3\ufe0f\u20e3 Trigger + Config + Fetch Feed\nRuns on a schedule (not a live RSS trigger, this uses the standard RSS Feed Read node, which works across n8n versions without needing a separately-installed trigger node)."
},
"typeVersion": 1
},
{
"id": "e81715c8-8bb5-4525-a17a-f37d1a988b14",
"name": "Section - Dedup",
"type": "n8n-nodes-base.stickyNote",
"position": [
3072,
2576
],
"parameters": {
"color": 7,
"width": 492,
"height": 380,
"content": "## 4\ufe0f\u20e3 5\ufe0f\u20e3 Skip Already-Posted Articles\nFetches the log of previously-processed article links and drops anything already handled, since RSS Feed Read returns the whole feed every run, not just new items."
},
"typeVersion": 1
},
{
"id": "6531e0ce-a9df-4fe6-acde-2505619efe30",
"name": "Section - Fetch and Extract",
"type": "n8n-nodes-base.stickyNote",
"position": [
3600,
2576
],
"parameters": {
"color": 7,
"width": 500,
"height": 332,
"content": "## 6\ufe0f\u20e3 7\ufe0f\u20e3 Fetch Full Article + Extract\nRSS summaries are usually incomplete, this fetches the full page and pulls clean text out of it. \u26a0\ufe0f The CSS selector is a guess, check it matches your blog's actual article container."
},
"typeVersion": 1
},
{
"id": "04d1bd72-44dd-4776-ab8f-0eaee9c0b1f8",
"name": "Section - Generate Thread",
"type": "n8n-nodes-base.stickyNote",
"position": [
4128,
2576
],
"parameters": {
"color": 7,
"width": 420,
"height": 356,
"content": "## 8\ufe0f\u20e3 Generate Thread + LinkedIn Post\nBreaks the article into a numbered Twitter thread (each tweet under 280 characters) and a separate longer-form LinkedIn post. Edit the prompt to change tone."
},
"typeVersion": 1
},
{
"id": "c5f13c92-8837-4344-83f3-3a86161f45a5",
"name": "Section - Validate and Log",
"type": "n8n-nodes-base.stickyNote",
"position": [
4576,
2496
],
"parameters": {
"color": 7,
"width": 420,
"height": 612,
"content": "## 9\ufe0f\u20e3 Validate + Log as Processed\nChecks every tweet against the real character limit. The article is marked processed either way, valid or not, so a broken thread doesn't get regenerated and reattempted every single hour."
},
"typeVersion": 1
},
{
"id": "73a26504-4f7d-4269-bba1-86d67a807ccf",
"name": "Section - Needs Review",
"type": "n8n-nodes-base.stickyNote",
"position": [
5088,
3120
],
"parameters": {
"color": 7,
"width": 300,
"height": 352,
"content": "## \u274c If Any Tweet Too Long\nLogged separately with the actual tweets so you can fix and post manually."
},
"typeVersion": 1
},
{
"id": "c5713cfd-c29a-433f-9e9e-18a0bf6c084b",
"name": "Section - Post Thread",
"type": "n8n-nodes-base.stickyNote",
"position": [
5088,
2480
],
"parameters": {
"color": 7,
"width": 900,
"height": 448,
"content": "## \ud83d\udd01 Post the Thread, One Tweet at a Time\nLoops through the tweets one by one. The first tweet posts standalone, every tweet after replies to the previous one so they chain into a real thread, tracked using workflow static data across loop iterations. \u26a0\ufe0f The X/Twitter node's exact fields (especially the reply-to field name) can shift between n8n versions, verify against your installed version before relying on this."
},
"typeVersion": 1
},
{
"id": "971e006e-7ef6-4baa-a1b6-807e05afad30",
"name": "Section - LinkedIn",
"type": "n8n-nodes-base.stickyNote",
"position": [
5632,
2976
],
"parameters": {
"color": 7,
"width": 400,
"height": 376,
"content": "## \ud83d\udd17 After the Thread Finishes\nOnce the loop completes, one LinkedIn post goes out with the longer-form version and the article link."
},
"typeVersion": 1
},
{
"id": "311ad2a0-1dbe-4e80-9728-c7950dec2adb",
"name": "Every Hour",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
2416,
2752
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "2c7c3059-e196-4897-9f70-e179f1e4dbe7",
"name": "Set Configuration",
"type": "n8n-nodes-base.set",
"position": [
2656,
2752
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cfg-1",
"name": "rss_feed_url",
"type": "string",
"value": "REPLACE_WITH_RSS_FEED_URL"
},
{
"id": "cfg-2",
"name": "max_tweets_in_thread",
"type": "number",
"value": 6
},
{
"id": "cfg-3",
"name": "brand_hashtags",
"type": "string",
"value": "REPLACE_WITH_HASHTAGS_EG_buildinpublic"
},
{
"id": "cfg-4",
"name": "sheet_id",
"type": "string",
"value": "REPLACE_WITH_GOOGLE_SHEET_ID"
},
{
"id": "cfg-5",
"name": "processed_sheet_name",
"type": "string",
"value": "Processed"
},
{
"id": "cfg-6",
"name": "review_sheet_name",
"type": "string",
"value": "NeedsReview"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "6537ff6b-03eb-47f1-bd28-d61d0e3a48ee",
"name": "Fetch RSS Feed",
"type": "n8n-nodes-base.rssFeedRead",
"position": [
2896,
2752
],
"parameters": {
"url": "={{ $json.rss_feed_url }}",
"options": {}
},
"typeVersion": 1.2
},
{
"id": "43a6a17c-ed8a-4885-9727-37c091b25fc2",
"name": "Fetch Processed Links",
"type": "n8n-nodes-base.googleSheets",
"position": [
3152,
2752
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Set Configuration').item.json.processed_sheet_name }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set Configuration').item.json.sheet_id }}"
}
},
"typeVersion": 4.5
},
{
"id": "13cbaa29-d248-4b71-b7dd-2b2a4f900c76",
"name": "Filter New Articles Only",
"type": "n8n-nodes-base.code",
"position": [
3392,
2752
],
"parameters": {
"jsCode": "const processedLinks = new Set(\n $('Fetch Processed Links').all().map(item => item.json.article_link)\n);\n\nreturn $('Fetch RSS Feed').all().filter(item => !processedLinks.has(item.json.link));"
},
"typeVersion": 2
},
{
"id": "77246e2a-d52c-4805-8095-b2fc4d209679",
"name": "Fetch Full Article",
"type": "n8n-nodes-base.httpRequest",
"position": [
3680,
2752
],
"parameters": {
"url": "={{ $json.link }}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "1bb240c7-8829-4097-b567-dc75adabbf62",
"name": "Extract Article Text",
"type": "n8n-nodes-base.html",
"position": [
3920,
2752
],
"parameters": {
"options": {},
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "article_text",
"cssSelector": "article, .post-content, .entry-content"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "d2bf9e92-ca8e-48c5-b460-2051a173c2d1",
"name": "Generate Thread and Post",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
4208,
2752
],
"parameters": {
"text": "=Turn this blog post into social content.\n\nTitle: {{ $('Filter New Articles Only').item.json.title }}\nLink: {{ $('Filter New Articles Only').item.json.link }}\nArticle text: {{ $json.article_text }}\n\nWrite a Twitter/X thread of at most {{ $('Set Configuration').item.json.max_tweets_in_thread }} tweets, each one under 280 characters including any numbering (e.g. \"1/5\"), that captures the article's key points in an engaging way. Also write one longer-form LinkedIn post (under 1300 characters) summarizing the article in a more professional tone, ending with the article link.",
"batching": {},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.8
},
{
"id": "719edc69-8f9a-42a4-9247-764e26a3ee63",
"name": "Thread Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGroq",
"position": [
4128,
2976
],
"parameters": {
"options": {}
},
"typeVersion": 1
},
{
"id": "281c55e1-688d-4f57-b1fb-3e5ba161283c",
"name": "Thread Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
4336,
2976
],
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"tweets\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"linkedin_post\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"tweets\",\n \"linkedin_post\"\n ]\n}"
},
"typeVersion": 1.2
},
{
"id": "3cee1b19-dff2-4f02-8ddf-2ab416ba92cb",
"name": "Validate Tweet Lengths",
"type": "n8n-nodes-base.code",
"position": [
4608,
2752
],
"parameters": {
"jsCode": "const output = $json.output;\nconst tweets = output.tweets || [];\n\nconst tooLong = tweets.filter(t => t.length > 280);\nconst allValid = tooLong.length === 0 && tweets.length > 0;\n\nreturn [{\n json: {\n tweets: tweets,\n linkedin_post: output.linkedin_post,\n all_valid: allValid,\n invalid_count: tooLong.length,\n article_title: $('Filter New Articles Only').first().json.title,\n article_link: $('Filter New Articles Only').first().json.link\n }\n}];"
},
"typeVersion": 2
},
{
"id": "3b06796c-f25d-4991-9934-77da909281b4",
"name": "Log Article as Processed",
"type": "n8n-nodes-base.googleSheets",
"position": [
4848,
2960
],
"parameters": {
"columns": {
"value": {
"was_valid": "={{ $json.all_valid }}",
"article_link": "={{ $json.article_link }}",
"article_title": "={{ $json.article_title }}"
},
"mappingMode": "defineBelow"
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Set Configuration').item.json.processed_sheet_name }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set Configuration').item.json.sheet_id }}"
}
},
"typeVersion": 4.5
},
{
"id": "eab0ed12-441c-4934-87ad-4e42a3a856ab",
"name": "Are All Tweets Valid?",
"type": "n8n-nodes-base.if",
"position": [
4848,
2688
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.all_valid }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "6c2acb98-b550-4fd7-9032-37f2dc3bb35c",
"name": "Log Needs Review",
"type": "n8n-nodes-base.googleSheets",
"position": [
5136,
3328
],
"parameters": {
"columns": {
"value": {
"tweets_json": "={{ JSON.stringify($json.tweets) }}",
"article_link": "={{ $json.article_link }}",
"article_title": "={{ $json.article_title }}",
"invalid_count": "={{ $json.invalid_count }}"
},
"mappingMode": "defineBelow"
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Set Configuration').item.json.review_sheet_name }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Set Configuration').item.json.sheet_id }}"
}
},
"typeVersion": 4.5
},
{
"id": "2f3172a1-69ff-4026-bdb6-d2e65ae3ab0b",
"name": "Split Out Thread Tweets",
"type": "n8n-nodes-base.splitOut",
"position": [
5136,
2688
],
"parameters": {
"options": {},
"fieldToSplitOut": "tweets"
},
"typeVersion": 1
},
{
"id": "1a5fdf03-7bfc-45b9-bb9b-50d970769906",
"name": "Loop Over Items",
"type": "n8n-nodes-base.splitInBatches",
"position": [
5376,
2688
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "ab43537e-95e9-43f6-93db-013741cebdc4",
"name": "Get Previous Tweet ID",
"type": "n8n-nodes-base.code",
"position": [
5616,
2656
],
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('node');\nreturn [{ json: { ...$json, previous_tweet_id: staticData.previous_tweet_id || null } }];"
},
"typeVersion": 2
},
{
"id": "5a7ed06f-0153-4f0b-b1ff-4801eb08ccd7",
"name": "Is First Tweet?",
"type": "n8n-nodes-base.if",
"position": [
5856,
2656
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.previous_tweet_id }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "4cbb3b1b-1e60-43a6-a521-3dc0aaa916d6",
"name": "Post First Tweet",
"type": "n8n-nodes-base.twitter",
"position": [
6096,
2528
],
"parameters": {
"text": "={{ $json.tweets }} {{ $('Set Configuration').item.json.brand_hashtags }}",
"additionalFields": {}
},
"typeVersion": 2
},
{
"id": "3201432e-1791-47aa-a8a4-1d7e6429cc3d",
"name": "Post Reply Tweet",
"type": "n8n-nodes-base.twitter",
"position": [
6096,
2768
],
"parameters": {
"text": "={{ $json.tweets }}",
"additionalFields": {
"inReplyToStatusId": "={{ $json.previous_tweet_id }}"
}
},
"typeVersion": 2
},
{
"id": "8b908449-d887-4153-825a-ec9ff5508ffc",
"name": "Save Tweet ID",
"type": "n8n-nodes-base.code",
"position": [
6336,
2656
],
"parameters": {
"jsCode": "const staticData = $getWorkflowStaticData('node');\nstaticData.previous_tweet_id = $json.id || $json.id_str || staticData.previous_tweet_id;\nreturn [{ json: $json }];"
},
"typeVersion": 2
},
{
"id": "b144a35e-8a0c-4281-80db-9880b75cd7c0",
"name": "Post LinkedIn Update",
"type": "n8n-nodes-base.linkedIn",
"position": [
5696,
3152
],
"parameters": {
"text": "={{ $('Validate Tweet Lengths').item.json.linkedin_post }}",
"additionalFields": {}
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "47f979bf-0724-4ef5-aff5-791bf46f5d73",
"nodeGroups": [],
"connections": {
"Every Hour": {
"main": [
[
{
"node": "Set Configuration",
"type": "main",
"index": 0
}
]
]
},
"Save Tweet ID": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Thread Parser": {
"ai_outputParser": [
[
{
"node": "Generate Thread and Post",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Fetch RSS Feed": {
"main": [
[
{
"node": "Fetch Processed Links",
"type": "main",
"index": 0
}
]
]
},
"Is First Tweet?": {
"main": [
[
{
"node": "Post First Tweet",
"type": "main",
"index": 0
}
],
[
{
"node": "Post Reply Tweet",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[
{
"node": "Post LinkedIn Update",
"type": "main",
"index": 0
}
],
[
{
"node": "Get Previous Tweet ID",
"type": "main",
"index": 0
}
]
]
},
"Post First Tweet": {
"main": [
[
{
"node": "Save Tweet ID",
"type": "main",
"index": 0
}
]
]
},
"Post Reply Tweet": {
"main": [
[
{
"node": "Save Tweet ID",
"type": "main",
"index": 0
}
]
]
},
"Set Configuration": {
"main": [
[
{
"node": "Fetch RSS Feed",
"type": "main",
"index": 0
}
]
]
},
"Thread Chat Model": {
"ai_languageModel": [
[
{
"node": "Generate Thread and Post",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Fetch Full Article": {
"main": [
[
{
"node": "Extract Article Text",
"type": "main",
"index": 0
}
]
]
},
"Extract Article Text": {
"main": [
[
{
"node": "Generate Thread and Post",
"type": "main",
"index": 0
}
]
]
},
"Are All Tweets Valid?": {
"main": [
[
{
"node": "Split Out Thread Tweets",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Needs Review",
"type": "main",
"index": 0
}
]
]
},
"Fetch Processed Links": {
"main": [
[
{
"node": "Filter New Articles Only",
"type": "main",
"index": 0
}
]
]
},
"Get Previous Tweet ID": {
"main": [
[
{
"node": "Is First Tweet?",
"type": "main",
"index": 0
}
]
]
},
"Validate Tweet Lengths": {
"main": [
[
{
"node": "Are All Tweets Valid?",
"type": "main",
"index": 0
},
{
"node": "Log Article as Processed",
"type": "main",
"index": 0
}
]
]
},
"Split Out Thread Tweets": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Filter New Articles Only": {
"main": [
[
{
"node": "Fetch Full Article",
"type": "main",
"index": 0
}
]
]
},
"Generate Thread and Post": {
"main": [
[
{
"node": "Validate Tweet Lengths",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs hourly to pull new items from an RSS feed, fetches and extracts full article text, uses Groq (via LangChain) to generate a Twitter/X thread and a LinkedIn post, then posts valid threads to X and publishes the LinkedIn update while logging results in Google…
Source: https://n8n.io/workflows/17800/ — 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 workflow is built for creators, solopreneurs, SaaS founders, and agencies looking to automate their social media content process from idea to publication. It combines the power of OpenAI, Google
AI Posts Content Machine. Uses agent, stickyNote, outputParserStructured, lmChatAnthropic. Scheduled trigger; 28 nodes.
How it works
AI Social Media Publisher from WordPress. Uses manualTrigger, googleSheets, lmChatOpenRouter, outputParserStructured. Event-driven trigger; 20 nodes.
This workflow automates the process of creating and publishing social media posts across multiple platforms (Twitter/X, Facebook, LinkedIn, and Instagram) based on content from a WordPress post. It us