# RSS Feed Monitor – New Entries Since the Last Run (`pontio/rss-feed-monitor`) Actor

Compare up to 20 supplied RSS/Atom feeds across runs. Return newly observed IDs or links after a baseline. Incomplete reads leave history unchanged and are uncharged.

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

## Pricing

from $1.40 / 1,000 feed compareds

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 Monitor – New Entries Since the Last Run

Compare supplied RSS 2.0, RSS 1.0 or Atom feeds across runs. The first successful check establishes a baseline and returns no old entries. Later checks return entries with identities not previously observed by this monitor. No API key is needed.

```json
{
  "urls": ["https://www.nasa.gov/feed/"],
  "monitorName": "news"
}
```

Reuse `monitorName` and the feed URLs on subsequent runs, or in an Apify schedule. A new name starts separate history. Canonical URL repeats (host case, default port and fragments) produce one row/check; different paths or query strings are separate feeds.

### Run it on a schedule

One run is a snapshot. A schedule is what makes it a monitor; the Actor does not create one for you.

1. Fill in `urls` and `monitorName` and save the input as a task.
2. In Apify Console open **Schedules**, click **Create new**, add the task and set the cron expression to `@hourly` (or `@daily`).
3. The first scheduled run takes the baselines and reports `baseline`. Every run after that returns only the entries it has not seen before.

To hear only about what matters, read the run's dataset and keep the rows where `reasonCode` is `new_entries`. Apify's Slack integration posts when a run finishes but cannot see the rows, so on its own it tells you the check ran, not what it found. A Make, n8n or Zapier workflow that starts when the run finishes, fetches the dataset items and filters on that field does.

What it costs at $0.002 per feed check: 20 feeds daily is about $1.20 a month, 20 feeds hourly about $29.

### Output

One row per processed distinct feed with `reasonCode`, `entries`, `newCount`, `observedCount`, `checkedAt`, `previousCheckedAt`, `url`, `finalUrl`, `httpStatus`, `format` and `truncated`. Each returned entry retains the reader's id, title, link, summary, content and dates when present. Summary/content can contain untrusted HTML; sanitize before displaying it.

Identity is the feed's ID with surrounding whitespace trimmed, otherwise its HTTP(S) link resolved against the final feed URL. IDs are not interpreted as URLs. The reader ignores `xml:base`, so relative links in feeds using XML base may resolve incorrectly and miss distinctions between entries; use feeds with stable IDs or absolute links for reliable comparisons. Dates do not determine novelty: newly observed is not necessarily newly published. Reordering, edits under the same identity and disappearing/reappearing identities produce no new entries. Duplicate identities within a response keep the first entry. Entries without an ID or usable link make that check inconclusive.

History stores identity hashes and the last successful check time in a key-value store named `rss-feed-monitor-qaejuflkkomn4ylnw` (the Actor's name and id) in your own Apify account. Failed, incomplete or inconclusive reads do not advance it. Corrupt or inaccessible history is a free `state_error`, not a fresh baseline.

### Billing

One charge per answered feed check, not per entry. Baselines, unchanged checks and successful checks with new entries are charged; failures are not. Processing stops at the run's charge limit. Unprocessed feeds have neither result rows nor history updates.

| reasonCode | Meaning | Charged? |
| --- | --- | --- |
| `baseline` | Initial identities recorded; old entries not emitted | yes |
| `new_entries` | Newly observed identities returned | yes |
| `unchanged` | No unseen identities in this response | yes |
| `invalid_url`, `unsafe_target` | Invalid or nonpublic target | no |
| `blocked`, `http_error`, `unreachable` | Feed could not be read | no |
| `invalid_redirect`, `redirect_loop`, `redirect_limit` | Redirect chain could not be followed safely | no |
| `too_large`, `invalid_xml`, `not_feed` | Response unusable as a bounded feed | no |
| `truncated_feed` | Reader could not retain the whole current feed | no |
| `missing_identity` | An entry has neither ID nor usable link | no |
| `state_limit` | New identities would exceed retained history | no |
| `state_error` | History could not be loaded, validated or saved | no |

### Limits

- Up to 20 feeds/run, sequential checks; ports 80/443, no URL credentials. Public DNS addresses are checked and pinned on each redirect hop.
- Up to 100 entries/current feed, 32 MiB decoded response, 15 seconds/read and five redirects. Truncation is uncharged and leaves state untouched, rather than comparing an incomplete snapshot.
- Up to 10,000 retained identities/feed. No eviction: after the ceiling, checks introducing new identities fail free. Start a new `monitorName` to reset, which deliberately treats the current response as baseline.
- Only entries present in a supplied response can be observed. Items published and removed between checks can be missed. No article crawling, edits/removal alerts, email notifications or publication-completeness claim.
- Do not run the same namespace/feed concurrently: state updates are not locked. History is saved before the dataset row; a crash between these writes can lose a notification. This is not a durable or exactly-once delivery service.

You must have permission to access and reuse each feed's content; public access is not blanket redistribution permission. The example source is NASA's documented RSS feed. Acknowledge NASA for its content; third-party copyright, likeness and endorsement restrictions still apply. NASA does not endorse this Actor.

# Changelog

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

# Actor input Schema

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

History namespace, 1–64 letters, digits, underscores or hyphens. Reuse it across runs; a new name starts a new baseline. Do not run the same namespace/feed concurrently.

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

Up to 20 supplied public RSS/Atom feeds. Canonical URL repeats are checked once. Caller must have feed access and content reuse permission. Baseline emits no old entries; later runs return newly observed identities, not necessarily newly published articles. IDs are whitespace-trimmed; relative links use the final feed URL, ignoring xml:base. Prefer stable IDs or absolute links.

## Actor input object example

```json
{
  "monitorName": "default",
  "urls": [
    "https://www.nasa.gov/feed/"
  ]
}
```

# Actor output Schema

## `results` (type: `string`):

Results produced by this run, in its default dataset.

# 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 = {
    "monitorName": "default",
    "urls": [
        "https://www.nasa.gov/feed/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("pontio/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 = {
    "monitorName": "default",
    "urls": ["https://www.nasa.gov/feed/"],
}

# Run the Actor and wait for it to finish
run = client.actor("pontio/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 '{
  "monitorName": "default",
  "urls": [
    "https://www.nasa.gov/feed/"
  ]
}' |
apify call pontio/rss-feed-monitor --silent --output-dataset

```

## MCP server setup

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