n8n: fetch an RSS feed on a schedule and save only the new items
A workflow that reads a blog's RSS feed every morning and appends the new posts to a file — without adding the same post twice. The deduplication is the whole point: a feed reader that re-saves yesterday's posts is a log file with duplicates. The workflow file below is the exact file that was imported and run for this guide.
On this page
What you need
- Node.js 20.19 to 24.x. n8n requires a Node.js version between 20.19 and 24.x, inclusive (n8n Docs, Install with npm). This guide ran on Node.js 24.22.1.
- n8n 2.46.7, started on your own machine. No account, no credentials — RSS is a public protocol.
- A terminal for the install and the file setup.
- An RSS feed to watch. This guide watches n8n's own blog,
https://blog.n8n.io/rss/. Swap in any feed by changing the URL in the RSS Read node.
Key result: every morning at 8:00, the workflow fetches the feed and appends only the posts it has not seen before to n8n-posts.csv. In the test run, three scheduled runs in a row left the file at 16 lines — a header plus the same 15 posts, added once.
The workflow at a glance
Node versions in the file: Schedule Trigger 1.4, RSS Read 1.2, Code 2, Edit Fields 3.5, Convert to File 1.2, Read/Write Files 1.1.
Download the workflow: workflow.json — import it into n8n in step 3. It is the exact file that was imported and run for this guide, including the file path used on the test machine (change it in step 4).
1. Install and start n8n
Run n8n from the terminal. n8n Docs, Install with npm describes this as the quick way to try n8n locally: the command downloads what it needs and serves the editor at http://localhost:5678.
Command
npx n8n
Open http://localhost:5678. The first start asks you to create an owner account (an email address and a password, kept on your own machine). Note that npm-based installs are deprecated from n8n 3.0, per the same install page — if you are on 3.0 or later, use the Docker install it links to instead, and re-check the node versions above before importing.
2. Make the file
On a default n8n 2 install, the Read/Write Files node can only write under ~/.n8n-files. That is the default value of N8N_RESTRICT_FILE_ACCESS_TO from n8n 2.0 (n8n Docs, Read/Write Files from Disk). Create the folder and the file with its header row now — the workflow appends data rows only, so the header has to exist first.
Command
mkdir -p ~/.n8n-files
printf 'title,link,pubDate\n' > ~/.n8n-files/n8n-posts.csv
3. Import the workflow
In n8n, open the workflow menu (the ⋯ at the top right of the canvas) and choose Import from file, then pick workflow.json. The canvas shows seven nodes in a line. What each node does:
- Every morning (Schedule Trigger). Runs the workflow at a fixed time. It works like the Cron utility: this workflow uses the custom cron expression
0 8 * * *, daily at 8:00 (n8n Docs, Schedule Trigger). You must publish the workflow for the schedule to fire. Note the timezone: the node uses the workflow timezone if set, otherwise the instance timezone, which defaults toAmerica/New_Yorkfor self-hosted instances — set the workflow timezone in the workflow settings if 8:00 should mean your time. - Read n8n blog (RSS Read). Reads the feed at the URL you give it — here
https://blog.n8n.io/rss/(n8n Docs, RSS Read). Each feed item arrives withtitle,link,pubDate, andguid. - Keep only new items (Code). The deduplication step, in Run Once for All Items mode (n8n Docs, Code). It keeps a list of items it has already seen in the workflow's static data, drops any item whose key is already in that list, and saves the updated list:
Config — Keep only new items, JavaScript
// Keep only feed items not seen in an earlier run.
// Seen links live in the workflow's static data, which n8n saves after each run.
const staticData = $getWorkflowStaticData('global');
staticData.seen = staticData.seen || [];
const seen = new Set(staticData.seen);
const fresh = [];
for (const item of $input.all()) {
const key = item.json.guid || item.json.link;
if (key && !seen.has(key)) {
seen.add(key);
fresh.push(item);
}
}
staticData.seen = [...seen];
return fresh;
The key is the item's guid, falling back to its link. Static data is the workflow's own small persistent store: the same object in every node of the workflow, saved by n8n after the run. The seen list grows by one entry per new post, so it stays small for a daily feed; for a feed with hundreds of new items a day, store the list in a database instead.
- Pick fields (Edit Fields). Keeps
title,link, andpubDateand drops the rest of the feed data. Include other fields is off. - Build CSV lines (Code). Turns the kept fields into CSV lines with a trailing newline, quoting any field that contains a comma, a quote, or a newline:
Config — Build CSV lines, JavaScript
const esc = (v) => {
const s = String(v ?? '');
return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
};
const lines = $input.all().map(({ json }) =>
[json.title, json.link, json.pubDate].map(esc).join(','),
);
return [{ json: { line: lines.join('\n') + '\n' } }];
- To text file (Convert to File). Operation Convert to Text File, source field
line, with Add BOM off (n8n Docs, Convert to File). - Append to file (Read/Write Files). Operation Write File to Disk, with the Append option on (n8n Docs, Read/Write Files from Disk).
4. Set the file path
Open the Append to file node and set File Path and Name to the absolute path of the file you made in step 2 — for example /home/alex/.n8n-files/n8n-posts.csv. The downloaded file carries the path used on the test machine (/home/hatch/.n8n-files/n8n-posts.csv); change it to yours. Keep the file under ~/.n8n-files unless you have changed N8N_RESTRICT_FILE_ACCESS_TO, or the write is refused.
5. Activate it
Click Publish. The schedule only fires on a published workflow — the Schedule Trigger docs say so directly (n8n Docs, Schedule Trigger). The first run lands at the next 8:00.
6. Prove the deduplication works
Do not wait a day to find out whether the dedup works. Temporarily change the schedule to run every minute, watch two runs, then set it back:
- Open the Every morning node, switch the trigger interval to Custom (Cron), and set the expression to
*/1 * * * *. - Publish the workflow again and wait about two minutes.
- Open the Executions tab. You should see two scheduled runs, both with status
success. - Set the expression back to
0 8 * * *and publish again.
In the test run for this guide, three scheduled runs fired at 21:34, 21:35, and 21:36 UTC. The first added 15 posts; the second and third added none. The workflow's stored seen list held 15 entries afterwards.
Trap — manual runs do not deduplicate. If you click Execute workflow in the editor to test, every run re-adds all the items. That is expected, not a bug in the Code node: n8n saves static data only when the published workflow runs from its trigger, not on manual runs. The docs say the same — static data is not available when testing workflows; the workflow must be active and called by a trigger or webhook to save static data. Judge the dedup on scheduled runs only, as in this step.
7. Check the file
Command
cat ~/.n8n-files/n8n-posts.csv
Output (first three rows from the test run)
title,link,pubDate
"Send contracts for e-signature from n8n, with TurboDocx, Slack approval and SharePoint filing",https://blog.n8n.io/spotlight-turbodocx/,"Sun, 08 Nov 2026 14:07:15 GMT"
Throttling vs. rate limiting: Backoff strategies for workflows,https://blog.n8n.io/throttling-vs-rate-limiting/,"Fri, 30 Oct 2026 16:30:34 GMT"
Why we're moving n8n's automation team beyond the service desk model,https://blog.n8n.io/why-were-moving-n8ns-automation-team-beyond-the-service-desk-model/,"Thu, 29 Oct 2026 07:10:00 GMT"
The file held 16 lines after the test: the header plus 15 posts, each once. Titles and dates containing commas are quoted, so the file still parses as CSV. Run wc -l ~/.n8n-files/n8n-posts.csv after the next scheduled run — the count should grow only when the blog publishes something new.
Key result: three scheduled runs, 15 posts saved once, zero duplicates. The first run added 15 rows; the second and third added 0, and the file stayed at 16 lines. Two manual runs before that each re-added all 15 — which is how the manual-run trap above was found.
Traps
Trap — manual runs do not save static data. Covered in step 6: clicking Execute workflow runs the whole workflow, including the file write, but the seen list is not saved. Every manual run looks like the first run. Test the flow manually; test the dedup on the schedule.
Trap — Convert to File's CSV mode is wrong for appending. The first version of this workflow used Convert to File's Convert to CSV operation. Each chunk started with a byte-order mark and had no trailing newline, so rows glued together. Build the lines in a Code node and convert them with Convert to Text File and Add BOM off, as in step 3.
Trap — the header row must already exist. The workflow appends data rows only. If n8n-posts.csv does not exist, the first run creates it without a header, and the file has no column names. Make the file with the header first, as in step 2.
Trap — 8:00 means the instance timezone unless you set one. The Schedule Trigger uses the workflow timezone if set, otherwise the instance timezone, which defaults to America/New_York for self-hosted instances. If the workflow fires at the wrong hour, set the timezone in the workflow settings.
Trap — the seen list never shrinks. Every new post adds one entry to the workflow's static data, and nothing in this workflow removes entries. That is fine for a daily blog feed. If you point this at a high-volume feed, the list grows without bound — switch the store to a database or a file, and keep only the newest hundred keys.
Summary
A Schedule Trigger fires daily at 8:00, RSS Read fetches the feed, a Code node drops every item already in the workflow's static seen list, Edit Fields keeps title, link, and pubDate, a second Code node builds CSV lines, Convert to File turns them into a file, and Read/Write Files appends them. Three scheduled runs saved 15 posts exactly once. The two mistakes that cost time during the build — manual runs re-adding everything because static data is only saved on scheduled runs, and Convert to File's CSV mode adding a byte-order mark with no trailing newline — are both in the traps above, with the working fix in the workflow file.
Sources
- n8n Docs, Schedule Trigger node — Trigger Rules, the cron examples, the publish requirement, and the timezone rules
- n8n Docs, RSS Read node — the URL parameter and the RSS Feed Trigger note
- n8n Docs, Code node — Run Once for All Items mode, and that the Code node cannot access the file system
- n8n Docs, getWorkflowStaticData — global vs node static data, and that static data is not saved when testing workflows
- n8n Docs, Convert to File node — Convert to CSV and Convert to Text File operations
- n8n Docs, Read/Write Files from Disk node — Write File to Disk, the Append option, and the
~/.n8n-filesdefault in n8n 2.0 - n8n Docs, Install with npm — the
npx n8nstart, the Node.js 20.19–24.x requirement, and that npm installs are deprecated from n8n 3.0
Verified 9 November 2026 on n8n 2.46.7 (Node.js 24.22.1) by importing workflow.json, publishing it, and running it on a one-minute schedule: three scheduled executions (21:34, 21:35, 21:36 UTC), all status success. The first added 15 rows to n8n-posts.csv; the second and third added 0; the stored seen list held 15 entries. The feed read was the live https://blog.n8n.io/rss/. The schedule was restored to 0 8 * * * after the test.