# Feed Delta Monitor | RSS, Atom & JSON Feed Changes (`sbio-apps/feed-delta-monitor`) Actor

Monitor public RSS, Atom and JSON feeds for new and edited entries across scheduled runs. Keep persistent history, skip unchanged entries and return per-feed status.

- **URL**: https://apify.com/sbio-apps/feed-delta-monitor.md
- **Developed by:** [Austin Aryain](https://apify.com/sbio-apps) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 successful feed checks

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Feed Delta Monitor

Get only new and edited entries from RSS, Atom and JSON feeds across scheduled runs. Feed Delta keeps the history in your Apify account, so your workflow does not need its own deduplication database.

**Pricing: $0.005 per successful feed check ($5 per 1,000 checks), with platform usage included.** The Actor's pricing panel is authoritative.

### What it does

- Reads public RSS 2.0, RSS 1.0/RDF, Atom and JSON Feed 1/1.1 documents.
- Identifies entries by ID/GUID, then URL, with a content fallback for unidentified entries.
- Detects new entries and revisions; unchanged entries stay out of `changes`.
- Preserves separate histories for each monitor and feed.
- Uses ETag and Last-Modified when the publisher supports them.
- Returns per-feed status, including a successful empty check when nothing changed.

Use it for release-note monitoring, research feeds, news pipelines and ingestion into downstream tools. It reads the supplied feed documents; it does not crawl linked articles or send messages.

### Start a monitor

Create an Apify task with this input, then schedule the task. Keep the monitor ID stable.

```json
{
  "feedUrls": ["https://www.jsonfeed.org/feed.json"],
  "monitorId": "release-monitor",
  "firstRun": "emit",
  "maxChangesPerFeed": 100
}
```

`emit` returns entries already present on the first run. Choose `baseline` to save them silently and return only subsequent additions or edits. A feed added to an existing monitor gets its own first-run baseline. A new monitor ID creates a separate history. Saved tasks may omit the monitor ID and use their task ID instead; direct/API runs must provide one.

Schedule runs at least five minutes apart and **allow only one active run for a given monitor ID**. Wait for a run to finish before retrying it. Use separate monitor IDs for independently scheduled consumers. Exact-once delivery and simultaneous runs against the same monitor are not supported.

### Output and billing unit

Each successful feed check returns **one dataset record** with a `changes` array. Feed checks with zero changes, HTTP 304 checks and silent baseline creation are successful checks and are chargeable. HTTP/parsing failures are listed in the free `OUTPUT` summary and produce no paid dataset record. There is no separate per-article or run-start fee.

```json
{
  "monitorId": "release-monitor",
  "feedUrl": "https://example.com/feed.json",
  "checkedAt": "2026-09-07T12:00:00.000Z",
  "status": "checked",
  "feedTitle": "Example releases",
  "format": "json-feed",
  "changeCount": 1,
  "remainingChanges": 0,
  "changes": [{
    "eventId": "deterministic-event-hash",
    "type": "new",
    "id": "release-1",
    "url": "https://example.com/releases/1",
    "title": "Example release",
    "contentText": "A new release is available.",
    "contentHtml": "",
    "summary": "",
    "publishedAt": null,
    "modifiedAt": null,
    "author": "",
    "tags": [],
    "identity": "entry-identity-hash",
    "identityBasis": "id",
    "revision": "content-revision-hash"
  }]
}
```

The example is synthetic. Actual hashes are 64 hexadecimal characters. To consume individual changes:

```js
const changes = feedChecks.flatMap(check => check.changes);
// Keep eventId in your destination's unique-key field to reject replayed output.
```

Statuses are `checked`, `not_modified`, `baseline_created` and `partial`. `remainingChanges` reports entries held back by the output cap. On the next run, those entries can be returned if they remain in the publisher's feed. The free `OUTPUT` record contains feed errors, checked counts and any time/spending-limit stop. A run with some broken feeds can complete successfully; inspect its summary too.

At this price, 10 feeds checked daily for 30 days cost $1.50 in event fees; 10 feeds checked hourly cost $36. These are usage examples, not a revenue forecast. The minimum run budget is $0.005. Provide enough budget for every feed you want checked: a run stops once it cannot afford another check, so a permanently undersized budget can leave later feeds unchecked. Apify account plans and access to stored datasets after a run are governed by Apify's pricing.

### Limits and delivery behavior

- Maximum 50 feeds per run; sequential requests, 15 seconds per request including redirects, 180-second processing budget checked between feeds. A current request may finish after that budget.
- Maximum 2 MB compressed and decompressed response, 2,000 entries per document and 8 MB normalized output. Maximum 1,000 emitted changes per feed per run; default 100.
- Public HTTP(S), standard ports, no URL credentials, no private-network destinations. No proxies, login, pagination, browser rendering or XML DTD/entity declarations.
- Full feed content is included when present. Treat feed text and HTML as untrusted input; sanitize it before display or execution in another system.
- History retains up to 20,000 identities per feed. Entries absent from the current document expire after 90 days; older identities can also be evicted at the cap. A changed monitor ID, deleted history, retention expiry or eviction can cause entries to be emitted again.
- If a publisher omits both an entry ID and URL, content is its identity. Editing that entry can appear as a new entry. Changes to fields outside the normalized output are not tracked. A feed that reuses one ID for conflicting entries is rejected.
- Feeds are snapshots. Items can disappear between checks; polling cannot recover entries that are no longer present. Absence is not reported as deletion.
- Delivery is at least once after interruptions: output becomes available before state advances. If a write, charge or checkpoint fails ambiguously, retrying can replay changes and incur another successful-check fee. `eventId` is stable across a replay of the same state transition. There is no cross-service atomic transaction.
- Persistent history is documented for repeated runs by the same Apify account. Accountless wallet/x402 persistence has not been verified and is not advertised.

### Data and support

The Actor stores output in the caller's default dataset, the run summary in its default key-value store, and a named key-value store containing hashed identities, revision counters, timestamps, feed titles and HTTP validators. It does not send data to a separate developer server or analytics service. Apify manages platform storage; the developer's platform access is governed by Apify permissions. Delete the named store to reset a monitor. No authentication tokens belong in feed URLs.

Report reproducible problems through the Actor's Apify Issues tab. Include the run ID and a public example feed; never post private credentials.

### Local development

Requires Node.js 22 or later.

```sh
npm ci --ignore-scripts
npm test
npm run demo
```

The demo fetches two public feeds twice and writes counts to `docs/live-demo-results.json`. It stores a local baseline under the ignored `storage` directory. It does not charge anyone or publish anything. `npm start` uses the Apify SDK; supply `storage/key_value_stores/default/INPUT.json` for local runs.

Deployment, pricing gates, maintenance and evidence requirements are recorded in `docs/LAUNCH.md`.

# Actor input Schema

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

1–50 RSS, Atom or JSON Feed URLs. Standard HTTP(S) ports only; no login, private network or full-article scraping.

## `monitorId` (type: `string`):

Stable name for this monitor, such as product-news. Reuse it to preserve history. Omit only when using a saved task: its task ID becomes the monitor ID. Use distinct IDs for independent consumers.

## `firstRun` (type: `string`):

Emit sends existing entries on the first run. Baseline saves current entries silently and sends future changes. Applies separately to each feed without saved history.

## `maxChangesPerFeed` (type: `integer`):

Limits the changes returned in each feed-check record. Remaining entries can be returned next run if they are still present in the publisher's feed.

## Actor input object example

```json
{
  "feedUrls": [
    "https://www.jsonfeed.org/feed.json"
  ],
  "monitorId": "release-monitor",
  "firstRun": "emit",
  "maxChangesPerFeed": 100
}
```

# Actor output Schema

## `checks` (type: `string`):

One record per successful feed check. Each record includes status, changeCount, remainingChanges and a changes array. A successful check with no changes still produces a paid record.

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

Free run summary, including feed errors, unprocessed feeds and any spending/time limit stop.

# 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://www.jsonfeed.org/feed.json"
    ],
    "monitorId": "release-monitor"
};

// Run the Actor and wait for it to finish
const run = await client.actor("sbio-apps/feed-delta-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 = {
    "feedUrls": ["https://www.jsonfeed.org/feed.json"],
    "monitorId": "release-monitor",
}

# Run the Actor and wait for it to finish
run = client.actor("sbio-apps/feed-delta-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 '{
  "feedUrls": [
    "https://www.jsonfeed.org/feed.json"
  ],
  "monitorId": "release-monitor"
}' |
apify call sbio-apps/feed-delta-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sbio-apps/feed-delta-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/Qhw76Awte87iE2ruK/builds/HX9nZPrlsSRHXaPGv/openapi.json
