# RSS Feed Monitor for News & Blogs - Only New Posts (`venturetoolsai/rss-feed-monitor`) Actor

RSS feed monitor for news sites, blogs, podcasts and release notes. Check many RSS, Atom and JSON Feed URLs in one run and get only the posts added since the last run, in one clean dataset. Failed feeds are free. Respects robots.txt.

- **URL**: https://apify.com/venturetoolsai/rss-feed-monitor.md
- **Developed by:** [Bill Roberts](https://apify.com/venturetoolsai) (community)
- **Categories:** News, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 feed items

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## RSS, Atom & JSON Feed Monitor - Only New Items

Check many RSS, Atom and JSON Feed URLs in one run and get **one clean dataset with the same fields for every feed**. Schedule it with **Only new items** turned on, and each run returns (and charges for) only the posts that were not delivered before.

### What it does

- **Reads standard feeds.** RSS 0.9x / 1.0 / 2.0, Atom and JSON Feed. You paste the feed URLs; the Actor does not search websites for feeds and does not read normal web pages.
- **One consistent output.** Every item has the same fields, whatever the feed format: `title`, `link`, `published` (ISO date, UTC), `summary` (plain text, HTML removed, up to 1,000 characters), `author`, `categories`, `enclosures` (for example podcast audio files), `feedTitle`, `feedUrl` and a stable `itemKey`. Optionally `contentText`: the full text the feed provides, HTML removed, up to 20,000 characters.
- **Only new items (optional).** Remembers which items it already delivered, in a named key-value store in your own Apify account, so scheduled runs return only new items. It also sends standard "has this changed?" headers (ETag / Last-Modified), so unchanged feeds are cheap to check.
- **Two simple optional filters:**
  - **Published in the last N hours.** Drops items older than N hours. Items with no date in the feed are kept.
  - **Keywords (any).** A plain text match. An item is kept if its title, summary or categories contain any of your words, ignoring upper/lower case. It matches parts of words too, so `art` also matches "start". There is no smart or AI matching.
- **Clear error rows.** A feed that can't be read gets its own row with an error code (for example `FEED_HTTP_404`, `NOT_A_FEED`, `INVALID_URL`, `ROBOTS_DISALLOWED`, `SITE_UNREACHABLE`), and the rest of the run continues.
- **Polite to websites:**
  - Follows robots.txt by default. If a site's robots.txt can't be reached at all, that feed is skipped.
  - At most about 1 request per second to the same site.
  - Retries temporary errors (HTTP 429 / 5xx, network errors) up to 3 times.
  - Ignores feeds larger than 10 MB.
  - Only public http(s) addresses are accepted.

### How to use it

1. Paste one or more feed URLs into **Feed URLs**. Sample input (these are example data sources, not endorsements): `https://blog.python.org/rss.xml`, `https://xkcd.com/atom.xml`.
2. Click **Start**. Results appear in the **Output** tab, ready to export as JSON, CSV, Excel and other formats Apify supports.
3. To monitor feeds, turn on **Only new items since the last run**, give the monitor its own **State store name**, and create an Apify **Schedule** (for example hourly or daily).

#### Input

| Field | Default | What it does |
|---|---|---|
| `feedUrls` | (required) | Feed URLs. Duplicates are removed. |
| `onlyNewItems` | `false` | Return only items not delivered by an earlier run with the same state store. |
| `stateStoreName` | `rss-monitor-state` | Name of the store that remembers delivered items. Use a different name for each separate monitor. |
| `sinceHours` | empty | Keep only items published in the last N hours (1–8,760). |
| `keywords` | empty | Keep only items whose title, summary or categories contain any of these words (plain text match). |
| `includeContent` | `false` | Add `contentText` (full feed text, up to 20,000 characters) when the feed provides it. |
| `maxItemsPerFeed` | 50 | Read at most the first N entries of each feed (feeds usually list the newest first). |
| `maxTotalItems` | 1,000 | Stop after delivering this many items in a run. |
| `maxFeeds` | 50 | Check at most this many feeds per run; extra feeds are skipped. |
| `respectRobotsTxt` | `true` | Skip feeds that the site's robots.txt does not allow. |

#### Output example (real output from a test run)

```json
{
  "feedUrl": "https://blog.python.org/rss.xml",
  "feedTitle": "Python Insider",
  "title": "Python 3.15.0 (final) is here!",
  "link": "https://blog.python.org/2026/10/python-3150-final-is-here/",
  "published": "2026-10-09T00:00:00Z",
  "categories": ["releases"],
  "summary": "A brand new Python here for you to enjoy!",
  "enclosures": [],
  "itemKey": "…",
  "fetchedAt": "2026-10-11T05:45:58Z",
  "error": null
}
```

Error row example: `{"feedUrl": "not a url", "error": {"code": "INVALID_URL", "message": "Not an http(s) URL: 'not a url'"}}`

### Pricing (pay per event)

| Event | Price | When it is charged |
|---|---|---|
| `feed-item` | **$0.001** ($1 per 1,000 items) | Each item delivered to your dataset |
| `feed-checked` | $0.0002 | Each feed fetched successfully, also when there is nothing new |
| `apify-actor-start` | $0.00005 | Once per run at up to 1 GB memory (Apify's standard start event; one per GB of memory) |

- **Free of charge:** feeds that fail, are unreachable, are blocked by robots.txt, or are invalid. Error rows are never charged.
- **Measured example:** a test run with 2 feeds and 6 items cost **$0.00645** (1 start + 2 feeds + 6 items) and took about 4 seconds.
- **Monitoring example (calculated):** checking 5 feeds every hour with nothing new costs $0.00105 per run, about $0.76 per 30 days. Each new item adds $0.001.
- **Spending caps:** set **Maximum cost per run** in the run options (at least $0.01), and/or `maxTotalItems`. The run stops cleanly when a limit is reached.
- **No separate platform-usage charge:** you pay only the events above.

### Good to know

- **Only new items needs a store.** It works when the same **State store name** is used run after run. A new name starts fresh, and the first run returns the current items.
- **Without Only new items,** every run returns the current feed items again and charges for them. Turn it on for schedules.
- **How long items are remembered.** Up to the last 2,000 delivered items are remembered per feed.
- **Breakage warning (only with Only new items):** if a feed that worked before suddenly returns a web page or something unreadable, the run log shows an `ALERT feed_possibly_broken` line, and the feed gets an error row. If it suddenly returns zero items, the run log shows the same alert line, with no error row.
- **Run summary.** Each run saves a `RUN_STATS` record (counts, errors, charges) in its key-value store.
- **Permissions:** runs with **limited permissions**. It only uses its own run storage and the named store it creates for Only new items.
- **Content rights:** feeds are published for syndication, but the content belongs to its publishers. Respect their terms and copyright if you republish it. The Actor only reads public feed URLs you provide. It does not log in or bypass paywalls.

### Support

Found a problem or need a feature? Open an issue in the **Issues** tab.

# Changelog

This Actor's version history is a separate document: https://apify.com/venturetoolsai/rss-feed-monitor/changelog.md

# Actor input Schema

## `feedUrls` (type: `array`):

RSS 0.9x/1.0/2.0, Atom or JSON Feed URLs. Duplicates removed.

## `onlyNewItems` (type: `boolean`):

Remembers delivered items in a named key-value store, so scheduled runs return (and charge for) new items only. Uses conditional GET (ETag / Last-Modified).

## `stateStoreName` (type: `string`):

Named key-value store used by 'Only new items'. Use different names for independent monitors.

## `sinceHours` (type: `integer`):

Leave empty for no date filter.

## `keywords` (type: `array`):

Keep only items whose title, summary or categories contain any keyword (case-insensitive).

## `includeContent` (type: `boolean`):

Adds contentText (HTML stripped, max 20,000 chars) when the feed provides it.

## `maxItemsPerFeed` (type: `integer`):

Newest N entries read from each feed.

## `maxTotalItems` (type: `integer`):

Safety cap on delivered (and charged) items.

## `maxFeeds` (type: `integer`):

Safety cap on feeds per run. Extra feeds are skipped.

## `respectRobotsTxt` (type: `boolean`):

Skip feeds that robots.txt disallows (recommended).

## Actor input object example

```json
{
  "feedUrls": [
    "https://blog.python.org/rss.xml"
  ],
  "onlyNewItems": false,
  "stateStoreName": "rss-monitor-state",
  "includeContent": false,
  "maxItemsPerFeed": 50,
  "maxTotalItems": 1000,
  "maxFeeds": 50,
  "respectRobotsTxt": true
}
```

# Actor output Schema

## `items` (type: `string`):

One row per delivered feed item, plus one error row per feed that could not be read.

## `runStats` (type: `string`):

Counts, error codes, charged events and stop reason for the run.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "feedUrls": [
        "https://blog.python.org/rss.xml"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("venturetoolsai/rss-feed-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "feedUrls": ["https://blog.python.org/rss.xml"] }

# Run the Actor and wait for it to finish
run = client.actor("venturetoolsai/rss-feed-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "feedUrls": [
    "https://blog.python.org/rss.xml"
  ]
}' |
apify call venturetoolsai/rss-feed-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,venturetoolsai/rss-feed-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/gvRilfEtCpmVwKcKO/builds/n2p5aepgNdgfAjbDE/openapi.json
