# Bulk RSS Feed & Atom Feed Reader - RSS to JSON (`pennydata/rss-atom-feed-reader-bulk`) Actor

Bulk RSS and Atom feed reader. Paste feed URLs or website URLs (feeds are autodiscovered) and get normalized items: title, link, date, author, summary, categories, image. Supports RSS 2.0, Atom, JSON Feed, since-date filter and per-feed caps. Broken feeds are skipped. $0.30 per 1,000 items.

- **URL**: https://apify.com/pennydata/rss-atom-feed-reader-bulk.md
- **Developed by:** [Penny Data](https://apify.com/pennydata) (community)
- **Categories:** News, Automation, AI
- **Stats:** 2 total users, 1 monthly users, 50.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.21 / 1,000 feed item returneds

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 & Atom Feed Reader: Bulk, JSON Feed, Autodiscover

**RSS feed reader API** for many feeds at once. Paste **RSS 2.0, Atom or JSON Feed** URLs, or just a website URL and the feed is **autodiscovered**, then get one normalized item per post: title, link, ISO date, author, summary, categories, image and enclosure. No login, no browser, no proxies. Pay **$0.0003 per item** ($0.30 per 1,000); failed or empty feeds cost nothing.

Use it for news monitoring, blog and podcast aggregation, content curation, competitor tracking, newsletter research and feeding feeds into Zapier, Make, n8n or your own pipeline via the Apify API.

### Why this feed reader

- **Bulk**: hundreds of feeds in one run, one shared output schema for RSS, Atom and JSON Feed.
- **Autodiscovery**: give `https://example.com` and it reads `<link rel="alternate">` feed links, then tries common paths (`/feed`, `/rss.xml`, `/atom.xml`, `/feed.json`).
- **Resilient**: a dead, blocked or non-feed URL is logged, skipped, listed in the `FAILED_FEEDS` key-value record and not charged. The run still succeeds with the feeds that worked.
- **Per-row status + SUMMARY**: every dataset row has `status` (`ok` feed item, `error` feed unreadable with `reason`, `skipped` feed read but no items matched). Only `ok` rows are charged. A `SUMMARY` record in the key-value store holds the counts per status and `datasetRows`.
- **Since-date filter**: only items published on or after a date.
- **Normalized dates**: always ISO 8601 (or `null`), whatever the feed uses (RFC 822, `dc:date`, Atom `updated`).

### Input

- **urls**: feed URLs or website URLs (aliases `feedUrls`, `feeds`, `url` also work).
- **sinceDate**: optional ISO date or date-time; older items are skipped and not charged. Items with no date are kept.
- **publishedAfter**, **filterByDate**: aliases of `sinceDate`.
- **fetchTimeoutSeconds**: HTTP timeout per feed request (5 to 120, default 25).
- **keywords**: optional list; keep only items whose title or summary contains any keyword (case-insensitive). Others are not charged.
- **includeContent**: add a `content` field with the full item body when the feed has one.
- **dedupe**: skip items whose guid/link already appeared in an earlier feed of the same run.
- **maxItemsPerFeed**: newest items per feed (default 20, max 1000). Also caps your cost per feed.

### Switching from another RSS actor

Paste your saved input as it is and change only the actor ID. These input names work unchanged:

| Rival actor | Their input | Here |
|---|---|---|
| automation-lab/rss-feed-reader | `feeds`, `maxItemsPerFeed`, `publishedAfter` | same names accepted |
| santamaria-automations/rss-feed-reader | `feedUrls`, `maxItemsPerFeed`, `includeContent`, `filterByDate`, `fetchTimeoutSeconds` | same names accepted |

Options this actor does not know are ignored with a warning in the log; the run does not fail. Output field names differ from other actors (see Output above), so check downstream mappings once. Names are from the rivals' public input schemas on 2026-10-22; this is not affiliated with them.

### Output (one item per post)

`feedUrl`, `sourceUrl`, `feedTitle`, `feedFormat` (rss / atom / jsonfeed), `guid`, `title`, `link`, `published`, `updated`, `author`, `summary` (HTML stripped, max 2,000 chars), `contentHtml` (as given in the feed), `categories`, `imageUrl`, `enclosureUrl`, `enclosureType`, `durationSeconds` (podcast episodes), `content` (only with includeContent), `status`.

### Example input

```json
{
  "urls": ["https://hnrss.org/frontpage", "https://github.blog"],
  "sinceDate": "2026-10-01",
  "maxItemsPerFeed": 20
}
```

The default input runs as-is, so you can click **Start** without editing anything.

### Example output

Values are illustrative of the shape, not a real post.

```json
{
  "feedUrl": "https://example.com/feed.xml",
  "sourceUrl": "https://example.com",
  "feedTitle": "Example Blog",
  "feedFormat": "rss",
  "guid": "https://example.com/post-1",
  "title": "Post title",
  "link": "https://example.com/post-1",
  "published": "2026-10-05T10:00:00.000Z",
  "updated": null,
  "author": "Jane Doe",
  "summary": "Plain-text summary of the post.",
  "categories": ["news"],
  "imageUrl": null,
  "enclosureUrl": null
}
```

### FAQ

**Does it support OPML?** Not yet; paste the feed URLs.
**Does it fetch full articles?** No. It reads only the feed document itself (plus the page at the URL you give, for autodiscovery). `contentHtml` is whatever the feed publisher includes.
**Which feed versions?** RSS 0.9x/1.0 (RDF)/2.0, Atom 1.0, JSON Feed 1.x.
**Am I charged for feeds that fail?** No, only items added to the dataset are billed.
**Is this affiliated with any publisher?** No. Feeds are published by sites for syndication; their own terms apply to how you reuse the content.

### Pricing (pay per event)

Event `feed-item`: one item pushed to the dataset, $0.0003 ($0.30 per 1,000). No start fee.

### Honest limits

- Feeds usually expose only the latest 10 to 100 items; this actor cannot return older posts.
- Autodiscovery needs the site to publish a feed link or use a common feed path; sites that block automated requests are skipped.
- Summaries are plain text; formatting is in `contentHtml` when the feed provides it.
- Item text and author names are public data supplied by the publisher; handle them according to the privacy law that applies to you.

### More from PennyData

- [Sitemap Extractor Plus](https://apify.com/pennydata/sitemap-extractor-plus): robots.txt and nested sitemap URL harvester.
- [Website Screenshot & URL to PDF](https://apify.com/pennydata/shot-pdf): full-page screenshots and URL/HTML to PDF.
- [Website Tech Stack Detector](https://apify.com/pennydata/website-tech-stack-detector): CMS, analytics, CDN and frameworks per URL.
- [Website Contact Extractor](https://apify.com/pennydata/website-contact-extractor): emails, phones and socials from sites.
- [Bulk Domain WHOIS & DNS Lookup](https://apify.com/pennydata/bulk-domain-whois-dns-lookup): registrar, expiry, nameservers via RDAP.

### AI disclosure

This actor was written with the assistance of an AI model (Claude, by Anthropic) and tested locally on live feeds.

# Actor input Schema

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

RSS 2.0, Atom or JSON Feed URLs. If a URL is an ordinary web page, the actor looks for a <link rel="alternate"> feed in its HTML (and common paths such as /feed, /rss.xml, /atom.xml) and reads that.

## `sinceDate` (type: `string`):

Optional. ISO date or date-time (e.g. 2026-10-01 or 2026-10-01T12:00:00Z). Items published before it are skipped and not charged. Items without a date are kept.

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

Newest items to return per feed. Also caps what you are charged per feed.

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

Alias of "urls"; both lists are merged and de-duplicated.

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

Optional. Keep only items whose title or summary contains any of these words (case-insensitive). Filtered-out items are not charged.

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

Add a `content` field with the full item body (content:encoded / Atom content) when the feed provides it.

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

Skip items whose guid/link already appeared in an earlier feed of this run.

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

Alias of sinceDate.

## `filterByDate` (type: `string`):

Alias of sinceDate.

## `fetchTimeoutSeconds` (type: `integer`):

HTTP timeout per feed request, 5 to 120.

## Actor input object example

```json
{
  "urls": [
    "https://github.blog/feed/"
  ],
  "maxItemsPerFeed": 5,
  "includeContent": false,
  "dedupe": false,
  "fetchTimeoutSeconds": 25
}
```

# Actor output Schema

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

No description

## `summary` (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://github.blog/feed/"
    ],
    "maxItemsPerFeed": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("pennydata/rss-atom-feed-reader-bulk").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://github.blog/feed/"],
    "maxItemsPerFeed": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("pennydata/rss-atom-feed-reader-bulk").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://github.blog/feed/"
  ],
  "maxItemsPerFeed": 5
}' |
apify call pennydata/rss-atom-feed-reader-bulk --silent --output-dataset

```

## MCP server setup

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

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/yLJbMaSCPkfJxi85x/builds/MY5EGEUi6F3iyo2WV/openapi.json
