This workflow corresponds to n8n.io template #14970 — we link there as the canonical source.
This workflow follows the HTTP Request → LinkedIn 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 →
{
"nodes": [
{
"id": "8ac0a983-be97-4e6f-ae9b-85a6dc61a0ed",
"name": "\ud83d\udccb MAIN \u2014 Workflow Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-96,
-768
],
"parameters": {
"width": 620,
"height": 712,
"content": "## \ud83d\udcf0 Auto-Post Blog Content to LinkedIn & Twitter/X\n\n**What this workflow does:**\nThis template monitors your blog's RSS feed for new posts, extracts the title, summary, and cover image, uploads the image to a public URL via the UploadToURL node, then generates platform-optimized captions using AI and auto-posts to both LinkedIn and Twitter/X \u2014 all hands-free.\n\n**Trigger:** RSS Feed polling (every 15 minutes)\n\n**Key nodes used:**\n- \ud83d\udd01 RSS Feed Trigger\n- \ud83e\uddf9 Data Extraction & Cleanup (Code Node)\n- \u2601\ufe0f UploadToURL (mandatory image hosting)\n- \ud83e\udd16 AI Caption Generator (OpenAI)\n- \ud83d\udd17 LinkedIn Post\n- \ud83d\udc26 Twitter/X Post\n- \u2705 Success Logger\n\n**Setup Requirements:**\n1. Add your RSS Feed URL in the RSS Trigger node\n2. Configure OpenAI API credentials\n3. Add LinkedIn OAuth2 credentials\n4. Add Twitter/X OAuth1 credentials\n5. UploadToURL node requires no extra auth \u2014 paste your upload endpoint\n\n**Note:** Deduplication logic is built-in via item uniqueness check to avoid re-posting old articles."
},
"typeVersion": 1
},
{
"id": "d084d08d-f0b6-4777-9796-0eb2a3a28ee0",
"name": "\ud83d\udcdd Note \u2014 Trigger & Filter",
"type": "n8n-nodes-base.stickyNote",
"position": [
624,
-16
],
"parameters": {
"color": 7,
"width": 588,
"height": 440,
"content": "### \ud83d\udd01 Step 1 \u2014 RSS Trigger & Filter\n**RSS Feed Trigger:** Polls your blog RSS every 15 min for new items.\n**IF Node:** Checks that both `title` and `enclosure` (cover image URL) are present. If no image is found, the workflow exits gracefully \u2014 no broken posts."
},
"typeVersion": 1
},
{
"id": "af3e35ec-f0e5-4976-9737-67c908bf4348",
"name": "\ud83d\udcdd Note \u2014 Fetch & Upload Image",
"type": "n8n-nodes-base.stickyNote",
"position": [
1232,
-16
],
"parameters": {
"color": 7,
"width": 652,
"height": 440,
"content": "### \u2601\ufe0f Step 2 \u2014 Fetch & Upload Image\n**HTTP Request (Fetch Image):** Downloads the raw binary of the blog cover image from the enclosure URL.\n**UploadToURL Node:** Takes the binary image data and uploads it to your configured public storage endpoint. Returns a hosted public URL used in social posts."
},
"typeVersion": 1
},
{
"id": "92221d21-dad5-4ac5-b21a-38f83317c965",
"name": "\ud83d\udcdd Note \u2014 AI Caption & Social Posting",
"type": "n8n-nodes-base.stickyNote",
"position": [
1904,
-64
],
"parameters": {
"color": 7,
"width": 908,
"height": 600,
"content": "### \ud83e\udd16 Step 3 \u2014 AI Caption + Posting\n**OpenAI Node:** Generates two platform-specific captions \u2014 one for LinkedIn (professional tone, 150-200 words, with hashtags) and one for Twitter/X (punchy, under 260 chars with 2-3 hashtags).\n**LinkedIn + Twitter/X Nodes:** Post the AI-generated captions with the hosted image URL. Both run in parallel after the AI step."
},
"typeVersion": 1
},
{
"id": "fd8ec451-3cec-495b-955c-a54a3ea0e0c2",
"name": "RSS Feed \u2014 Blog Monitor",
"type": "n8n-nodes-base.rssFeedReadTrigger",
"position": [
640,
224
],
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
}
},
"typeVersion": 1
},
{
"id": "786d7bb9-03b5-425d-9a19-932bfd47da8e",
"name": "IF \u2014 Has Title & Cover Image",
"type": "n8n-nodes-base.if",
"position": [
864,
224
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "check-title",
"operator": {
"type": "string",
"operation": "isNotEmpty"
},
"leftValue": "={{ $json.title }}",
"rightValue": ""
},
{
"id": "check-image",
"operator": {
"type": "string",
"operation": "isNotEmpty"
},
"leftValue": "={{ $json.enclosure?.url }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2
},
{
"id": "51d82d45-98b0-4157-8534-67368299906a",
"name": "Code \u2014 Extract & Clean Blog Data",
"type": "n8n-nodes-base.code",
"position": [
1072,
224
],
"parameters": {
"jsCode": "const item = $input.first().json;\n\nconst title = item.title || '';\nconst link = item.link || '';\nconst imageUrl = item.enclosure?.url || item['media:content']?.['@_url'] || '';\n\n// Strip HTML tags from content/summary\nconst rawSummary = item.contentSnippet || item.summary || item.content || '';\nconst cleanSummary = rawSummary\n .replace(/<[^>]+>/g, '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/ /g, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .substring(0, 500);\n\nconst pubDate = item.pubDate || item.isoDate || new Date().toISOString();\n\nreturn [{\n json: {\n title,\n link,\n imageUrl,\n summary: cleanSummary,\n pubDate,\n slug: title.toLowerCase().replace(/[^a-z0-9]+/g, '-')\n }\n}];"
},
"typeVersion": 2
},
{
"id": "f47b124c-2f3e-4beb-bc5e-bcdbcfda90fd",
"name": "HTTP \u2014 Fetch Cover Image Binary",
"type": "n8n-nodes-base.httpRequest",
"position": [
1296,
224
],
"parameters": {
"url": "={{ $json.imageUrl }}",
"options": {
"timeout": 15000,
"redirect": {
"redirect": {
"maxRedirects": 5
}
},
"response": {
"response": {
"responseFormat": "file"
}
}
}
},
"typeVersion": 4.2
},
{
"id": "faf1fa1a-fbb9-47a8-9a5f-1518b1fe5af1",
"name": "Code \u2014 Merge Hosted URL with Blog Data",
"type": "n8n-nodes-base.code",
"position": [
1744,
224
],
"parameters": {
"jsCode": "const uploadResponse = $input.first().json;\nconst prevData = $('Code \u2014 Extract & Clean Blog Data').first().json;\n\n// Handle various uploadtourl response formats\nconst hostedUrl = \n uploadResponse?.url ||\n uploadResponse?.data?.url ||\n uploadResponse?.file?.url ||\n uploadResponse?.link ||\n prevData.imageUrl; // fallback to original if upload fails\n\nreturn [{\n json: {\n ...prevData,\n hostedImageUrl: hostedUrl\n }\n}];"
},
"typeVersion": 2
},
{
"id": "1588fa1f-5442-41da-b14d-fbd9f949b0fd",
"name": "OpenAI \u2014 Generate Platform Captions",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
1952,
224
],
"parameters": {
"resource": "chat"
},
"typeVersion": 1.4
},
{
"id": "b37fbddd-84d6-4a15-9e20-938774c00c30",
"name": "Code \u2014 Parse AI Captions Safely",
"type": "n8n-nodes-base.code",
"position": [
2176,
224
],
"parameters": {
"jsCode": "const aiResponse = $input.first().json;\nconst blogData = $('Code \u2014 Merge Hosted URL with Blog Data').first().json;\n\nlet captions = { linkedin: '', twitter: '' };\n\ntry {\n const raw = aiResponse?.choices?.[0]?.message?.content || '{}';\n // Remove markdown code fences if present\n const cleaned = raw.replace(/```json|```/g, '').trim();\n captions = JSON.parse(cleaned);\n} catch (e) {\n // Fallback captions\n captions.linkedin = `\ud83d\udcd6 New Blog Post: ${blogData.title}\\n\\n${blogData.summary.substring(0, 200)}...\\n\\nRead more: ${blogData.link}\\n\\n#Blog #Business #Content`;\n captions.twitter = `\ud83d\udcd6 ${blogData.title} \u2014 ${blogData.link} #Blog #MustRead`;\n}\n\nreturn [{\n json: {\n ...blogData,\n linkedinCaption: captions.linkedin,\n twitterCaption: captions.twitter\n }\n}];"
},
"typeVersion": 2
},
{
"id": "17ac6813-23f2-4890-9e4d-3460f092dd0a",
"name": "LinkedIn \u2014 Publish Post with Image",
"type": "n8n-nodes-base.linkedIn",
"position": [
2400,
128
],
"parameters": {
"text": "={{ $json.linkedinCaption }}",
"person": "={{ $json.linkedinPersonUrn }}",
"additionalFields": {
"title": "={{ $json.title }}"
},
"shareMediaCategory": "IMAGE"
},
"typeVersion": 1
},
{
"id": "8a292f70-edf9-4e68-9f30-3de84e18cb1c",
"name": "Twitter/X \u2014 Publish Tweet",
"type": "n8n-nodes-base.twitter",
"position": [
2400,
352
],
"parameters": {
"text": "={{ $json.twitterCaption }}",
"additionalFields": {}
},
"typeVersion": 1
},
{
"id": "e809ff73-6948-4762-aaee-938510b94d74",
"name": "Code \u2014 Success Logger",
"type": "n8n-nodes-base.code",
"position": [
2624,
224
],
"parameters": {
"jsCode": "const data = $('Code \u2014 Parse AI Captions Safely').first().json;\nconsole.log(`\u2705 Blog post published: ${data.title}`);\nconsole.log(`\ud83d\udd17 LinkedIn caption length: ${data.linkedinCaption?.length}`);\nconsole.log(`\ud83d\udc26 Twitter caption length: ${data.twitterCaption?.length}`);\nconsole.log(`\ud83d\uddbc\ufe0f Hosted image: ${data.hostedImageUrl}`);\nreturn [{ json: { status: 'success', title: data.title, timestamp: new Date().toISOString() } }];"
},
"typeVersion": 2
},
{
"id": "858b40b8-0549-4cbc-9464-ca9f2538737e",
"name": "Upload a File",
"type": "n8n-nodes-uploadtourl.uploadToUrl",
"position": [
1504,
224
],
"parameters": {},
"credentials": {
"uploadToUrlApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
}
],
"connections": {
"Upload a File": {
"main": [
[
{
"node": "Code \u2014 Merge Hosted URL with Blog Data",
"type": "main",
"index": 0
}
]
]
},
"RSS Feed \u2014 Blog Monitor": {
"main": [
[
{
"node": "IF \u2014 Has Title & Cover Image",
"type": "main",
"index": 0
}
]
]
},
"Twitter/X \u2014 Publish Tweet": {
"main": [
[
{
"node": "Code \u2014 Success Logger",
"type": "main",
"index": 0
}
]
]
},
"IF \u2014 Has Title & Cover Image": {
"main": [
[
{
"node": "Code \u2014 Extract & Clean Blog Data",
"type": "main",
"index": 0
}
]
]
},
"Code \u2014 Parse AI Captions Safely": {
"main": [
[
{
"node": "LinkedIn \u2014 Publish Post with Image",
"type": "main",
"index": 0
},
{
"node": "Twitter/X \u2014 Publish Tweet",
"type": "main",
"index": 0
}
]
]
},
"HTTP \u2014 Fetch Cover Image Binary": {
"main": [
[
{
"node": "Upload a File",
"type": "main",
"index": 0
}
]
]
},
"Code \u2014 Extract & Clean Blog Data": {
"main": [
[
{
"node": "HTTP \u2014 Fetch Cover Image Binary",
"type": "main",
"index": 0
}
]
]
},
"LinkedIn \u2014 Publish Post with Image": {
"main": [
[
{
"node": "Code \u2014 Success Logger",
"type": "main",
"index": 0
}
]
]
},
"OpenAI \u2014 Generate Platform Captions": {
"main": [
[
{
"node": "Code \u2014 Parse AI Captions Safely",
"type": "main",
"index": 0
}
]
]
},
"Code \u2014 Merge Hosted URL with Blog Data": {
"main": [
[
{
"node": "OpenAI \u2014 Generate Platform Captions",
"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.
uploadToUrlApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Turn your blog into a self-driving social media machine. This workflow monitors your RSS feed, extracts new content, and uses AI to craft platform-perfect posts for LinkedIn and Twitter/X, complete with hosted images.
Source: https://n8n.io/workflows/14970/ — 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.
Overview This workflow automates the process of converting RSS feed articles into ready-to-publish social media posts using OpenAI, NocoDB, and Telegram. It's ideal for content teams, marketing manage
Never let an event go unpromoted. This workflow monitors your Google Calendar, generates branded promotional graphics, and schedules a staggered social media countdown across LinkedIn, Twitter/X, and
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
✨🤖Automated AI Powered Social Media Content Factory for X + Facebook + Instagram + LinkedIn. Uses outputParserStructured, lmChatGoogleGemini, lmChatOpenAi, httpRequest. Event-driven trigger; 57 nodes
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