RSS Keyword Monitor — Only New Items & Webhooks
Pricing
from $2.00 / 1,000 new items
RSS Keyword Monitor — Only New Items & Webhooks
Monitor any RSS/Atom feeds for keywords on a schedule. Delivers & charges only new items since the last run. Webhooks, regex, n8n/Make/MCP ready.
Pricing
from $2.00 / 1,000 new items
Rating
0.0
(0)
Developer
Telemark Digital
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
0
Monthly active users
3 days ago
Last modified
Categories
Share
If you're re-parsing the same RSS feeds on a schedule and deduping downstream, this is your bill cut. RSS Keyword Monitor watches any RSS / Atom / RDF feeds, filters by your keywords or regex, and delivers only the new items it has never sent you before — with a webhook when something new lands. Most polls return nothing new and cost almost nothing, because unchanged feeds are skipped with conditional GET (HTTP 304). Built for pipelines and AI agents, not for reading in a browser.
What RSS Keyword Monitor does
Give it a list of feed URLs and (optionally) keywords, regex patterns, and exclude terms. Run it on an Apify Schedule — hourly, every 15 minutes, daily, whatever. On each run it:
- fetches every feed (sending
If-None-Match/If-Modified-Since, so unchanged feeds return 304 and cost nothing), - parses RSS 2.0, Atom 1.0 and RSS 1.0 (RDF), handling legacy encodings and broken feeds without crashing,
- keeps a durable per-Task seen-set, so it suppresses repeat delivery within your configured dedup window,
- filters by keyword / regex, drops your exclude terms, and
- writes one clean JSON record per new item to the dataset — and only charges for those new items.
It turns a schedule (or an AI agent poll) into a cheap, recurring "what's new in my feeds" event stream. You can download the results as JSON, CSV, Excel or HTML, pull them from the API, wire them into n8n / Make / Zapier, or call it as an MCP tool.
When to use it — and when NOT to (please read this)
This Actor is honest about where it wins. Use it when you have specific feeds + filtering + cross-run dedup + one batched machine-readable call. Don't use it for these three cases — we'll tell you the better tool:
- One-off bulk reads of a feed? Use a plain feed reader (e.g.
automation-lab/rss-feed-reader). It's cheaper per item for a single full dump. We're built for the recurring delta, not the archive pull — and on a one-shot read we're honestly ~2× more expensive per item. - Watching just 1–3 feeds, unfiltered? A free n8n RSS Feed Trigger (self-hosted) or Make "Watch RSS feed items" will do it. We start paying off when you have many feeds, keyword/regex filtering, and want cross-run dedup state without wiring up custom code.
- A human reading keyword alerts in an inbox? Inoreader Pro (~€6.67/mo, 2,500 feeds, 30 alert rules) or Feedly beat us for eyeballs. We are not an inbox — no reading UI, no ≤1h "refresh." We emit structured per-item JSON deltas for machines.
Still here? Then you're the buyer: an automation operator or agent feeding a pipeline, not an inbox.
How much does it cost?
Two events: $0.005 per run (actor-start) and $0.002 per new matching item (new-item, i.e. $2.00 per 1,000 new items). You are charged only for new items after filtering — duplicates, excluded items, unchanged (304) feeds and failed feeds are never billed. A quiet hourly watch is dominated by the tiny per-run fee, and most of those runs find nothing new.
| Your workload | This Actor | A stateless reader charging per item every poll | You save |
|---|---|---|---|
| 20 feeds, hourly, ~2 new matching/run | 720 × ($0.005 + 2×$0.002) = $6.48/mo | 720 × ($0.035 + ~200×$0.001) = $169/mo | ~26× |
| Same, their most defensive 1-item config | — | 720 × $0.055 = $39.60/mo (misses bursts) | ~6× |
| 20 feeds, daily digest, ~40 new/day | 30 × ($0.005 + 40×$0.002) = $2.55/mo | 30 × $0.235 = $7.05/mo | ~2.8× |
| One-shot bulk read, 10 feeds × 50 items | $0.005 + 500×$0.002 = $1.01 | $0.035 + 500×$0.001 = $0.54 | they win ~1.9× — use them |
| vs Make native, 50 feeds @ 15-min | 2,880 × $0.005 + items ≈ $15–20/mo | ~144k ops/mo ≈ $30+/mo tier, no keyword filter | ~2× + filtering |
The wedge is the per-run fee and the fact that scheduled watches are mostly duplicates — which you shouldn't pay to receive again. On a hot, always-changing firehose (every item is new), the per-item price dominates and a plain reader is cheaper; if that's you, use one.
Only users on a paid Apify plan are billed; the free tier's monthly credit lets you trial it. Prices can change with 14 days' notice (Apify policy).
2-minute quickstart
- Click Try for free.
- In Feed URLs, paste one or more feeds (defaults are prefilled so you can just hit Start).
- Optionally add Keywords (e.g.
acquisition,funding round) — leave empty to get every new item. - Click Start. The first run seeds the seen-set and returns matching items (up to Max items per run).
- Run it again — you'll get 0 new items (everything's already been delivered). That's the point.
Run it on a schedule (the whole point)
Cross-run dedup only makes sense on a Task + Schedule, because state is keyed by Task.
- Configure the input, then Create task ▸.
- Open the Task ▸ Schedule ▸ e.g. every hour.
- Add a Webhook URL (below) to get pushed only when there's something new.
Example Task input:
{"feeds": ["https://hnrss.org/frontpage","https://www.theverge.com/rss/index.xml","https://feeds.bbci.co.uk/news/rss.xml"],"keywords": ["acquisition", "funding round", "layoffs"],"excludeTerms": ["rumor"],"matchFields": ["title", "description"],"onlyNew": true,"webhookUrl": "https://your-endpoint.example.com/rss-hook"}
Recipes
n8n
Replace N "RSS Feed Trigger" nodes + N "Filter" nodes with one node. Add an HTTP Request node (or the Apify node) that runs this Actor, or add a Webhook node and paste its URL into webhookUrl — you'll receive a POST only when there are new items. Self-hosted n8n users watching 1–3 feeds don't need us; 50-feed graphs do.
Make (Integromat)
Every Make scenario poll consumes operations even when nothing is new — a 50-feed 15-minute watch burns ~144k ops/mo before any processing, and the native RSS module has no keyword filter. Instead: one Apify run polls all feeds and returns only filtered deltas to a single Custom webhook module. Point webhookUrl at that module's URL.
Slack (or Discord / Teams)
Create an Incoming Webhook in Slack and put its URL in webhookUrl. Slack endpoints receive a concise text message with up to 10 item links. Other endpoints receive the raw JSON payload below.
Raw webhook payload
webhookUrl receives a POST (Content-Type: application/json) only when there are new items:
{"summary": {"newItemsDelivered": 2,"feedsOk": 8,"feedsNotModified": 20,"feedsError": 0,"onlyNew": true,"chargeLimitReached": false,"webhookDelivered": true,"charge": { "actorStart": 1, "newItemEvents": 2 }},"items": [{"feedUrl": "…","title": "…","link": "…","matchedTerms": ["acquisition"],"firstSeenAt": "…","isNew": true}],"itemsTruncated": false}
items carries up to the 50 newest records; the full set is always in the dataset. A webhook failure is logged but never fails the run.
Use it from an AI agent (MCP)
Eligible paid Apify Actors can be exposed through mcp.apify.com when you add them to your MCP server configuration. The only-new semantics fit agent polling — "what's new in these feeds since I last asked?" Call this Actor with feeds + keywords; on a Task it remembers what it already returned within the dedup window.
Input
| Field | Type | Default | Notes |
|---|---|---|---|
feeds | string[] | Apify blog + BBC samples | RSS/Atom/RDF URLs, 1–500. Replace the samples with the feeds you want to monitor. |
keywords | string[] | [] | Case-insensitive substrings, OR-matched. Empty = deliver every new item. |
regexPatterns | string[] | [] | OR-matched with keywords (case-insensitive). Invalid pattern fails the run with a clear error. |
excludeTerms | string[] | [] | Drop matched items containing any of these. Applied before charging. |
matchFields | string[] | ["title","description"] | Any of title, description, content, categories. |
onlyNew | boolean | true | Off = plain filtered read of the current feeds (still charged per item). |
maxItemsPerRun | integer | 200 | Hard billing ceiling. In only-new mode, validators are held so overflow is retried on the next run. |
dedupWindow | integer | 45 | Days an item stays in the seen-set (7–365). |
includeContent | boolean | true | Include content:encoded HTML per item when present. No article-page fetching. |
webhookUrl | string | — | POSTed only when there are new items. |
resetState | boolean | false | Wipe this Task's seen-set + HTTP validators. Use once, then turn off. |
Output
One dataset record per new matching item:
{"feedUrl": "https://hnrss.org/frontpage","feedTitle": "Hacker News: Front Page","feedType": "rss","itemKey": "guid:https://news.ycombinator.com/item?id=44861023","guid": "https://news.ycombinator.com/item?id=44861023","title": "EU AI Act enforcement begins for foundation models","link": "https://example.com/eu-ai-act","author": "tosh","publishedAt": "2026-07-11T08:14:02.000Z","firstSeenAt": "2026-07-11T08:30:11.412Z","categories": ["ai", "regulation"],"description": "Enforcement of the EU AI Act's obligations…","content": "<p>Full content:encoded HTML when the feed provides it…</p>","matchedTerms": ["eu ai act"],"isNew": true}
A per-run summary (per-feed statuses, counts, charge totals, and spending-limit status) is written to the run's key-value store under OUTPUT — the dataset stays clean for pipelines. firstSeenAt is our server clock at first delivery. publishedAt is the feed's untrusted claim: it is never used as a time cursor, but it is part of the hash fallback when an item has neither GUID nor link.
How the "only new" state works
- Keyed by Task. State lives under your Task ID, so editing keywords or feeds never resets history — no fragile manual snapshot key to fat-finger. Ad-hoc (non-Task) runs share a persistent per-user scope; create a Task for isolated recurring monitoring.
- Sharded per feed in a named key-value store, pruned on writes, capped at 20,000 identity keys/feed (oldest evicted). GUID items can use two keys. Eviction degrades to occasional re-delivery, not failure.
dedupWindowcontrols how long an item is remembered. Feeds that legitimately re-surface an item older than the window will re-deliver it.- Redirects: we follow up to 5; on a permanent (301) redirect we log the new URL and recommend you update it — state stays on the URL you configured, so renaming never orphans history.
resetState: truefully wipes this Task's seen-set and stored validators for a clean start.
Feed handling & reliability
- Formats: RSS 2.0, Atom 1.0, RSS 1.0 (RDF). (JSON Feed is not yet supported — it returns a clear per-feed error, not a crash.)
- Encodings: respects the Content-Type / XML-declaration charset with an
iconvfallback for legacy JP/CN encodings; strips BOMs; tolerates leading whitespace; detects HTML error pages instead of choking on them. - Per-feed isolation: each network attempt has a hard timeout and every response has a 10 MB cap; one dead, hostile or malformed feed becomes a single
errorrow in the summary — the run still succeeds and you're not billed for it. - Politeness: a descriptive User-Agent,
gzipaccepted, conditional GET on delta polls, bounded retries, and sequential requests for feeds configured on the same host. Redirects are bounded and revalidated. WAF-fronted feeds may still block cloud egress.
Run a recurring Task no more frequently than its previous run can finish. Concurrent runs of the same Task are not transactional and can race on shared state.
How it compares
| Tool | Multi-feed | Keyword/regex filter | Cross-run dedup (only-new) | Charges per new item | Webhook | For |
|---|---|---|---|---|---|---|
| This Actor | ✅ 1–500 | ✅ keyword + regex + exclude | ✅ Task-keyed, durable | ✅ | ✅ | pipelines / agents |
| automation-lab/rss-feed-reader | ✅ | ➖ downstream | ❌ (dedup downstream) | ❌ per item every poll | ➖ | bulk reads |
| santamaria rss reader | ✅ | ❌ | ❌ | per feed-fetch | ❌ | bulk fetches |
| taroyamada feed aggregator | ✅ | ✅ keywords | ✅ (manual snapshotKey) | ✅ | ✅ | credit where due — closest peer |
| n8n RSS Trigger (free) | ➖ 1/node | ✅ (Filter node) | ❌ (needs custom staticData) | n/a (compute) | ✅ | 1–few feeds, self-host |
| Make "Watch RSS" | ➖ 1/module | ❌ | ✅ but burns ops each poll | n/a (ops) | ✅ | Make-native flows |
| Inoreader / Feedly | ✅ | ✅ | ✅ (for reading) | n/a (subscription) | ➖ | humans reading alerts |
No trash talk — several of these are great at what they do. We own exactly one lane: scheduled, filtered, deduped, machine-readable feed deltas.
FAQ
What does the first run return? Everything currently matching (up to maxItemsPerRun), and it seeds the seen-set. Subsequent runs return only what's new since. To seed silently, set a low maxItemsPerRun on the first run.
A feed has no GUIDs — will I get duplicates? No. When guid is absent we key on link, and if that's missing too, on a stable hash of feedUrl + title + pubDate. We also guard against WordPress-style GUID rotation (same link+title, new guid → treated as already seen).
A feed keeps re-surfacing old items. They're suppressed while within dedupWindow. If they resurface after it, they re-deliver — raise the window if that's unwanted.
Does editing keywords or feeds reset my history? No — state is keyed by Task, not by your config.
Why did a run "succeed" with a dead feed? Per-feed isolation: the dead feed is an error row in the run summary (and unbilled); the rest of the run proceeds. Check the OUTPUT record for per-feed detail.
Can it fetch the full article text? No — it returns what the feed provides (content:encoded when present). Article-page extraction is a different, costlier job.
JSON Feed? Not yet — it returns a clear per-feed error rather than failing the run.
Support
Found a bad feed or an edge case? Open an issue on the Actor's Issues tab with the feed URL — feed pathology is exactly what this Actor is built to handle well, and reports make it better.
