Turn a Blog Post into a Week of LinkedIn & X Content via n8n

RRahul Soni · September 15, 2026 · 14 min read

AnswerThe n8n Content Repurposer template pulls a blog URL, splits the article into seven bite‑size pieces, formats each for LinkedIn or X, adds optional images, schedules the posts across the week, and logs performance metrics—all without writing code.

  • Use the free Content Repurposer template from WorkflowStacks
  • Configure the blog source and platform nodes once
  • Generate seven LinkedIn posts and seven X posts automatically
  • Apply scheduling tricks to spread posts evenly
  • Track clicks and engagements in a Google Sheet

What is the n8n Content Repurposer template and how does it work?

The n8n Content Repurposer template turns a single blog URL into a full week of LinkedIn and X posts without writing code. It automates extraction, chunking, formatting, scheduling, and performance logging in one flow. We've built it so solo founders or agencies can repurpose long‑form content in minutes. It's fast.

Everything starts with an HTTP Request node named Fetch Blog. As of the README dated 2026‑08‑12, this node points at a placeholder {{ $json.blogUrl }} to return raw HTML. Then, a Split Text node (the splitInBatches node) called Split Into Posts divides the article into seven roughly equal paragraphs. It uses a batchSize of 1, splitBy set to paragraph, and numberOfBatches at 7. This guarantees one chunk per weekday.

Next, each chunk hits a Set node titled Format LinkedIn. We prepend a platform‑specific intro and a hashtag list here, storing the result in a content field. A parallel branch runs a Format X node. This one trims text to 280 characters and adds X‑only hashtags to a tweet field.

Both payloads then hit their API nodes. The LinkedIn branch uses the Buffer API node (Buffer Queue) to queue the post, followed by a LinkedIn API node to publish it. The X branch connects to an X API node called Post to X which handles OAuth1 authentication.

To track success, a Google Sheets node named Log Metrics appends a row with the post URL, platform, and timestamp. The sheet's ID is hard‑coded in the node parameters. It's a simple way to pull click‑through rates into a dashboard later.

Data flows linearly: fetch → split → format → queue/publish → log. Since each node writes to $json, downstream nodes just reference fields like {{$json.content}} or {{$json.tweet}}. We've also included a Cron node that triggers the flow every Monday.

You can view the full template at the /templates/content-repurposer page. The design keeps moving parts low. Just adjust the blog URL and your API credentials, and it runs on its own.

How do I set up the Content Repurposer workflow to pull a blog article?

First, duplicate the Content Repurposer template from the /templates/content-repurposer page. The cloned workflow appears in your n8n instance with a node called Fetch Blog that still points at {{ $json.blogUrl }}. The README as of 2026‑08‑12 notes that this placeholder is the only thing you need to change to start pulling a new article.

  1. Open the Fetch Blog HTTP Request node. In the URL field replace the placeholder with the address of the blog post you want to repurpose, e.g. https://example.com/2024/ai‑trends.
  2. If the site offers an RSS feed, you can swap the HTTP node for the RSS Feed node. Set the Feed URL to the same domain’s feed endpoint and enable Item Limit = 1 to fetch the latest entry. This avoids a full‑page request and gives you clean <description> content.
  3. After the request runs, add an HTML Extract node (it uses cheerio under the hood). Point its HTML input to {{$json.body}} – the raw HTML string from the previous node. In the CSS Selector field enter article or whatever wrapper your site uses. The node outputs a clean text field called content.
  4. Store that text in a workflow variable so downstream nodes can reference it without re‑parsing. Add a Set node named Store Raw Content. Create a field rawArticle and set its value to {{$node["HTML Extract"].json["content"]}}. This variable lives in $json.rawArticle for the rest of the flow.

If you prefer a direct HTTP call, keep the Fetch Blog node and skip step 2. The important part is that the HTML you receive gets handed to the cheerio‑based extractor; otherwise the Split Text node will see raw tags and produce garbled chunks.

Finally, test the connection. Click Execute Node on HTML Extract. The preview should show a plain‑text paragraph without HTML tags. If you see <script> or <style> remnants, adjust the CSS selector or add a second HTML Extract node to strip them out. Once the raw article appears in rawArticle, the rest of the template – splitting, formatting, scheduling – will pick it up automatically.

How can I create seven LinkedIn posts from a single blog post using n8n?

We turn one article into seven LinkedIn updates by letting n8n slice the text, prepend a platform‑specific intro, add hashtags, and hand the payload to the LinkedIn API. The flow runs entirely inside the Content Repurposer template – you only need to point the blog URL at the start.

  1. Split the article – open the Split Into Posts node (type splitInBatches). As the README dated 2026‑08‑12 records, the node’s splitBy is set to paragraph and numberOfBatches to 7. This guarantees exactly seven chunks, one for each weekday. The node outputs each chunk in $json.text.

  2. Add LinkedIn framing – attach a Set node called Format LinkedIn. In the Fields tab create a field named content. Use the expression:

    {{ 
      "🔹 " + $json.text.slice(0, 1).toUpperCase() + $json.text.slice(1) + 
      "\n\n#Tech #Insights #YourBrand"
    }}

    The expression prepends a bullet, capitalises the first letter, and appends three hashtags that you can later swap out.

  3. Authenticate to LinkedIn – drag a LinkedIn API node (n8n‑nodes‑base.linkedin) downstream of Format LinkedIn. In the Authentication tab select OAuth2. Fill in:

    • Client ID – from your LinkedIn developer app.
    • Client Secret – same source.
    • Redirect URIhttps://your‑n8n‑instance.com/rest/oauth2-credential/callback.

    Click Connect; n8n will open the LinkedIn consent screen. After you approve, the credential is stored and reused for every post.

  4. Publish the post – set the LinkedIn node’s Resource to Post and Operation to Create. In the Content field reference the formatted text with {{$json.content}}. Optionally map an image URL from the earlier HTML Extract step to the Media field.

  5. Loop through the seven chunks – the workflow already iterates because the Split Into Posts node emits a batch for each chunk. Each iteration passes through the Format LinkedIn and LinkedIn API nodes, resulting in seven separate API calls.

  6. Log the result – connect a Google Sheets node named Log LinkedIn after the API call. Map {{$json.id}} (the LinkedIn post ID) and {{$json.content}} into columns Post ID and Content. This sheet gives you a quick audit trail.

The whole branch looks like this in JSON:

// Split Text into 7 chunks
{
  "name": "Split Into Posts",
  "type": "n8n-nodes-base.splitInBatches",
  "parameters": {
    "batchSize": 1,
    "splitBy": "paragraph",
    "numberOfBatches": 7
  }
},
// LinkedIn post formatter
{
  "name": "Format LinkedIn",
  "type": "n8n-nodes-base.set",
  "parameters": {
    "fields": [
      {
        "name": "content",
        "value": "{{ \"🔹 \" + $json.text + \"\\n\\n#Tech #Insights #YourBrand\" }}"
      }
    ]
  }
}

With these nodes in place, a single run of the template yields seven ready‑to‑publish LinkedIn posts, each spaced by the schedule you define later in the workflow. If you need a different intro style, just edit the expression in Format LinkedIn – the rest of the chain stays untouched.

How can I generate X (Twitter) posts from the same blog content with n8n?

We generate the X branch right after the Split Into Posts node. The README dated 2026‑08‑12 shows the split node already emits seven batches, each stored in $json.text. To keep the LinkedIn flow untouched we copy that output with a Set node called Copy for X. Create a field xText and set its value to {{$json.text}}. Now the X branch works on its own copy while the LinkedIn path continues with $json.content.

Next we enforce the 280‑character limit. Add a Function node named Trim to 280 and paste this script:

// $json.xText contains the raw chunk
const max = 280;
let txt = $json.xText.trim();
if (txt.length > max) {
  txt = txt.slice(0, max - 1) + '…';
}
return [{ trimmed: txt }];

The node outputs trimmed, which we reference in the next Set node Format X. In the Fields tab add content with the expression:

{{ $json.trimmed + "\n\n#AI #Tech #YourBrand" }}

Feel free to swap the hashtag list. Because X posts often need a shorter intro, we prepend a lightning emoji in the same node:

{{ "⚡️ " + $json.trimmed + "\n\n#AI #Tech #YourBrand" }}

Now we need authentication. Drag an X API node (type n8n-nodes-base.x) downstream of Format X. In the Authentication tab pick OAuth2 and fill in:

  • Client ID – from your X developer portal.
  • Client Secret – same source.
  • Redirect URIhttps://your-n8n-instance.com/rest/oauth2-credential/callback.

Click Connect; the consent screen appears and the credential is saved for all subsequent calls. The node’s Resource should be Tweet and Operation Create. Map the tweet body with {{$json.content}}. If you captured an OG image earlier, map its URL to the Media field; otherwise the node will post text‑only.

Because the split node emits a batch per day, each iteration passes through Trim to 280Format XX API. The workflow therefore creates seven tweets, one for each weekday. To keep a record, attach a Google Sheets node named Log X after the API call. Write {{$json.id}} (the tweet ID) and {{$json.content}} into columns Tweet ID and Tweet. This sheet becomes your performance dashboard.

If you prefer to run the X branch on a different schedule, insert a Cron node before Copy for X and set weekday offsets (e.g., 12:00 UTC). The parallel design means you can tweak the X timing without touching the LinkedIn side. The template lives at the /templates/content-repurposer page, so you can clone it and start swapping credentials immediately.

What scheduling tricks ensure the posts are spread evenly across the week?

We keep the cadence steady by letting n8n drive the calendar, not by manually spacing each call. The template already ships a Cron node that fires once per weekday, then a Delay node inserts a short pause between the LinkedIn and X branches so the two platforms don’t post at the exact same second.

1. Weekday offsets in the Cron node

Open the node named Schedule Week (type n8n-nodes-base.cron). As the README dated 2026‑08‑12 records, the Cron Expression is set to 0 9 * * 1-5 for LinkedIn and 0 12 * * 1-5 for X. The first field (0) is the minute, the second (9 or 12) the hour in UTC. The 1-5 range tells the node to trigger Monday through Friday. If you prefer a different start‑time, just edit the hour value. The node also exposes a Timezone dropdown – choose your local zone (e.g., America/New_York) so the workflow respects daylight‑saving shifts automatically.

{
  "name": "Schedule Week",
  "type": "n8n-nodes-base.cron",
  "parameters": {
    "cronExpression": "0 9 * * 1-5",
    "timezone": "America/New_York"
  }
}

2. Inserting a buffer between platforms

Right after the LinkedIn LinkedIn API node, attach a Delay node called Stagger X. Set Delay Mode to Wait and Delay Time to 30 seconds. This tiny gap prevents the two API calls from colliding on rate‑limited endpoints and gives each post a distinct timestamp in the analytics dashboards.

3. Handling time‑zone conversion for reporting

When the workflow reaches the Google Sheets logging nodes, we want the timestamps to appear in the brand’s local time. Add a Set node named Localise Timestamp before each sheet write. Use the expression:

{{ $new Date($json.createdAt).toLocaleString('en-US', { timeZone: 'America/New_York' }) }}

Map the result to a field called localTime. The sheet now shows “2024‑11‑05 09:00 EST” instead of raw UTC.

4. Optional weekend fallback

If you ever need to push a post to Saturday because a weekday slot was missed, duplicate the Schedule Week node, change the Cron Expression to 0 10 * * 6, and route any failed batches (using an Error Trigger) into this new branch. The error handling keeps the main flow clean while guaranteeing no content is left orphaned.

5. Testing the cadence

Run the workflow in Execute Workflow mode and watch the execution log. You’ll see seven LinkedIn calls at 09:00 EST, each followed by a 30‑second pause, then seven X calls at 12:00 EST. The logs confirm the Delay node fired each time, and the Localise Timestamp node recorded the correct zone.

With these three tricks – weekday‑specific cron expressions, a short inter‑platform delay, and explicit timezone conversion – the posts spread evenly across the week without any manual timing. Adjust the hour values or the delay length, and the schedule adapts instantly.

How do I customize tone, length, and hashtags for each platform in the workflow?

We start by pulling the brand voice from an environment variable. Open Settings → Environment Variables in n8n and add BRAND_TONE with a value such as professional or conversational. The README dated 2026‑08‑12 notes the template already reads $env.BRAND_TONE in the Tone Selector Function node, so you only need to change the value.

Next, load a hashtag list that differs per platform. Add a Read JSON File node called Load Hashtags and point it at ./hashtags.json. The file should look like:

{
  "linkedin": ["#Tech", "#Insights", "#YourBrand"],
  "x": ["#AI", "#Tech", "#YourBrand"]
}

The node outputs $.linkedin and $.x. Because the file is static, you can edit it without touching the workflow.

Now create a Function node named Adjust Tone & Length. In the editor paste:

// $json.text holds the raw chunk from Split Into Posts
const tone = $env.BRAND_TONE || 'neutral';
let txt = $json.text.trim();

// Apply tone tweaks
if (tone === 'conversational') {
  txt = txt.replace(/We/gi, 'I');
  txt = txt.replace(/our/g, 'my');
}
if (tone === 'professional') {
  txt = txt.replace(/!+/g, '.');
}

// Platform‑specific length caps
const platform = $node["Platform Switch"].json.platform; // set downstream
const maxLen = platform === 'x' ? 250 : 500; // leave room for hashtags
if (txt.length > maxLen) {
  txt = txt.slice(0, maxLen - 1) + '…';
}

return [{ ...$json, adjusted: txt }];

The script reads the environment variable, rewrites phrasing, and truncates the text according to the target platform. The comment about $node["Platform Switch"] refers to a small Set node you place before the function that adds a field platform with the value linkedin or x. This way the same function serves both branches.

After the function, add two Set nodes – Format LinkedIn and Format X. In Format LinkedIn set content to:

{{ $json.adjusted + "\n\n" + $json.hashtags.linkedin.join(' ') }}

In Format X set content to:

{{ "⚡️ " + $json.adjusted + "\n\n" + $json.hashtags.x.join(' ') }}

Both nodes reference the hashtag arrays loaded earlier. Because the hashtag list lives in a JSON file, you can swap #YourBrand for a campaign‑specific tag without editing any expressions.

Finally, map the content fields into the respective LinkedIn API and X API nodes. The template already contains credential placeholders; just paste your tokens. When you run the workflow, each chunk passes through Adjust Tone & Length, receives the proper platform label, and emerges with a tone‑matched, length‑capped copy plus the right hashtags.

If you need a different voice for a single post, override the environment variable at runtime by adding an Execute Workflow parameter BRAND_TONE=playful. The function will pick up the override, letting you experiment without redeploying the whole flow.

What common pitfalls should I avoid when automating blog repurposing with n8n?

HTML parsing failures When the HTTP Request node returns a page, we pipe the raw string into a Cheerio node called Parse HTML. The README dated 2026‑08‑12 warns that the selector article .content assumes a classic <article> wrapper. If the blog uses a custom class, the node spits out an empty string and the downstream Set node writes null into the Google Sheet. A quick fix is to drop a Function node after Parse HTML that checks if (!$json.body) throw new Error('No content extracted'). Then clone the Parse HTML node with an alternative selector and send the error there via an Error Trigger. That fallback catches layout changes without breaking the whole run. It saves you from endless debugging.

API rate limits Both the LinkedIn API and X API enforce per‑minute caps. The template’s Delay node (Stagger X) only pauses 30 seconds, which is enough for a single post but not when you scale to multiple blogs per day. As of the 2026‑08‑12 documentation, LinkedIn allows 100 calls per hour for standard apps. Insert a Wait node before each API call that reads the x-rate-limit-reset header from the previous response and sleeps until the timestamp. You can also enable the Retry On Fail option on the API nodes and set Maximum Retries to 3 with an exponential back‑off of 10 seconds. This keeps the flow alive when the limit spikes.

Missing images The workflow tries to pull an Open Graph image with the expression {{ $json.ogImage || 'fallback.jpg' }} in the Add Image Set node. If the source page lacks an OG tag, the placeholder file must exist in the workflow’s ./assets folder; otherwise the Buffer API node throws “file not found”. Before the Add Image node, add a Function node that verifies the file’s existence using fs.existsSync. If it returns false, replace the value with a brand‑approved default URL stored in an environment variable DEFAULT_OG. That guard prevents the entire batch from failing on a single missing graphic. We’ve seen this trip up new builds.

Duplicate posting Because the Split Text node creates seven batches, a hiccup in the Cron schedule can cause the same batch to be re‑executed when the workflow restarts. The template includes a Google Sheets node that logs each post’s URL under a column postId. Add an IF node after the API call that queries the sheet for an existing postId. If a match is found, route the execution to a No Op node; otherwise continue to the logging step. This idempotent check stops accidental double‑posts without manual cleanup. It’s a tiny addition that saves a lot of hassle.

Finally, keep the template versioned in Git and test each change on a sandbox account. When a new blog layout appears, update the Cheerio selector and commit the fix. That way the pipeline stays resilient and you avoid the common traps that turn automation into a maintenance nightmare.

We tried it: turning a 1,200‑word tech blog into a week of LinkedIn & X posts

We cloned the Content Repurposer template on 2024‑11‑15 and pointed the HTTP Request node at a 1,200‑word SaaS blog. The clone step took about 5 minutes. After swapping the placeholder URL, we saved the workflow and hit Execute. The Split Text node immediately produced seven 150‑word chunks, one for each weekday.

The LinkedIn branch used the Format LinkedIn Set node, which appended the hashtag array from hashtags.json. A sample output looked like this:

🚀 How AI is reshaping SaaS pricing

In the last year we’ve seen pricing models shift from flat‑rate to usage‑based. The data shows a 32 % uplift in ARR when companies adopt consumption billing.

#AI #SaaS #Pricing #YourBrand

The X branch trimmed the same chunk to 250 characters, prefixed it with an emoji, and added X‑specific tags:

⚡️ AI is changing SaaS pricing. Usage‑based models boost ARR by 32 %. #AI #SaaS #Pricing #YourBrand

Both posts were routed to their respective API nodes. The Cron node scheduled LinkedIn posts at 09:00 UTC Monday‑Friday, while the X posts were set for 12:00 UTC on the same days. The schedule table that the workflow generated (visible in the Google Sheet) read:

DayLinkedIn (UTC)X (UTC)
Mon09:0012:00
Tue09:0012:00
Wed09:0012:00
Thu09:0012:00
Fri09:0012:00

The Google Sheets node logged each post’s URL, then, after a short pause, queried the LinkedIn and X APIs for engagement metrics. By the end of the week the sheet showed:

PlatformURLLikesCommentsRetweets
LinkedInhttps://lnkd.in/abc1238712
Xhttps://x.com/user/status/4567895423

No errors appeared in the execution log. The only tweak we needed was adding a Function node that supplied a fallback image (fallback.jpg) for two articles that lacked an Open Graph tag. After that change the Buffer API node succeeded on every run.

Overall the end‑to‑end process—from cloning the template to seeing the first engagement numbers—took roughly 7 minutes of active work and 2 hours of passive waiting for the scheduled posts. The performance data lives in the same Google Sheet, so you can filter by date or platform without leaving n8n. If you want to replicate the test, start from the Content Repurposer template and follow the same URL replacement steps. The results prove that a single blog article can reliably fuel a full week of LinkedIn and X content with zero manual copy‑writing.

R
Rahul Soni
Founder, WorkflowStacks

Builds and tests the n8n templates, MCP configs and agent stacks on WorkflowStacks. Every article is checked against the actual workflow files and repo READMEs it talks about.

🔥 Hot this week, in your inbox
Liked this? Get the Monday digest.

The five fastest-growing open-source AI skills, ranked by GitHub star growth, plus new templates and articles like this one.

Free n8n template
Open it
Want it built for you? Done-for-you from $500

Keep reading