# RSS Feed Reader: RSS, Atom & JSON Feed to JSON (`subimpact/rss-feed-reader`) Actor

Read RSS, RDF, Atom and JSON Feed URLs and get one clean JSON row per item: title, link, ISO dates, author, summary, categories, image, enclosures. Only-new mode for scheduled runs, feed discovery and archive history for 350+ news feeds. $0.50 per 1,000 items.

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

## Pricing

from $0.50 / 1,000 feed items

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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: RSS, Atom & JSON Feed to JSON

Read any number of RSS 2.0, RSS 1.0 (RDF), Atom 1.0 and JSON Feed URLs and get one clean JSON row per item.
Every row has the same fields whatever the feed format was, so you can point it at 5 feeds or 500 and load the
dataset straight into a spreadsheet, a database or an AI pipeline.

Three things it does that a plain feed reader does not:

- **Only new items (delta mode).** `onlyNew: true` remembers, per feed, which items it already delivered and
  returns only what arrived since. Built for scheduled runs.
- **Archive history.** `includeArchive: true` adds older items the live feed no longer carries, for the 350+ news
  publisher feeds in our news index. Archive rows are flagged with `fromArchive: true`.
- **Feed discovery.** Give it a normal web page and it follows the feed that page advertises
  (`<link rel="alternate" type="application/rss+xml">`), then reports the discovered URL.

No browser, plain HTTP only, 256 MB of memory. Dates are always ISO 8601 UTC, HTML is stripped from summaries,
and broken charsets (ISO-8859-1, windows-1252) are decoded correctly.

### What one row looks like

| Field | Meaning |
| --- | --- |
| `itemTitle` | Item headline |
| `link` | Item URL, resolved to an absolute URL |
| `guid` | The feed's own identifier (RSS guid, Atom id, JSON Feed id) |
| `publishedAt` | Publication time, ISO 8601 UTC, `null` when the feed has no usable date |
| `updatedAt` | Last update time, ISO 8601 UTC |
| `author` | Byline (`dc:creator`, Atom author, JSON Feed authors) |
| `summary` | Plain-text summary, HTML stripped, max 1,000 characters |
| `contentHtml` | Full content HTML, max 20,000 characters, only with `includeContent: true` |
| `categories` | Categories, tags or labels from the feed |
| `imageUrl` | Lead image from an image enclosure, `media:thumbnail`, `media:content` or the first `<img>` |
| `enclosures` | Media attachments as `{url, mimeType, length}` |
| `feedTitle`, `feedUrl`, `inputUrl` | Feed title, the URL actually read, and the URL you supplied |
| `feedFormat` | `rss2`, `rss1`, `atom` or `jsonfeed` |
| `fromArchive` | `true` when the row came from the archive index |
| `itemKey` | Stable identity used by only-new mode: guid/id, else link, else sha1(title + published) |
| `scrapedAt` | When this run read the item |

A `RUN_SUMMARY` record in the run's key-value store lists every feed with its status (`ok` or `error`), HTTP
status, items found, items delivered, new items, archive items, warnings and the error text. Feeds that fail
(a 404, a timeout, a page that is not a feed) are listed there and logged; they never become dataset rows and are
never charged.

### Example input

```json
{
  "feedUrls": [
    "https://feeds.bbci.co.uk/news/rss.xml",
    "https://hnrss.org/frontpage"
  ],
  "maxItemsPerFeed": 10,
  "maxTotalItems": 50,
  "onlyNew": false,
  "includeArchive": false
}
```

`feedUrls` accepts RSS, Atom and JSON Feed URLs, and also ordinary web pages that advertise a feed. Empty
`feedUrls` reads the BBC News feed, so the actor can be tried in one click. `{}` works too.

Handy extras: `publishedAfter` and `publishedBefore` (ISO dates or datetimes, inclusive), `keywords` (keep items
whose title or summary contains any of them), `includeContent` (full content HTML), `stateStoreName` (which
key-value store keeps the only-new state), `maxConcurrency` (1 to 20 feeds in parallel) and an optional
`proxyConfiguration` (no proxy by default).

### Example output

Real rows from a run over the saved 2026-10-25 BBC News feed (`https://feeds.bbci.co.uk/news/rss.xml`, RSS 2.0):

```json
{
  "itemTitle": "Rosebank: Life on the oil field at the centre of a political storm",
  "link": "https://www.bbc.co.uk/news/articles/cqy8ljkle97ro?at_medium=RSS&at_campaign=rss",
  "guid": "https://www.bbc.co.uk/news/articles/cqy8ljkle97ro#2",
  "publishedAt": "2026-10-25T07:15:38.000Z",
  "updatedAt": null,
  "author": null,
  "summary": "Scotland editor James Cook finds out what life is like on board a ship in an untapped oil field awaiting its fate.",
  "contentHtml": null,
  "categories": [],
  "imageUrl": "https://ichef.bbci.co.uk/ace/standard/240/cpsprodpb/60a5/live/584f1ed0-cfb0-11f1-9012-0172b9f73a72.jpg",
  "enclosures": [],
  "feedTitle": "BBC News",
  "feedUrl": "https://feeds.bbci.co.uk/news/rss.xml",
  "inputUrl": "https://feeds.bbci.co.uk/news/rss.xml",
  "feedFormat": "rss2",
  "fromArchive": false,
  "itemKey": "https://www.bbc.co.uk/news/articles/cqy8ljkle97ro#2",
  "scrapedAt": "2026-10-25T07:44:26.965Z"
}
```

An older item that the live feed had already dropped, delivered from the archive (`includeArchive: true`), from
`https://www.abc.net.au/news/feed/51892/rss.xml`:

```json
{
  "itemTitle": "ATO criticised for 'not going in to bat' for millions of Australians underpaid super",
  "link": "https://www.abc.net.au/news/2026-10-22/australian-workers-criticise-ato-unpaid-superannuation-recovery/107288902",
  "guid": "5f1a3b644e0f236a",
  "publishedAt": "2026-10-21T20:30:23.000Z",
  "author": "Grace Atta",
  "summary": "One in four Australian workers have been underpaid their super. Many of them, along with industry experts, are calling on the tax office to be more proactive in addressing the billions that go unrecovered each year.",
  "categories": [],
  "imageUrl": "https://live-production.wcms.abc-cdn.net.au/ce033f74c9da1461eca359efc2738372?impolicy=wcms_crop_resize&cropH=1077&cropW=1616&xPos=0&yPos=1&width=862&height=575",
  "feedTitle": "Latest business news",
  "feedUrl": "https://www.abc.net.au/news/feed/51892/rss.xml",
  "inputUrl": "https://www.abc.net.au/news/feed/51892/rss.xml",
  "feedFormat": "rss2",
  "fromArchive": true,
  "itemKey": "5f1a3b644e0f236a",
  "scrapedAt": "2026-10-25T07:45:28.185Z"
}
```

### Only new items on a schedule

Set `onlyNew: true` on a scheduled task and each run returns just the items that appeared since the previous
run. The actor keeps one record per feed in a named key-value store in your account (`stateStoreName`, default
`rss-feed-reader-state`): the item keys it delivered (newest first, capped at 2,000 per feed) plus their
canonical links, so an item is still recognised when a feed changes its guids. The record is written only after
that feed's rows have been pushed, so a failed run does not swallow items.

The first run delivers everything the feed currently holds and remembers it. Point a new task at a different
`stateStoreName` if you want independent tracking.

### Archive history

`includeArchive: true` asks our news index for the items it still holds for each feed and merges them with the
live items. Items are de-duplicated by identity, sorted newest first, and still respect `maxItemsPerFeed`,
`maxTotalItems` and the date filters. Archive rows carry `fromArchive: true`, and when the live feed cannot be
read at all (`feedFormat` is then `null`) the archive rows are still delivered with a note in `RUN_SUMMARY`.

The archive covers 350+ news publisher feeds (general news, business, tech and regional publishers). Feeds
outside the index simply return live items plus a short note. If the archive is unreachable or slow, the run
carries on with live items only; the archive never fails a run.

### Pricing

Pay per delivered item: **$0.50 per 1,000 items** ($0.0005 per row), charged only after a row has been pushed.
There is no run start fee beyond Apify's $0.00005 minimum actor start event, and Apify platform usage (compute,
and proxy if you switch it on) is billed by Apify as usual.

A default run over the two prefilled feeds delivers about 56 items and costs about $0.03. The global
`maxTotalItems` cap (default 100) keeps a runaway feed list bounded. Error feeds, empty feeds and filtered-out
items are free.

### How this compares

Apify Store pricing on the Free plan, checked 25 October 2026:

| Actor | Users (30 days) | Price |
| --- | --- | --- |
| This actor (`rss-feed-reader`) | new | $0.50 per 1,000 items, plus Apify's $0.00005 actor start |
| automation-lab/rss-feed-reader | 41 | $0.035 per run start + $1.15 per 1,000 items |
| santamaria-automations/rss-feed-reader | 43 | $0.001 per run start + $2.00 per 1,000 enriched results |
| technicaldost/rss-feed-scraper | 15 | $8.00 per 1,000 items |

Competitor platform usage is extra, see their listings. Compared with the three of them, this actor charges
about half to a sixteenth per item, has no meaningful start fee, and adds only-new mode plus archive history.

### Limits and good to know

- One row per delivered item, so 1,000 items in one run cost $0.50.
- `maxItemsPerFeed` (default 50, max 1,000) and `maxTotalItems` (default 100, max 100,000) are hard caps.
- Requests time out after 25 seconds, follow at most 5 redirects, and are rejected above 15 MB. Network errors,
  429 and 5xx are retried three times with a fresh proxy session; other 4xx responses (a 404 feed) are not retried.
- Dates that a feed does not provide (or that cannot be parsed) become `null` instead of a guess. While a date
  filter is set, undated items are left out because they cannot be proven to be in range.
- Some feeds only publish a truncated list (10 or 20 items). `maxItemsPerFeed` cannot return more than the feed
  itself offers, which is exactly what the archive option helps with.
- RSS 2.0, RSS 1.0 (RDF), Atom 1.0 and JSON Feed 1.x are supported. HTML-only pages without a feed link are
  reported as a feed error.

### Responsible use

Only read feeds you are allowed to read, and keep the request rate reasonable: the actor reads each feed URL a
few times per run at most, and `maxConcurrency` lets you slow it down for small publishers. Respect the
publisher's terms and robots policy, keep attribution (the `author`, `feedTitle` and `link` fields are there for
that) and do not republish full articles: `summary` is plain text and `contentHtml` is only included when you ask
for it, truncated to 20,000 characters.

# Actor input Schema

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

RSS, Atom or JSON Feed URLs. A normal web page also works: the actor follows the feed it advertises (link rel=alternate). Leave empty to read the BBC News feed, so you can try the actor in one click.

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

Newest items kept per feed, after the date and keyword filters (1-1000). A feed's items are always returned newest first.

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

Global cap for the whole run (1-100000). Once it is reached no more rows are pushed. You pay per delivered item, so the default run stays under $0.05.

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

Keep items published on or after this date. ISO date (2026-10-01) or datetime (2026-10-01T09:00:00Z). Items with no date are dropped while a date filter is set.

## `publishedBefore` (type: `string`):

Keep items published on or before this date (a bare date covers the whole day). ISO date or datetime.

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

Keep only items whose title or summary contains ANY of these words, case-insensitive. Leave empty for every item.

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

On: every row also carries contentHtml (content:encoded / Atom content / content_html), truncated to 20,000 characters. Off (default): only the plain-text summary (max 1,000 characters) is returned.

## `onlyNew` (type: `boolean`):

For scheduled runs: remembers, per feed, which items were already delivered. A named key-value store in your account keeps the item keys, and it is written only after a feed's rows are pushed. The first run delivers everything and remembers it.

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

Named key-value store that holds the only-new state (letters, numbers and dashes, 1-63 characters). Use a different name per task if you want independent tracking. Not used when onlyNew is off.

## `includeArchive` (type: `boolean`):

On: also return older items the live feed no longer carries, for feeds covered by our news index (350+ news publisher feeds). Archive rows have fromArchive = true. If the archive is unavailable the run still returns live items and adds a note about it.

## `maxConcurrency` (type: `integer`):

How many feeds are read at the same time (1-20). Lower it for slow or rate-limited servers.

## `proxyConfiguration` (type: `object`):

No proxy by default, which is what most feeds need. Enable Apify Proxy only if a feed blocks your server IP.

## Actor input object example

```json
{
  "feedUrls": [
    "https://feeds.bbci.co.uk/news/rss.xml",
    "https://hnrss.org/frontpage",
    "https://www.jsonfeed.org/feed.json"
  ],
  "maxItemsPerFeed": 50,
  "maxTotalItems": 100,
  "keywords": [
    "election",
    "budget"
  ],
  "includeContent": false,
  "onlyNew": false,
  "stateStoreName": "rss-feed-reader-state",
  "includeArchive": false,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `itemTitle` (type: `string`):

Headline of the feed item

## `link` (type: `string`):

Absolute item URL

## `guid` (type: `string`):

The feed's own item identifier

## `publishedAt` (type: `string`):

Publication time, ISO 8601 UTC

## `updatedAt` (type: `string`):

Last update time, ISO 8601 UTC

## `author` (type: `string`):

Byline from the feed, when present

## `summary` (type: `string`):

Plain-text summary, max 1,000 characters

## `contentHtml` (type: `string`):

Full item content HTML, max 20,000 characters (includeContent on only)

## `categories` (type: `string`):

Categories, tags or labels from the feed

## `imageUrl` (type: `string`):

Lead image URL

## `enclosures` (type: `string`):

Media attachments: url, mimeType, length

## `feedTitle` (type: `string`):

Title of the feed

## `feedUrl` (type: `string`):

URL the feed was read from (after redirects and discovery)

## `inputUrl` (type: `string`):

The URL you supplied for this feed

## `feedFormat` (type: `string`):

rss2, rss1, atom or jsonfeed

## `fromArchive` (type: `string`):

True when the item came from the news archive index

## `itemKey` (type: `string`):

Stable item identity used by only-new mode

## `scrapedAt` (type: `string`):

When this run read the item, ISO 8601 UTC

# 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://feeds.bbci.co.uk/news/rss.xml",
        "https://hnrss.org/frontpage"
    ],
    "maxItemsPerFeed": 50,
    "maxTotalItems": 100,
    "maxConcurrency": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("subimpact/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 = {
    "feedUrls": [
        "https://feeds.bbci.co.uk/news/rss.xml",
        "https://hnrss.org/frontpage",
    ],
    "maxItemsPerFeed": 50,
    "maxTotalItems": 100,
    "maxConcurrency": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("subimpact/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 '{
  "feedUrls": [
    "https://feeds.bbci.co.uk/news/rss.xml",
    "https://hnrss.org/frontpage"
  ],
  "maxItemsPerFeed": 50,
  "maxTotalItems": 100,
  "maxConcurrency": 5
}' |
apify call subimpact/rss-feed-reader --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,subimpact/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/zr7RCastUqeD5s7kQ/builds/nlM5VmS3I87TIZ4CM/openapi.json
