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

Turn RSS, Atom, RDF and JSON feeds (or any website with a feed) into uniform JSON: titles, links, ISO dates, authors, content, enclosures. Since/keyword/dedupe filters and optional full article text. From $0.20 per 1,000 items, no start fee.

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

## Pricing

Pay per event

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

Turn any **RSS feed**, **Atom feed**, **RDF/RSS 1.0** or **JSON Feed** into clean, uniform JSON rows: title, link, guid, ISO publish/update dates, author, categories, summary, full content (HTML and plain text), **enclosures and media** (podcast audio, images) and comments URL. Paste a **website URL** instead and the feed is found automatically from `<link rel="alternate">` or common paths like `/feed`, `/rss.xml`, `/atom.xml`.

Fast and cheap: plain HTTP, no browser, no proxy, **no start fee**. Optional **full article text** for every item.

### What you can do with it

- **News and blog monitoring:** schedule it with *Published since* = `1 day` to get only new posts from dozens of feeds, de-duplicated, in one dataset.
- **Keyword alerts:** keep only items mentioning your brand, product or topic (*Only items mentioning*), drop noise with *Skip items mentioning*.
- **RSS to JSON / CSV / Excel:** one schema for every feed format, so downstream code, Zapier/Make flows, Google Sheets or a database never see format differences.
- **Content and AI pipelines:** turn on *Fetch full article text* to get the readable text of each linked article for summarization, RAG or archiving.
- **Podcast and release tracking:** podcast episode audio URLs (enclosures), GitHub release feeds, YouTube channel feeds, changelogs.

### Input

| Field | What it does |
|---|---|
| `urls` | Feed URLs or websites, one per line. Websites are searched for their feed. |
| `since` | Only items newer than `7 days`, `24 hours`, `2 weeks` or a date (`2026-10-01`). `keepUndated` also keeps items the feed gives no date. |
| `includeKeywords` / `excludeKeywords` | Keep or drop items by words in title, summary, content or categories. |
| `allFeeds` | Read every feed a website advertises, not just the main one. |
| `dedupe` | Skip items already returned in this run (same guid or link). On by default. |
| `fetchArticleText`, `maxArticles` | Add the full readable text of each item's linked page (capped). |
| `maxItemsPerFeed`, `maxItems` | Caps to control cost. |

Example:

```json
{ "urls": ["https://news.ycombinator.com/rss", "wordpress.org/news", "https://www.jsonfeed.org/feed.json"],
  "since": "3 days", "includeKeywords": ["python", "ai"], "maxItems": 200 }
```

### Output

One row per feed item:

```json
{
  "feedTitle": "Python Insider", "feedUrl": "https://blog.python.org/rss.xml", "feedLink": "https://blog.python.org/",
  "feedFormat": "rss", "title": "Python 3.16.0 alpha 1 is now available",
  "link": "https://blog.python.org/2026/10/python-3160-alpha-1-is-now-available/",
  "guid": "https://blog.python.org/2026/10/python-3160-alpha-1-is-now-available/",
  "published": "2026-10-13T00:00:00Z", "updated": null, "author": "Savannah Ostrowski",
  "categories": ["releases"], "summary": "Python 3.16.0a1 is the first of ...",
  "contentHtml": "<p>Python 3.16.0a1 is ...</p>", "contentText": "Python 3.16.0a1 is ...",
  "enclosures": [{"url": "https://.../episode.mp3", "type": "audio/mpeg", "length": 23145678, "source": "enclosure"}],
  "imageUrl": "https://.../cover.jpg", "commentsUrl": "https://.../comments",
  "articleUrl": "https://...", "articleTitle": "...", "articleText": "...", "articleTextLength": 4045,
  "input": "https://blog.python.org/"
}
```

- `published` / `updated` are ISO 8601 in UTC, whatever date format the feed uses.
- `summary` is the plain text of the feed's description/summary (up to 1,000 characters). `contentHtml` is the full content when the feed has it (`content:encoded`, Atom `content`, `content_html`), otherwise the description; `contentText` is its plain text.
- Podcast feeds also fill `durationSeconds`, `episode`, `season`, `episodeType` and `explicit` (iTunes tags); every row carries `feedLanguage`.
- `enclosures` merges RSS enclosures, Atom enclosure links, JSON Feed attachments and Media RSS (`media:content`, `media:thumbnail`).
- `article*` fields appear only with *Fetch full article text* (`articleError` explains a page that could not be read).
- An input that can't be read (no feed found, site down) adds one **uncharged** row `{"input": "...", "error": "..."}`; the other feeds are still processed. A per-input summary (feeds found, item counts, errors) is saved to the key-value store as `SUMMARY`.

### Pricing

Pay per item, with lower prices on higher Apify plans, and **no start fee**:

- `item-parsed`: **$0.50 per 1,000 items** on the free plan, down to **$0.20 per 1,000** on Gold and above.
- `article-fetched` (only with *Fetch full article text*, only when text was extracted): **$2 per 1,000 articles** on the free plan, down to **$0.80 per 1,000** on Gold and above.

Date, keyword, duplicate and count filters all apply before you are charged; error rows are free. A run where no feed can be read, or every feed is empty, fails clearly instead of returning an empty dataset. A filtered run with nothing new succeeds with 0 results.

### Limits and notes

- Reads what a feed publishes: most feeds list only the latest 10 to 50 items, so schedule runs to build history.
- With *Published since*, items without any date are skipped unless *Keep items without a date* is on.
- Full-article extraction is generic (main/article text, no per-site rules) and honors robots.txt; paywalled or JavaScript-only pages return little or no text and are not charged.
- Feeds are fetched politely: inputs on the same host are read one after another, with a declared user agent and timeouts. Feeds up to 20 MB, articles up to 3 MB.
- XML is parsed safely: DOCTYPE and entity declarations are stripped first, so XXE and entity-expansion attacks can't run.
- Public data only. Sites that block non-browser clients (or require login) won't return a feed.

# Actor input Schema

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

One per line. A feed URL (RSS 2.0, Atom, RDF/RSS 1.0, JSON Feed, gzip ok) is read directly. A website or blog URL (`wordpress.org/news`, `https://blog.python.org`) is searched for its feed via `<link rel="alternate">` and common paths like `/feed`, `/rss.xml`, `/atom.xml`, `/feed.json`.

## `since` (type: `string`):

Only items published (or updated) after this: a relative window like `7 days`, `24 hours`, `2 weeks`, or a date like `2026-10-01`. Items without any date are skipped when this is set. Empty = all items in the feed.

## `keepUndated` (type: `boolean`):

With 'Published since' set, also keep items whose feed gives no date (they are skipped by default because their age is unknown).

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

Keep an item only if its title, summary, content or categories contain one of these (case-insensitive). Empty = all.

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

Drop items whose title, summary, content or categories contain any of these.

## `allFeeds` (type: `boolean`):

When a website advertises several feeds (e.g. news sections), read all of them instead of just the main one. Comment feeds are listed last.

## `dedupe` (type: `boolean`):

Skip an item if its guid (or link) was already returned in this run. Useful when feeds overlap.

## `fetchArticleText` (type: `boolean`):

Also open each item's link and extract the page's readable text (`articleText`). Charged as a separate `article-fetched` event, only when text is extracted. Respects robots.txt. Slower: one page request per item.

## `maxArticles` (type: `integer`):

Cap on full-article fetches for the whole run (0 = up to 2,000). Items beyond it are still returned, without `articleText`.

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

Newest-first feeds usually list latest items first. 0 or empty = no limit.

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

Hard cap on results for the whole run (also caps your cost). 0 or empty = no limit.

## Actor input object example

```json
{
  "urls": [
    "https://news.ycombinator.com/rss",
    "https://blog.python.org/"
  ],
  "since": "7 days",
  "keepUndated": false,
  "allFeeds": false,
  "dedupe": true,
  "fetchArticleText": false,
  "maxArticles": 100
}
```

# Actor output Schema

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

One row per feed item, normalized across RSS, Atom, RDF and JSON Feed.

# 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://news.ycombinator.com/rss",
        "https://blog.python.org/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidyharvest/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://news.ycombinator.com/rss",
        "https://blog.python.org/",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("tidyharvest/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://news.ycombinator.com/rss",
    "https://blog.python.org/"
  ]
}' |
apify call tidyharvest/rss-feed-reader --silent --output-dataset

```

## MCP server setup

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