# RSS Feed Reader & New Items Monitor (`zerolag/rss-feed-reader`) Actor

Read RSS, Atom and JSON feeds, or paste a website and the Actor finds its feed. Get only items that are new since your last run, with the full article text and podcast files. You pay only for new items saved, so a quiet feed costs almost nothing.

- **URL**: https://apify.com/zerolag/rss-feed-reader.md
- **Developed by:** [ZeroLag Data](https://apify.com/zerolag) (community)
- **Categories:** News, Automation, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 feed reader that turns RSS, Atom and JSON feeds into clean rows. Paste a feed or a plain website: the Actor finds the feed, saves only items that are new since your last run, and can add the full article text.

- **Price:** $3.00 per 1,000 feed items on the Free plan, $2.10 on Gold. Platform usage is included.
- **Input:** feed addresses, websites, OPML lists, subreddits, Medium profiles, GitHub repositories.
- **Output:** title, link, date, summary, full article text, categories, image, podcast file and length.
- **Measured:** 15 items from 3 feeds, with full text, in 7 seconds. The next run found nothing new, took 2 seconds and charged only the $0.00005 start fee.

Found a bug, need a field, or want a new Actor? Open an issue on the **Issues** tab. If it works well for you, a short review helps others find it.

### How to use it

1. Paste your feeds or websites into **Feeds or websites**, one per line.
2. Keep **Only new items** on to use the Actor as a monitor. Turn on **Full article text** if a teaser is not enough.
3. Click **Start**. The first run saves the latest items of each feed.
4. Schedule the Actor, hourly or daily. Every later run saves and charges only items it has not seen.

```json
{
    "urls": ["https://blog.apify.com/", "https://feeds.bbci.co.uk/news/rss.xml", "krebsonsecurity.com"],
    "includeFullText": true,
    "maxItemsPerFeed": 5
}
```

| You paste | The Actor reads |
| --- | --- |
| A feed address | RSS 0.9x, 1.0 or 2.0, Atom, or JSON Feed |
| A website | The feed the page names, then links that look like a feed, then usual paths such as `/feed` |
| The address of an OPML file | Every feed in the list |
| A subreddit, a Medium profile | Its feed |
| A GitHub repository | Its release notes |

### What data you get

Every item has the same fields. A value the feed does not give is `null`.

| Field | What it holds |
| --- | --- |
| `recordType` | `item`, or `notice` for the free row that explains an empty run |
| `title`, `url`, `summary` | Title, link, and plain text up to 1,000 characters |
| `publishedAt`, `updatedAt` | Dates in UTC |
| `contentText`, `contentHtml` | The body the feed carries, as plain text and as published |
| `fullText`, `fullTextWords` | The whole article with its paragraphs, and the word count |
| `fullTextSource`, `fullTextError` | `feed` or `page`, or why there is no full text |
| `categories`, `imageUrl` | Tags and the main image |
| `isNew` | `true` if the item appeared after the monitor's first read, `false` if it was already there, `null` with **Only new items** off |
| `author` | Empty unless you turn on **Include author names**, because names are personal data. Email addresses are never saved |
| `enclosureUrl`, `enclosureType`, `enclosureBytes` | Audio or video file |
| `durationSeconds`, `episode`, `season`, `transcriptUrl` | Podcast details |
| `commentsUrl`, `itemId` | Comments page and the id the feed uses |
| `feedTitle`, `feedUrl`, `feedType`, `feedLanguage`, `siteUrl` | The feed that was read |
| `sourceUrl`, `retrievedAt` | The address from your input and the time of the read |

A real item, without the long text fields:

```json
{
    "recordType": "item",
    "title": "Widespread PHP Flaw May Cause Many Site Outages Today",
    "url": "https://krebsonsecurity.com/2026/10/widespread-php-flaw-may-cause-many-site-outages-today/",
    "publishedAt": "2026-10-21T15:26:07.000Z",
    "imageUrl": "https://krebsonsecurity.com/wp-content/uploads/2026/10/shutterstock-codeonscreenphp.png",
    "isNew": false,
    "feedTitle": "Krebs on Security",
    "feedUrl": "https://krebsonsecurity.com/feed/",
    "feedType": "rss",
    "sourceUrl": "https://krebsonsecurity.com/",
    "fullTextWords": 674,
    "fullTextSource": "feed"
}
```

Each run also writes a **Feed report**: for each address the feed found, a status such as `ok`, `unchanged`, `refused` or `no_feed_found`, the reason, and the item counts.

### How much does it cost?

| Plan | Per 1,000 feed items |
| --- | --- |
| Free | $3.00 |
| Bronze | $2.70 |
| Silver | $2.40 |
| Gold | $2.10 |

You pay only for items saved to the dataset. Items seen before, items you filter out, failed feeds and notices cost nothing. Each run also has a start fee of $0.00005. Full text costs no extra, and there is no separate charge for platform usage.

Example: 10 feeds that publish 20 new items a day make about 600 items a month, which cost $1.80 on the Free plan plus the start fees. To cap a run, set **Items per run**, or set a maximum cost in the run options.

### How to monitor RSS feeds for new items

1. Save your list as an Apify task, or give it a **Monitor name**. Each task and each name has its own memory, so two lists that share a feed do not take items from each other.
2. Set **Items per feed**. The first run saves that many newest items of each feed and treats the rest as old, so you do not pay for the backlog. These rows have `isNew: false`.
3. Schedule it. Later rows have `isNew: true`.

An item is new when neither its id nor its link has been seen, so a feed that changes the id of a story but keeps its link does not create a duplicate. The same article in two of your feeds is saved and charged once. An edited article is not new.

No item is lost at a limit. If **Items per feed**, **Items per run** or your spending limit stops a run, the items it did not reach come with the next run, if the feed still holds them. In a test with a $0.05 limit, the first run saved 18 of 130 items and the next run saved the other 112.

The memory is a key-value store in your Apify account, named `rss-feed-reader-<monitor name>`. A task with no name uses `rss-feed-reader-task-<task id>`, and direct runs use `rss-feed-reader-default`. Delete it, or use a new name, to start over.

### How to get the full article text

Turn on **Full article text**. If the feed holds the whole article, the Actor uses it and reads no page. If the feed holds a teaser, the Actor reads the page once and takes the article out of it. `fullTextSource` says which one happened. `fullText` keeps the paragraphs, so you can pass it to a summary, a search index or an AI pipeline.

In a test with 16 feeds, 229 of 254 items got full text in 163 seconds: 90 from the feed and 139 from the page. The other 25 were 20 Reddit items closed by robots.txt, 4 podcast episodes with no page, and 1 page with no article text. Such items are still saved with what the feed gave, and `fullTextError` says why.

### How to track podcasts

Podcast feeds work like any other feed. A 20 MB podcast feed took 3 seconds in a test.

```json
{
    "title": "10pm ET, November 05 | The latest headlines from NPR",
    "publishedAt": "2026-11-06T03:09:51.000Z",
    "feedTitle": "NPR News Now",
    "enclosureType": "audio/mpeg",
    "enclosureBytes": 4586728,
    "durationSeconds": 280
}
```

### How to read a list of feeds

Export your subscriptions as OPML, put the file online and paste its address. A public list of 28 feeds gave 46 items, 2 from each of 23 feeds, in 6 seconds. The other 5 were YouTube feeds, which do not work at the moment.

### How to filter by keyword and date

This input keeps items from the last 7 days that mention a data breach or ransomware. Filtered items are not charged.

```json
{
    "urls": ["https://krebsonsecurity.com/feed/", "https://techcrunch.com/"],
    "onlyNewItems": false,
    "includeKeywords": ["data breach", "ransomware"],
    "publishedAfter": "7 days"
}
```

Only whole words match, so `AI` does not match `email`. The Actor looks in the title, summary, feed content and categories, not in text it reads from pages.

### Limits

- **A feed is not an archive.** You get only what the feed holds at that moment. In a test with 19 feeds, most held 10 to 50 items.
- **No bypass.** The Actor uses one open User-Agent, no proxy and no login. If a site refuses, the feed gets the status `refused`. The Guardian's feed answered HTTP 403 in our tests.
- **Pages.** robots.txt is obeyed, a paywall stays a paywall, and a page that builds its text with JavaScript gives no full text.
- **YouTube.** Channel feeds answered HTTP 404 in our tests, so YouTube does not work at the moment.
- **Feeds only.** A site with no feed gets the status `no_feed_found`.
- **One run per monitor at a time.** Two runs that overlap can save the same items twice.

### API and integrations

Run the Actor from the [Apify API](https://docs.apify.com/api/v2), on a schedule, or from Make, Zapier and n8n, and read the items from the run's dataset. A `notice` row is not an item: skip it, or turn off **Save a notice when nothing is saved**. AI agents can call the Actor through the [Apify MCP server](https://mcp.apify.com).

### FAQ

**Why did the second run save nothing?** Nothing new was published. The run adds one free notice row and charges only the start fee.

**How do I read a whole feed again?** Turn off **Only new items**, or use a new **Monitor name**.

**Why is one feed missing from the results?** Open the **Feed report** in the run output. A feed that cannot be read does not stop the run and costs nothing.

**Can I republish the full text?** Articles belong to their publishers. Check the terms of each site before you store or republish its text.

**How many feeds can one run read?** Up to 5,000 addresses, OPML lists included. More memory reads more feeds at the same time.

*This Actor is an independent tool with no connection to any feed publisher, Reddit, Medium, GitHub or YouTube. All trademarks belong to their owners.*

# Actor input Schema

## `urls` (type: `array`):

RSS, Atom or JSON Feed addresses, OPML lists, or plain website addresses, one per line. For a website the Actor finds the feed itself. A subreddit, a Medium profile or a GitHub repository works too.

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

On: the Actor remembers what it saved. The first run saves the latest items of each feed and marks the rest as old. Every later run saves and charges only items it has not seen. Off: every run reads each feed from the top.

## `monitorName` (type: `string`):

Separate memory for each list of feeds. Use a new name to start over. Left empty, each saved task has its own memory and direct runs share one. Letters, numbers and hyphens.

## `includeFullText` (type: `boolean`):

Adds the whole article to each item, also when the feed holds only a teaser. If the feed already has the whole text, the page is not read. Pages closed by robots.txt or by a login stay closed, and the item says why in fullTextError.

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

The most items to save from one feed in one run, newest first. In a monitor, new items above this number wait for the next run. 0 means no limit.

## `maxItems` (type: `integer`):

The run stops after saving this many items in total. In a monitor, the items it did not reach come in the next run. 0 means no limit.

## `publishedAfter` (type: `string`):

Keep only items from this date on. Pick a date or a period such as 7 days. Items without a date are kept. Empty means no date limit.

## `includeKeywords` (type: `array`):

Keep items that mention at least one of these words or phrases. Case does not matter, and only whole words match, so AI does not match email. The Actor looks in the title, summary, feed content and categories.

## `excludeKeywords` (type: `array`):

Drop items that mention any of these words or phrases. Same rules as Must contain.

## `includeAuthors` (type: `boolean`):

Author names are personal data, so the author field stays empty unless you turn this on. Email addresses are never saved.

## `noticeWhenEmpty` (type: `boolean`):

A run that saves no items adds one free row that says why, for example no new items. Turn this off if your workflow treats every row as an item.

## Actor input object example

```json
{
  "urls": [
    "https://blog.apify.com/",
    "https://feeds.bbci.co.uk/news/rss.xml"
  ],
  "onlyNewItems": true,
  "includeFullText": true,
  "maxItemsPerFeed": 5,
  "maxItems": 1000,
  "includeKeywords": [],
  "excludeKeywords": [],
  "includeAuthors": false,
  "noticeWhenEmpty": true
}
```

# Actor output Schema

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

No description

## `feeds` (type: `string`):

No description

# 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 = {
    "urls": [
        "https://blog.apify.com/",
        "https://feeds.bbci.co.uk/news/rss.xml"
    ],
    "onlyNewItems": true,
    "includeFullText": true,
    "maxItemsPerFeed": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("zerolag/rss-feed-reader").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 = {
    "urls": [
        "https://blog.apify.com/",
        "https://feeds.bbci.co.uk/news/rss.xml",
    ],
    "onlyNewItems": True,
    "includeFullText": True,
    "maxItemsPerFeed": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("zerolag/rss-feed-reader").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 '{
  "urls": [
    "https://blog.apify.com/",
    "https://feeds.bbci.co.uk/news/rss.xml"
  ],
  "onlyNewItems": true,
  "includeFullText": true,
  "maxItemsPerFeed": 5
}' |
apify call zerolag/rss-feed-reader --silent --output-dataset

```

## MCP server setup

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

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/pnIOOgqEqHgxkIhCS/builds/aqFwp42CYtrL4iWAI/openapi.json
