# RSS & Atom Delta API (`hisoka8/rss-atom-delta-api`) Actor

Monitor public RSS, Atom, and JSON feeds and emit only new or updated items.

- **URL**: https://apify.com/hisoka8/rss-atom-delta-api.md
- **Developed by:** [Owen Armstrong](https://apify.com/hisoka8) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 feed checkeds

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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

## RSS & Atom Delta API

Detect only **new and updated feed items across Actor runs**. Supply public RSS 2.0, Atom, or JSON Feed 1.1 URLs; the first run creates a silent baseline, and later runs emit only changes.

### Why use it

Most feed readers return the whole feed every time. This Actor keeps bounded per-monitor state, uses conditional HTTP requests, and emits table-ready delta records for automations, agents, and webhooks.

### Input

```json
{
  "feedUrls": [
    "https://hnrss.org/newest"
  ],
  "monitorId": "product-watch",
  "resetState": false,
  "maxFeedBytes": 1000000,
  "maxRedirects": 3,
  "maxStateItems": 5000
}
````

| Field | Type | Default | Limits | Meaning |
|---|---:|---:|---:|---|
| `feedUrls` | string\[] | required | 1–10 | Explicit public HTTP(S) feed URLs. |
| `monitorId` | string | `default` | 1–64 chars | Stable namespace for independent monitors; letters, numbers, `_`, and `-` only. |
| `resetState` | boolean | `false` | — | Delete prior state and silently rebaseline. |
| `maxFeedBytes` | integer | `1000000` | 100 KB–2 MB | Per-feed response-body limit. |
| `maxRedirects` | integer | `3` | 0–5 | Redirect limit; every destination is revalidated. |
| `maxStateItems` | integer | `5000` | 100–10,000 | Retained item hashes per feed. |

Undocumented input fields are rejected.

### Output

The default dataset receives one row per delivered change:

```json
{
  "monitorId": "product-watch",
  "feedUrl": "https://example.com/feed.xml",
  "feedTitle": "Product releases",
  "changeType": "new",
  "itemId": "release-42",
  "itemTitle": "Version 4.2",
  "itemUrl": "https://example.com/releases/42",
  "publishedAt": "2026-07-19T07:00:00Z",
  "updatedAt": null,
  "summary": "Bounded, sanitized text",
  "item": {
    "id": "release-42",
    "title": "Version 4.2",
    "url": "https://example.com/releases/42"
  }
}
```

The default key-value store record `OUTPUT` contains the run summary (`feedsSucceeded`, `feedsFailed`, `changesEmitted`, and secret-safe error types).

### Pay-per-event pricing

| Event | Proposed price | Charged when |
|---|---:|---|
| `feed-checked` | **$0.0020** | A feed was fetched or conditionally confirmed unchanged and successfully processed. |
| `change-detected` | **$0.0002** | A new/updated item was precommitted and submitted through Apify's paid dataset-delivery path. Successful calls report only the SDK-fulfilled count; the at-most-once failure tradeoff below still applies. |
| `apify-actor-start` | **$0.00005** | Synthetic Apify start event; platform-managed, never charged by Actor code. |

Failed fetches, unsafe URLs, oversized bodies, and parse failures are not charged by Actor code. Before paid output, the Actor preflights the remaining event budget and commits exactly the chargeable prefix to state. If state persistence fails, no feed or change event is charged and no dataset row is written. If the run's event budget is exhausted, remaining network work stops; a partially chargeable batch commits and delivers only its chargeable prefix, while the rest remains retryable with older conditional headers.

Paid output uses **at-most-once** semantics, not exactly-once transactions across Apify storage services. If billing or dataset delivery fails after the state precommit, the run fails and that committed prefix is not rolled back or automatically offered again; this prevents duplicate customer charges and duplicate rows on retry but can omit that prefix after a rare platform failure. Use the failed-run signal for operational alerting.

The controlled 20-case benchmark is in [`benchmark-results/ECONOMIC_BENCHMARK.md`](benchmark-results/ECONOMIC_BENCHMARK.md). It demonstrates positive margin potential under the stated model; it is not a claim of cloud payouts or external usage.

### Safety and scope

- Only explicit public `http://` and `https://` feed URLs are accepted.
- DNS results, redirects, and destination IP classes are validated.
- Connections are pinned to a validated address while retaining the original hostname for TLS SNI and certificate checks.
- Private, loopback, link-local, reserved, metadata-service, and otherwise unsafe destinations are rejected.
- The fetcher applies a 60-second monotonic budget across URL validation, redirects, response headers, and chunked body reads. The Actor also stops awaiting the off-loop worker after 61 seconds, so a stuck system resolver cannot block lease renewal or batch termination. Responses, redirects, summaries, feed count, and retained state are also bounded.
- RSS/Atom XML is parsed with `defusedxml`.
- Active HTML is removed from titles and summaries.
- Item/article URLs found inside feed entries are never requested. Retrieval is limited to each explicit feed URL and its separately revalidated HTTP redirects; a feed operator can redirect that feed request to another public HTTP resource, whose body must still pass feed parsing before any state or charge is produced.
- Authentication bypass, CAPTCHA bypass, paywall bypass, and private-data collection are out of scope.
- The feed operator's terms and applicable laws remain the user's responsibility.

### State semantics

State is kept in the named key-value store `rss-atom-delta-state-v1`. A state key is derived from the exact feed URL plus `monitorId`. The first successful run emits no historical items. Only the first `maxStateItems` unique entries in source order are evaluated and retained; overflow entries are ignored rather than repeatedly labeled and billed as new. JSON Feed items without required IDs are discarded rather than assigned unstable identifiers.

On Apify cloud, runs sharing a `monitorId` use the public Request Queue `list_and_lock_head` API before state is read, with a unique client key and 180-second lock renewed every 60 seconds. A second overlapping cloud run for that monitor fails instead of racing; renewal loss cancels the batch, owner-scoped unlock releases a successful lease, and a crashed process relies on platform lock expiry. Different monitor IDs remain independent. Crawlee's local file-backed Request Queue did not serialize separate development containers in verification, so local multi-process overlap is unsupported and must be avoided; authenticated cloud overlap and renewal verification remain publication gates.

The Actor definition pins the default and minimum run memory to **256 MB**, matching the controlled economic benchmark. Callers may choose a larger supported memory allocation; doing so increases compute cost beyond the benchmark model.

### Local verification

```bash
python -m pip install -r requirements.txt
PYTHONPATH=src python -m pytest tests -q
PYTHONPATH=src python benchmarks/run_benchmark.py
docker build -t rss-atom-delta-api:local .
```

Current verified checkpoints are recorded in the mission evidence; rerun them after any code change.

# Actor input Schema

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

One to ten public RSS, Atom, or JSON Feed URLs. HTTP and HTTPS only; private-network destinations and unsafe redirects are rejected.

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

Stable namespace for this monitor. Change it to maintain an independent baseline for the same feed.

## `resetState` (type: `boolean`):

Delete this monitor's retained state and create a new silent baseline.

## `maxFeedBytes` (type: `integer`):

Reject larger response bodies before parsing.

## `maxRedirects` (type: `integer`):

Follow at most this many redirects, revalidating every destination.

## `maxStateItems` (type: `integer`):

Bound the number of item hashes retained for future comparisons.

## Actor input object example

```json
{
  "feedUrls": [
    "https://hnrss.org/newest"
  ],
  "monitorId": "default",
  "resetState": false,
  "maxFeedBytes": 1000000,
  "maxRedirects": 3,
  "maxStateItems": 5000
}
```

# Actor output Schema

## `changes` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("hisoka8/rss-atom-delta-api").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("hisoka8/rss-atom-delta-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call hisoka8/rss-atom-delta-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=hisoka8/rss-atom-delta-api",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "RSS & Atom Delta API",
        "description": "Monitor public RSS, Atom, and JSON feeds and emit only new or updated items.",
        "version": "0.1",
        "x-build-id": "jCcIGRTiCfluix24N"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/hisoka8~rss-atom-delta-api/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-hisoka8-rss-atom-delta-api",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/hisoka8~rss-atom-delta-api/runs": {
            "post": {
                "operationId": "runs-sync-hisoka8-rss-atom-delta-api",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/hisoka8~rss-atom-delta-api/run-sync": {
            "post": {
                "operationId": "run-sync-hisoka8-rss-atom-delta-api",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "feedUrls"
                ],
                "properties": {
                    "feedUrls": {
                        "title": "Public feed URLs",
                        "minItems": 1,
                        "maxItems": 10,
                        "type": "array",
                        "description": "One to ten public RSS, Atom, or JSON Feed URLs. HTTP and HTTPS only; private-network destinations and unsafe redirects are rejected.",
                        "items": {
                            "type": "string",
                            "minLength": 8,
                            "maxLength": 2048
                        },
                        "default": [
                            "https://hnrss.org/newest"
                        ]
                    },
                    "monitorId": {
                        "title": "Monitor ID",
                        "pattern": "^[A-Za-z0-9_-]+$",
                        "minLength": 1,
                        "maxLength": 64,
                        "type": "string",
                        "description": "Stable namespace for this monitor. Change it to maintain an independent baseline for the same feed.",
                        "default": "default"
                    },
                    "resetState": {
                        "title": "Reset baseline",
                        "type": "boolean",
                        "description": "Delete this monitor's retained state and create a new silent baseline.",
                        "default": false
                    },
                    "maxFeedBytes": {
                        "title": "Maximum feed bytes",
                        "minimum": 100000,
                        "maximum": 2000000,
                        "type": "integer",
                        "description": "Reject larger response bodies before parsing.",
                        "default": 1000000
                    },
                    "maxRedirects": {
                        "title": "Maximum redirects",
                        "minimum": 0,
                        "maximum": 5,
                        "type": "integer",
                        "description": "Follow at most this many redirects, revalidating every destination.",
                        "default": 3
                    },
                    "maxStateItems": {
                        "title": "Maximum retained items per feed",
                        "minimum": 100,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Bound the number of item hashes retained for future comparisons.",
                        "default": 5000
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
