# RSS Feed Scraper and New Articles Monitor (`zinin/rss-feed-scraper-monitor`) Actor

Extract RSS and Atom feed items with stable version hashes, publication dates and safe summaries, or monitor new and edited items in a persistent Apify store.

- **URL**: https://apify.com/zinin/rss-feed-scraper-monitor.md
- **Developed by:** [Tim Zinin](https://apify.com/zinin) (community)
- **Categories:**
- **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.

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

## RSS Feed Scraper and New Articles Monitor

Extract RSS and Atom feed items with stable version hashes, publication dates and safe summaries, or monitor new and edited items using a selected persistent Apify store.

**Evidence status (2026-09-05).** The actor was built on the Apify platform (build jbz3sLcXhlJh3Kp69), its pay-per-event tariff was read back from the platform, and six private acceptance runs on Apify infrastructure produced the rows shown in this document: a fixture snapshot, an empty channel, a capped snapshot, a live NASA feed, a first monitor pass and a repeated monitor pass with no changes. An independent review preceded the build.

### R1. What you get

The actor reads permitted RSS2.0 and Atom1.0 XML feeds and returns normalized item records. A snapshot returns the feed’s unique eligible items each time. Monitor compares stable item-version keys against a selected persistent store, returning new or edited versions and a free no\_changes result when nothing eligible changed.

It does not download linked articles, enclosures or images. The summary is the feed’s description or Atom summary converted to plain text. Public RSS 2.0 and Atom 1.0 feeds supplied by the buyer are supported. The initial prefill is an owned synthetic KVS feed with exactly three records.

### R2. Who uses it

Editors can use monitor output as a review queue for newly announced items. Automation builders can connect snapshot output to a catalog import. Analysts can retain item identity, content hash and source dates for reproducible comparisons. The actor supplies a bounded extraction and change signal; it does not rank news importance, verify reporting accuracy or publish notifications.

### R3. How to run

Begin in snapshot mode using the prefill. After the fixture files have been uploaded and their hashes verified, the run should show wave-a-001, wave-a-002 and wave-a-003. Switch to monitor only after selecting a persistent store through the input picker. Run it twice with the same store: first delivery records versions, and the second unchanged run produces no\_changes.

Start with one source and a small result limit. Run it once, inspect both the dataset and the OUTPUT summary, and save the input next to the run ID. This gives your team a reproducible baseline before a recurring schedule starts. A successful platform status means execution finished; it does not by itself establish that a useful result was obtained. Check found, resultCount, partial and error in the actual rows.

Use the dataset preview to inspect nested values before exporting to a spreadsheet. JSON preserves arrays, nulls and booleans most faithfully. CSV is a presentation format: arrays may appear as serialized values and spreadsheet applications can reinterpret strings. Download a JSON copy when you need an audit record. Keep the source URL and checkedAt value attached to every derived row so a colleague can identify the observation behind a decision.

After that first inspection, connect the run to an existing Apify schedule or your own workflow. Choose a frequency that matches how often the source changes. Scheduling faster than the publisher updates usually adds cost without adding evidence. A schedule is a series of independent runs, each with its own input, resource limits and start event. The actor does not create schedules, send messages or modify the source on your behalf.

Before handing the workflow to someone else, document the expected paid unit, the chosen caps and the action for an empty or partial response. Give the receiving team one successful dataset example and one free failure example from your accepted runs. Do not teach an integration to treat every dataset row as a billable result: explanatory rows deliberately share the same dataset so they remain visible during export.

### R4. Pricing

An edited item is a new billable version only when its normalized content hash changes and that exact feed+guid+hash version is absent from retained history. An unchanged item, duplicate within one XML document or304 response does not produce another result event. Snapshot intentionally bills the items it returns again on later runs.

The price is **$0.005 per start + $0.0005 per item**, read back from the platform tariff on 2026-09-05 (`apify-actor-start` $0.005, `result-found` $0.0005, model PAY\_PER\_EVENT). The primary paid unit is one complete unique feed item version delivered in snapshot, or one new or edited version delivered in monitor. Unchanged,304,empty,invalid and incomplete notices have no result event. The start event is charged once per GB of run memory: the default 256 MB run pays one start unit, while the acceptance runs below were started with 4096 MB and therefore show `apify-actor-start: 4`. Keep the default memory unless a feed needs more.

| Completed paid units | Proposed total including one start |
|---|---|
| 0 | $0.0050 |
| 1 | $0.0055 |
| 5 | $0.0075 |
| 20 | $0.0150 |
| 100 | $0.0550 |

These are arithmetic examples at the tariff above, not receipts. Input and response limits can require multiple runs; each additional run has its own start. Free result rows do not refund a start event that has already occurred.

Billing follows delivery of the documented unit. One completed item is written with the named result-found event in the same SDK call. An incomplete unit or diagnostic notice is written without that event. The distinction is visible in found and resultCount; the OUTPUT summary separately reports delivered, paid and free. A dataset can therefore contain more rows than the number of paid results. Count result events when reconciling invoices.

The actor reads the actual run pricing before source work. On the platform, a missing result price, unreadable pricing or a priced automatic dataset-item event stops the run. If writing an explanation might itself cost money, the explanation stays in the log and failed run status. The start event may already have been charged. A failed run is not a promise that every previous activity was free, so reconcile the recorded event counts rather than assuming a refund.

A budget check runs inside the same lock as the paid write. Concurrent delivery attempts cannot both reserve the same last available amount. The check compares remaining money with the next row price before calling the SDK. This guards against the SDK behavior that can otherwise permit one final row beyond the limit. A budget just below one complete unit produces zero additional paid rows; it is not rounded up to buy a unit the budget does not cover.

If the SDK says the last paid row reached the event limit, that row still counts as delivered and paid. Only subsequent work stops. If delivery or charging throws, the outcome can be uncertain: a dataset write may have succeeded before a charge response failed. The actor stops and marks the run failed instead of automatically retrying the row. Reconcile the dataset and event counters before rerunning an uncertain delivery, because a new run can repeat previously obtained data.

Platform evidence for the tariff above (charged event counts of the accepted runs): snapshot of the fixture feed GxXrxAuskWnYjGwwx `result-found: 3`; empty channel Wh7jJau9HPJmBEeY5 `0`; snapshot capped at one item 4tZgXUB7SUosfg0mm `1`; NASA feed capped at three u5rVFaZg055gpZZMk `3`; first monitor pass 4tu1veoFaWm1YsPga `3`; repeated monitor pass on the same store DEN4djUgnIDgiv4ab `0`. Platform account subscriptions, prepaid credits and discounts are governed by the account's current terms.

### R5. Input contract

Monitor requires stateStoreId. There is no fallback to an owner-global store, a run-default ephemeral store or another customer’s history. The input uses the platform store picker, so limited permissions follow the selected resource. Snapshot ignores a supplied store ID and never opens it. OUTPUT summary storage is separate from monitor state.

```json
{
  "feedUrls": [
    "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml"
  ],
  "mode": "snapshot",
  "maxItemsPerFeed": 20
}
```

#### feedUrls

Supported sources are public HTTP(S) RSS 2.0 and Atom 1.0 feeds supplied by the buyer for syndication processing. The buyer is responsible for authorization and downstream use. URLs cannot contain credentials or fragments. Every redirect is checked by the same URL safety and DNS-pinned SSRF policy, and its final URL is recorded. Robots rules, 401/403 restrictions and the one-request-per-second host limit are respected; no access control is bypassed.

Type: `array`. Default/prefill: `["https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml"]`. Declared constraints: `{"maxItems": 10}`. The runtime validates this field before starting source work; invalid values produce a failed run through the free log/status channel. Numeric strings and truthy strings are not silently accepted as numbers or booleans.

#### mode

Snapshot returns unique current items without reading or writing monitor state. Monitor requires a selected persistent KVS and emits versions not present in its bounded history.

Type: `string`. Default/prefill: `"snapshot"`. Declared constraints: `{"enum": ["snapshot", "monitor"]}`. The runtime validates this field before starting source work; invalid values produce a failed run through the free log/status channel. Numeric strings and truthy strings are not silently accepted as numbers or booleans.

#### maxItemsPerFeed

Maximum complete eligible item versions delivered per feed. In monitor, already-seen versions are skipped before this cap, so subsequent capped runs can progress through remaining items.

Type: `integer`. Default/prefill: `20`. Declared constraints: `{"minimum": 1, "maximum": 100}`. The runtime validates this field before starting source work; invalid values produce a failed run through the free log/status channel. Numeric strings and truthy strings are not silently accepted as numbers or booleans.

#### stateStoreId

Select a persistent store you control with the store picker. Required for monitor; ignored in snapshot. Use a dedicated store and serialize runs using the same checkpoint.

Type: `string`. Selected explicitly in monitor mode; no owner-global store is substituted. Declared constraints: `{"maxLength": 64}`. The runtime validates this field before starting source work; invalid values produce a failed run through the free log/status channel. Numeric strings and truthy strings are not silently accepted as numbers or booleans.

Send one JSON object as input. Arrays are bounded before work starts, and the implementation rejects unknown input fields rather than silently ignoring a misspelled option. Use JSON booleans true and false, not the strings "true" or "false". Use integer values for limits. Limits are ceilings, not promises that the source contains that many useful records. A valid request can return fewer records or a free status row.

Supported sources are public HTTP(S) RSS 2.0 and Atom 1.0 feeds supplied by the buyer for syndication processing. The buyer is responsible for authorization and downstream use. URLs cannot contain credentials or fragments. Every redirect is checked by the same URL safety and DNS-pinned SSRF policy, and its final URL is recorded. Robots rules, 401/403 restrictions and the one-request-per-second host limit are respected; no access control is bypassed.

The caps work together. Increasing a per-source limit does not override the run deadline, byte limit, maximum number of input sources or output-size limits. Split a large job into bounded runs and record which inputs belong to each batch. If you change a limit between runs, preserve that change in your evidence log: two outputs collected under different truncation settings are not directly comparable without checking partial flags.

Defaults are intended to make a small first run inspectable. They are not a production source recommendation or a guarantee of permanent availability. A source owner can move content, change markup or temporarily reject requests. Keep your selected production input in a saved task after acceptance so an updated default does not silently change what your schedule monitors. Recheck permissions and source scope whenever the production URL changes.

### R6. Output examples and result states

A three-item snapshot emits three complete rows. A valid channel without item emits exact not\_found/false/empty-error/zero. A repeated monitor run emits exact no\_changes/false/empty-error/zero, whether detected from matching hashes in a200 response or from a valid304 conditional response.

With maxItemsPerFeed=1, the first complete item is delivered and a free partial notice follows. The unprocessed versions are not checkpointed. Later monitor runs skip already-delivered versions before applying the cap, so they can progress to the second and third records. Malformed XML yields source\_error, never clean absence.

A row with found=true and resultCount=1 represents the actor's documented completed unit. A row with found=false and resultCount=0 needs its status interpreted. Clean absence has an empty error string; transport, parsing and delivery problems carry a nonempty error or fatal run summary. Do not collapse these cases into a single empty-table branch. They answer different operational questions and require different follow-up actions.

The partial flag describes completeness under the selected input and caps. It can appear on a free notice after complete units have already been delivered, or on an incomplete unit that is not billable. The per-product rules in this section tell you which case applies. A downstream workflow should preserve the flag even when it keeps the useful data. Removing the warning during CSV flattening can turn an honest limited observation into an unsupported claim of completeness.

The default key-value store record OUTPUT is the run summary. Requested describes the input scope; processed describes units or inputs examined according to this actor's documented counter; unprocessed identifies remaining work. Delivered and paid describe successful result delivery, free describes explanatory rows, failed counts delivered error notices, and deliveryUncertain identifies writes that require reconciliation. These counts are not interchangeable with HTTP request count.

The examples below are dataset rows from private acceptance runs executed on Apify infrastructure on 2026-09-05 with the deployed build and the live tariff. They are unedited; where a value is shortened for display the marker is inside the value.

#### Accepted platform run examples (2026-09-05)

Example A is a paid snapshot item from run GxXrxAuskWnYjGwwx (fixture feed). Example B is the free empty-channel row from run Wh7jJau9HPJmBEeY5. Example C is the free partial notice from run 4tZgXUB7SUosfg0mm (maxItemsPerFeed=1, one item paid before it). Example D is a paid item from the live NASA feed, run u5rVFaZg055gpZZMk. Example E is a paid `new` version from the first monitor pass 4tu1veoFaWm1YsPga; Example F is the free `no_changes` row from the repeated monitor pass DEN4djUgnIDgiv4ab on the same store.

Example A — snapshot item, paid (`result-found` counted once).

```json
{
  "input": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "sourceUrl": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "found": true,
  "status": "ok",
  "resultCount": 1,
  "partial": false,
  "error": "",
  "warnings": [],
  "checkedAt": "2026-09-05T13:01:29.625Z",
  "schemaVersion": "1.0",
  "feedUrl": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "guid": "wave-a-001",
  "title": "Fixture article one",
  "link": "https://example.com/wave-a/one",
  "publishedAt": "2026-09-01T09:00:00.000Z",
  "updatedAt": null,
  "publishedRaw": "Tue, 01 Sep 2026 09:00:00 GMT",
  "updatedRaw": null,
  "contentHash": "ad49f5671282b6c5f888c522e82de84a763cc71b95e42c40f5045e6acd51e9b9",
  "changeType": "snapshot",
  "summary": "First synthetic entry for deterministic snapshot and monitor checks.",
  "identity": "a5765bfed852150db01ae8d19264cc4e2e04afe1e5cdd6c3a1ea7b3135e1532d",
  "recordKey": "25738fd5e0ac039808775f26b4187b368de2fb61e7af74d1f1138d52281211e6",
  "syntheticFixture": true,
  "finalUrl": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "redirectChain": []
}
```

Example B — valid channel without items, free.

```json
{
  "input": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed-empty.xml",
  "sourceUrl": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed-empty.xml",
  "found": false,
  "status": "not_found",
  "resultCount": 0,
  "partial": false,
  "error": "",
  "warnings": [],
  "checkedAt": "2026-09-05T13:01:35.045Z",
  "schemaVersion": "1.0",
  "feedUrl": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed-empty.xml",
  "httpStatus": 200,
  "syntheticFixture": true
}
```

Example C — cap reached, free partial notice after one paid item.

```json
{
  "input": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "sourceUrl": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "found": false,
  "status": "partial",
  "resultCount": 0,
  "partial": true,
  "error": "",
  "warnings": [
    "Only complete delivered items were checkpointed; no feed validator saved while items remain incomplete."
  ],
  "checkedAt": "2026-09-05T13:01:40.207Z",
  "schemaVersion": "1.0",
  "feedUrl": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml"
}
```

Example D — live public feed item (NASA), paid.

```json
{
  "input": "https://www.nasa.gov/feed/",
  "sourceUrl": "https://www.nasa.gov/feed/",
  "found": true,
  "status": "ok",
  "resultCount": 1,
  "partial": false,
  "error": "",
  "warnings": [],
  "checkedAt": "2026-09-05T13:01:44.779Z",
  "schemaVersion": "1.0",
  "feedUrl": "https://www.nasa.gov/feed/",
  "guid": "https://www.nasa.gov/?post_type=press-release&p=1044835",
  "title": "NASA Ames Fire Department Aircraft Firefighting Training",
  "link": "https://www.nasa.gov/news-release/nasa-ames-fire-department-aircraft-firefighting-training/",
  "publishedAt": "2026-09-04T19:51:56.000Z",
  "updatedAt": null,
  "publishedRaw": "Fri, 04 Sep 2026 19:51:56 +0000",
  "updatedRaw": null,
  "contentHash": "6c5d2c473e9c98c1e1cf06531d5da8db48af9b977391f6dee15f5be84e67c1da",
  "changeType": "snapshot",
  "summary": "The fire department at NASA’s Ames Research Center in California’s Silicon Valley will perform training on the Moffett Federal Airfield beginning Tuesday, Sept. 8 through Friday, Sept. 11. The training will involve the use of a propane-fueled aircraft fire simulator and be conducted from 8 a.m. to 8 p.m. PDT. Because the aircraft simulator is […]",
  "identity": "c0f45ba832a59083ae1d56dc12028a7d8b8c8196cf51ddad879118bebdd9d90c",
  "recordKey": "2c1e268095f3a8a10f42af80dc2e67ebe3df310263cfad01f7baf81c74012c74",
  "syntheticFixture": false,
  "finalUrl": "https://www.nasa.gov/feed/",
  "redirectChain": []
}
```

Example E — monitor, first pass: a new version, paid.

```json
{
  "input": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "sourceUrl": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "found": true,
  "status": "ok",
  "resultCount": 1,
  "partial": false,
  "error": "",
  "warnings": [],
  "checkedAt": "2026-09-05T13:01:50.330Z",
  "schemaVersion": "1.0",
  "feedUrl": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "guid": "wave-a-001",
  "title": "Fixture article one",
  "link": "https://example.com/wave-a/one",
  "publishedAt": "2026-09-01T09:00:00.000Z",
  "updatedAt": null,
  "publishedRaw": "Tue, 01 Sep 2026 09:00:00 GMT",
  "updatedRaw": null,
  "contentHash": "ad49f5671282b6c5f888c522e82de84a763cc71b95e42c40f5045e6acd51e9b9",
  "changeType": "new",
  "summary": "First synthetic entry for deterministic snapshot and monitor checks.",
  "identity": "a5765bfed852150db01ae8d19264cc4e2e04afe1e5cdd6c3a1ea7b3135e1532d",
  "recordKey": "25738fd5e0ac039808775f26b4187b368de2fb61e7af74d1f1138d52281211e6",
  "syntheticFixture": true,
  "finalUrl": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "redirectChain": []
}
```

Example F — monitor, repeated pass on the same store: no changes, free.

```json
{
  "input": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "sourceUrl": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "found": false,
  "status": "no_changes",
  "resultCount": 0,
  "partial": false,
  "error": "",
  "warnings": [],
  "checkedAt": "2026-09-05T13:01:54.238Z",
  "schemaVersion": "1.0",
  "feedUrl": "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml",
  "httpStatus": 200,
  "syntheticFixture": true
}
```

### R7. Field dictionary

#### `input`

Normalized requested feed URL. All rows from one feed retain this input even if an item link points to another website. The actor fetches the feed, not that linked article.

#### `sourceUrl`

The requested feed URL used for provenance and version identity. Redirects follow the general public-URL safety policy; the final URL is recorded and do not silently change your input identity.

#### `feedUrl`

Feed identity namespace. The same GUID from two different feed URLs produces different identities and record keys. If you intentionally move a feed URL, review the state behavior before replacing a scheduled input.

#### `guid`

RSS GUID, Atom entry ID, or item link when no GUID/ID exists. It must be nonempty and <=2048characters. The actor does not invent an identity from row position or title.

#### `title`

Whitespace-normalized item title, bounded to1000characters. A missing title is permitted if a summary supplies useful content. The title is source data, not an instruction to the actor or your downstream workflow.

#### `link`

Absolute HTTP(S) article link when supplied, otherwise null for an item with another stable identity. Relative links resolve against the feed URL. No article, image or enclosure request is made.

#### `publishedAt`

Parsed publication timestamp in UTCISO8601, or null if absent or unparseable. This is source publication evidence and must not be replaced by checkedAt when calculating content age.

#### `updatedAt`

Parsed RSS extension or Atom updated timestamp when available. Null is preserved if missing. A changed contentHash can identify an edited projection even when the publisher does not supply an updated timestamp.

#### `publishedRaw`

Unmodified source publication-date text or null. Keep it when auditing how a publisher represented the date, especially if the normalized date is null with a warning.

#### `updatedRaw`

Unmodified source update-date text or null. This value participates in content hashing so a source date change remains an observable version change.

#### `contentHash`

SHA256 of the normalized title,link,raw publication date,raw update date and plain summary in fixed order. Feed boilerplate changes and formatting changes discarded during summary normalization do not create item versions.

#### `identity`

SHA256 of the feed URL and GUID/ID/link identity serialized as a JSONarray. This is the stable item identity across content versions. It is useful as a downstream parent key.

#### `recordKey`

SHA256 of feed URL,GUID and contentHash as a JSONarray. This identifies a specific version for deduplication. It is not an Apify record URL and does not grant access to the selected store.

#### `changeType`

snapshot in snapshot mode, new for an unseen identity in monitor, updated when the identity exists in bounded retained history but this version key is unseen. Evicted history can cause an older item to appear new again.

#### `summary`

Plain normalized description/summary, bounded to20000characters. HTML scripts and styles are removed; the actor exports text and does not execute feed HTML. Full article bodies and enclosure content are outside this contract.

#### `found`

True for a complete eligible item version. False for unchanged or empty feeds, errors and partial notices. A feed with no new monitor items still returns a free explanatory row.

#### `status`

ok for delivered item versions, no\_changes for an unchanged monitor response or deduplicated feed, not\_found for a valid empty feed, source\_error for HTTP/XML/item problems, partial for incomplete eligible-item delivery.

#### `resultCount`

One per completed item version, zero per explanatory row. A feed containing three completed eligible items creates three result events, not one event for the whole XML file.

#### `partial`

True on the free notice when eligible versions remain because of caps, invalid item fields or delivery interruption. Completed item rows remain complete and can already have been paid and checkpointed.

#### `error`

Empty for clean no\_changes, valid empty channel and ordinary limit notices. Nonempty for HTTP failures, unsolicited304, malformed or prohibited XML and invalid item contracts. Do not interpret XML failure as an empty feed.

#### `warnings`

Array of source-date parsing and completeness caveats. An invalid source date is retained in raw form and does not become a fabricated current timestamp.

#### `checkedAt`

UTC observation time for this row. It is independent of the source dates, feed HTTP validators and persistent checkpoint timestamps. Preserve those distinct clocks when measuring publication age or monitor latency.

#### `syntheticFixture`

True for item and clean-status rows from the owned Wave A feed prefix. The bundled RSS content is intentionally synthetic and immutable; it is not a news feed or a real commercial canary.

#### `schemaVersion`

Version1.0 of the row contract. Store this alongside records if consumers depend on the hash projection, changeType semantics or date normalization.

### R8. Evidence and boundaries

The owned fixture feed has explicit GUID,pubDate,link,title and description on each of its three entries. Dates are fixed September2026 test values, not evidence of today’s news. The immutable feed XML and MANIFEST.sha256 define the regression input.

DTD declarations are rejected before parsing, including internal entity declarations. The SAX parser has no network entity resolver and no custom entity definitions are installed. Only predefined and numeric XML entities are accepted. A document that attempts an external DTD or entity is refused before any such resource can be requested.

Every observation is tied to the response actually obtained. checkedAt is the actor's collection time; it does not automatically mean that the source data was published or updated at that time. Cached pages and feeds can be older than their HTTP response time. Source dates, revisions and validators should remain separate fields in your downstream model. If a publisher omits a field, absence is preserved rather than replaced with a plausible value.

The HTTP transport validates the URL before connection, checks every DNS answer, rejects private and loopback addresses and pins the approved address to the connection. Redirects are checked again, including redirects that appear to remain on the same host. These safeguards apply to robots requests as well as content requests. A redirect into a forbidden host or route stops that request; the actor does not follow it because the first URL looked acceptable.

Responses are bounded in bytes and time. At most five redirects are followed per attempt. Network failures, 429 and server errors can receive at most two retries with bounded backoff; Retry-After is considered within the remaining deadline. Ordinary client errors and invalid successful-response formats are not retried as if waiting would repair them. A timeout describes what the actor observed; it does not prove that the source is permanently unavailable.

Supported sources are public HTTP(S) RSS 2.0 and Atom 1.0 feeds supplied by the buyer for syndication processing. The buyer is responsible for authorization and downstream use. URLs cannot contain credentials or fragments. Every redirect is checked by the same URL safety and DNS-pinned SSRF policy, and its final URL is recorded. Robots rules, 401/403 restrictions and the one-request-per-second host limit are respected; no access control is bypassed.

Exported text remains untrusted source data. The parser does not execute scripts or follow instructions embedded in content. If you later send the text to an AI workflow, place it in a data field and give that workflow its own instruction boundary. Do not let a publisher-controlled title, description or table cell choose credentials, network destinations or workflow permissions.

### R9. Decision routing

Route new and updated versions to separate editorial branches if your team needs to distinguish an announcement from a correction. Route no\_changes to an ordinary successful polling record; it should not overwrite previous items with empty content. Route XML and HTTP failures to source health. If a run is partial, retain the complete items and schedule a controlled continuation.

Create explicit routing branches for complete results, clean absence, partial observations, source errors and fatal delivery problems. The normal success branch should require the documented completed-unit fields, not merely a nonempty dataset. The absence branch can record that no matching content was observed. The partial branch should retain useful complete units while opening a review item for the missing scope. The error branch should avoid overwriting the last good source record with an empty value.

Use a small retry policy in the surrounding workflow. The actor already retries suitable transient HTTP failures. Immediately restarting the entire run several times can multiply start charges and repeat already delivered units. A delayed retry with the same saved input is easier to reconcile. If the run reports delivery uncertainty, compare the dataset and result event counts first; an automatic full retry is especially inappropriate when the previous write may have landed.

Keep decisions proportional to evidence. A machine-readable status is suitable for triage, a review queue or a candidate change list. It is not a substitute for inspecting the underlying source before a consequential decision. Attach the source URL, collection time and relevant evidence fields to any ticket or report your workflow creates. That lets an operator verify the observation without rerunning the entire extraction.

When the result changes between runs, compare the same source identity and the same input limits. A different cap, redirect destination or source revision can explain the difference. Do not label a missing row as a deletion until you have ruled out partial output, a changed source scope and a failed request. Preserve the last successful observation alongside the latest attempt so outages do not look like intentional content removal.

### R10. Commercial playbooks

For a feed-to-catalog import, use identity as the item key and recordKey as a version key. Store publishedAt,updatedAt and checkedAt separately. Keep the latest accepted version in the application view while retaining older versions for audit. Do not use the title as a stable database key.

For a newsroom queue, attach the feed URL and item link to each candidate. Show source publication age and the actor observation time. A repeated GUID with edited summary is a candidate update, not automatically a correction requiring publication; your editor makes that decision.

For a recurring operational report, start by defining the question the report must answer and the smallest source set that can answer it. Store each run's input, summary and result dataset together. Produce a compact review view for colleagues, while retaining the original JSON for audit. This creates a traceable chain from a source observation to a business action without pretending that extraction alone proves the action was correct.

For a client deliverable, agree on the unit, source scope and completeness rules before collection. Explain whether a capped response is acceptable and who reviews failures. Quote the proposed extraction cost separately from your own analysis and service fee. A small technical result can still require editorial or operational judgment, and the actor does not promise revenue, savings or a particular conversion rate from that judgment.

For a migration or recurring comparison, freeze the input configuration for the comparison period. Assign a batch identifier in your own workflow and save a baseline dataset. Run the same source scope again after the change, then compare stable identities and evidence rather than dataset row positions. Row order is useful for inspection but is generally a poor business key. Mark differences whose underlying run was partial as requiring review.

For an internal acceptance checklist, have someone other than the builder inspect a successful result and a deliberate negative. Verify source identity, the paid-unit count and how the integration handles a free notice. A positive demo alone does not exercise the branches most likely to cause misleading dashboards. Keep this check small enough to repeat when the actor version, source markup or saved input changes.

### R11. Integration recipes

Serialize scheduled monitor runs that use the same selected store. There is no distributed lease or compare-and-swap spanning several actor runs. Two overlapping runs can read the same checkpoint, deliver the same version and overwrite each other’s history. Use one schedule or a workflow mutex for each monitor store.

When moving an integration, copy its checkpoint only through an explicitly authorized storage workflow and verify it separately. Do not change stateStoreId casually: an empty store starts a new history and can bill currently visible items as new.

#### Dataset API and typed storage

Use the dataset identifier from the completed run when fetching results. Store the original JSON response before projecting columns into your application database. Preserve booleans as booleans, null as null and arrays as arrays; stringifying everything makes later validation harder. If your warehouse needs flat tables, create parent and child records with an explicit parent key and retain the source URL on the parent.

#### n8n or Make

Create a workflow that starts the saved task, waits for the run to finish, reads the summary and then downloads the dataset. Put the summary-status branch before the action that updates production records. Route partial and fatal runs to a review path. Avoid treating "run finished" as "all expected data arrived". Keep any credentials in the workflow platform's secret storage rather than placing them in source URLs or exported dataset columns.

#### Webhook receiver

A completion notification is a signal to fetch the run artifacts. It is not a complete substitute for those artifacts. Make your receiver idempotent using the run identifier and your own processing ledger, because webhooks can be delivered more than once. Acknowledge the notification promptly and process large exports separately. This prevents a slow database transaction from causing repeated notifications and duplicate downstream actions.

#### Spreadsheet review

Choose a stable set of top-level columns and keep a separate JSON archive for nested fields. Add a visible status or partial column next to the business content rather than hiding warnings on another sheet. When opening CSV in spreadsheet software, import identifiers and source text as text where appropriate. Spreadsheet auto-formatting can alter leading zeros, dates and strings beginning with formula characters. That is a downstream import concern, not evidence that the source changed.

#### Application database

Use a staging table before merging into a canonical dataset. Validate expected types, source identity and completed-unit status in staging. Keep a record of the input settings and actor version with the batch. Commit the merge only after your chosen completeness checks pass. An empty or failed extraction should create an attempt record without deleting the last known good data. This pattern makes source outages visible while protecting previously reviewed information.

#### Human review queue

Create one review record per actionable observation and include the original source URL, collection time, reason and relevant evidence. Group related observations in your own application rather than dropping provenance to reduce row count. The actor supplies evidence; your integration decides who receives it. It does not send emails, create tickets or message third parties by itself.

#### Reproducible exports

Save the input JSON, run ID, default dataset ID and OUTPUT summary together with your export. If a recipient asks why a value was included, you can reconstruct the observation without guessing which run produced the file. When you transform text or flatten nested data, document the transformation in your own export metadata. A derived CSV should not be presented as an untouched source response.

### R12. Operating guide

State lives in RSS\_MONITOR\_STATE\_V1 in the selected KVS. It contains at most10000item-version entries and1000feed-validator entries, with serialized validation at5MB. Entries retain fixed hashes and observation times rather than full item bodies. The oldest item-version entries are evicted when the cap is reached.

Checkpoint writes occur only after emit confirms delivery and, on a monetized platform run, a result charge. A write failure stops the run with state-checkpoint-failed after delivery; it does not pretend the previously paid item was absent. No transaction spans billing and checkpoint, so a crash in that gap can repeat a version on a later run.

ETag and Last-Modified are saved only after all eligible versions in a feed response have been handled. Partial runs leave no conditional validator that could hide undelivered versions behind304. A304 without a complete saved monitor checkpoint is a source\_error. Snapshot sends no checkpoint-derived conditional headers.

#### Resource envelope

The deployment defaults to a 300-second run timeout and a shorter 240-second working deadline. Per-response byte caps, input caps and request ceilings provide additional bounds. The shorter deadline leaves time to stop source work and write a summary before the platform timeout. It cannot guarantee recovery from every external failure, such as a storage service that remains unavailable during finalization.

Requests are paced per host. The one-request-per-second setting is this actor's own conservative operating limit, not a claim about an upstream service's published quota. Redirects, robots reads and retries consume requests too. Increasing the number of sources can therefore increase elapsed time even when few paid units result. Do not estimate runtime solely from the number of dataset rows.

#### Read the summary first

Compare requested, processed and unprocessed to understand how much of the input was handled. Then compare delivered and paid with result events, and inspect free and failed for diagnostic rows. HTTP request count measures transport activity, not billable output. A batch can require several requests for a single unit, and a free source failure still consumes bounded runtime. Preserve these counter meanings in dashboards and support tickets.

#### Change management

Recheck your saved task after an actor version change or a source format change. Run the smallest representative input, inspect the typed output and compare the paid unit count with expectations. If your application depends on a nested field, test that field specifically rather than only checking that a dataset exists. Keep the previous accepted export so you can distinguish a parser regression from a real upstream content change.

#### Reconciliation

When delivery is uncertain, record the run ID before doing anything else. Download the dataset and inspect chargedEventCounts from the run. Compare the results with the OUTPUT counters and log messages. The actor does not claim a transaction spanning external storage, a charge service and your downstream database. A failed charge response can leave useful data present, so never infer non-delivery from a failed run status alone.

#### Troubleshooting workflow

Start with the exact input that failed, the run summary and one affected source URL. Check whether the failure was input validation, URL safety refusal, robots refusal, DNS or transport failure, response format, a resource cap or delivery. Change one factor at a time and rerun a small bounded case. Removing all caps or broadening the source scope to chase a single failure usually obscures the reason rather than resolving it.

#### Acceptance and release

Local tests exercise the actual Apify SDK charging path with controlled persistence and charge transport boundaries. They cover exact budget boundaries, concurrency, missing pricing, priced dataset writes and failures before and after dataset delivery. They are necessary implementation evidence but do not replace a private platform build, real source canary, tariff readback or independent acceptance. The build draft remains a draft until those separate checks are recorded.

### R13. FAQ and limitations

Feed responses must be UTF-8 XML. Unsupported byte encodings are refused by the transport decoder; the actor does not guess an alternative character encoding.

**Does monitor provide permanent exactly-once delivery?** No. History is bounded, concurrent runs must be serialized and delivery plus checkpoint is not transactional. Crashes, history eviction or a new store can cause repeats.

**Does an RSS formatting change count as an edit?** Only when the normalized projected item fields change their hash. Whitespace or discarded HTML markup may normalize to the same summary.

**Can a feed item have no link?** Yes, if it has a stable GUID/AtomID and useful title or summary. No identity is invented from its position.

**Why does a cap not save ETag?** Saving a complete-feed validator too early could turn the next request into304 while unprocessed items still need delivery.

**Why does the dataset contain a row with no business content?** The actor returns free status rows to distinguish clean absence, limits and errors. Those rows keep the reason visible in the same export. Filter on the documented completed-unit contract when calculating useful output, and retain status rows in an operational log so the integration does not hide failures.

**Can I remove every limit for a large job?** No. The actor has a bounded product contract, including runtime, input and response limits. Break larger jobs into reviewed batches. A source that needs a browser, authentication or a different endpoint requires a separate capability decision rather than an unlimited flag.

Supported sources are public HTTP(S) RSS 2.0 and Atom 1.0 feeds supplied by the buyer for syndication processing. The buyer is responsible for authorization and downstream use. URLs cannot contain credentials or fragments. Every redirect is checked by the same URL safety and DNS-pinned SSRF policy, and its final URL is recorded. Robots rules, 401/403 restrictions and the one-request-per-second host limit are respected; no access control is bypassed.

**Will a retry produce exactly the same output?** Only if the source and relevant state remain unchanged and the same limits are used. Live content can change between attempts. Keep source identities, revisions or hashes where available and retain the input. Do not promise byte-identical exports from a changing external source.

**What should I send in a bug report?** Include the actor version or build ID, run ID, a minimal redacted input, the expected contract and the actual status or field. Remove tokens and confidential source content. One representative row plus the summary is usually more useful than a large unfiltered export. Explain whether the issue reproduces in a fresh small run.

**Is this page a guarantee of platform cost or availability?** No. The prices above were read back from the deployed tariff on 2026-09-05; check the tariff shown on the Store page before a large run. The source can change independently of the actor. Local parser and SDK tests establish specific behavior under tested conditions; cloud acceptance and ongoing operational monitoring establish different facts.

### R14. Sources, rights and support

Primary format references: [RSS2.0 specification](https://www.rssboard.org/rss-specification) and [Atom format](https://www.rfc-editor.org/rfc/rfc4287). Parser behavior is based on the [Saxes project](https://github.com/lddubeau/saxes); persistent storage uses the [Apify KeyValueStore SDK](https://docs.apify.com/sdk/js/reference/class/KeyValueStore). Format documentation does not grant a license to scrape or republish arbitrary feeds. The current owned fixture source basis is recorded in the reviewed Wave A hosting block.

Use only sources and content that you are authorized to process for your intended purpose. Preserve publisher attribution and applicable license notices in derived work. A conversion to JSON does not remove the obligations attached to the original content. Where the source has additional notices, keep those notices with the evidence and review them before redistribution.

Supported sources are public HTTP(S) RSS 2.0 and Atom 1.0 feeds supplied by the buyer for syndication processing. The buyer is responsible for authorization and downstream use. URLs cannot contain credentials or fragments. Every redirect is checked by the same URL safety and DNS-pinned SSRF policy, and its final URL is recorded. Robots rules, 401/403 restrictions and the one-request-per-second host limit are respected; no access control is bypassed.

For support, use the issue channel on the actor's Apify page after publication and include a reproducible minimal case. Deployment-specific evidence should include the build ID and run ID so support can compare the code and pricing used by that run. Never include API tokens, private cookies or unrelated personal data in a public issue. For a new capability, describe the input source, expected unit and the decision your integration needs to make.

Related-tool links below point only to published actors of the same author. The output examples above are accepted platform rows, not local fixture output.

#### Related tools

Related tools for adjacent workflows in data pipelines for AI applications.

| Actor | What it does |
|---|---|
| [Live Price Oracle](https://apify.com/zinin/live-price-oracle) | Pair it in the data pipelines for AI applications workflow: Get timestamped DexScreener market observations for up to 100 token contracts or bounded ticker searches |
| [RAG Dataset Builder](https://apify.com/zinin/sitemap-to-knowledge) | Pair it in the data pipelines for AI applications workflow: Turn buyer-authorized public website sitemaps into bounded, source-linked text chunks for RAG, search,... |
| [Structured Data Extractor](https://apify.com/zinin/structured-extract) | Pair it in the data pipelines for AI applications workflow: Extract title, meta, JSON-LD, headings, links, emails, and price observations from up to 100 authorized... |
| [URL to Markdown Converter](https://apify.com/zinin/url-to-markdown) | Pair it in the data pipelines for AI applications workflow: Convert up to 100 authorized public HTML pages into clean Markdown for RAG, AI agents, SEO research, and... |

# Actor input Schema

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

One to ten public RSS 2.0 or Atom 1.0 feed URLs you are authorized to process. No URL credentials or fragments. Robots and pinned SSRF checks apply on every hop; the prefill is our synthetic daily feed.

## `mode` (type: `string`):

Snapshot returns unique current items without reading or writing monitor state. Monitor requires a selected persistent KVS and emits versions not present in its bounded history.

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

Maximum complete eligible item versions delivered per feed. In monitor, already-seen versions are skipped before this cap, so subsequent capped runs can progress through remaining items.

## `stateStoreId` (type: `string`):

Select a dedicated persistent key-value store you control. Required in monitor, ignored in snapshot. Limited permissions grant access only to the store selected here. Serialize runs that share it.

## Actor input object example

```json
{
  "feedUrls": [
    "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml"
  ],
  "mode": "snapshot",
  "maxItemsPerFeed": 20
}
```

# Actor output Schema

## `results` (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 = {
    "feedUrls": [
        "https://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml"
    ],
    "mode": "snapshot",
    "maxItemsPerFeed": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("zinin/rss-feed-scraper-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://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml"],
    "mode": "snapshot",
    "maxItemsPerFeed": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("zinin/rss-feed-scraper-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://api.apify.com/v2/key-value-stores/b0cgFsJRqVv3XOZyG/records/feed.xml"
  ],
  "mode": "snapshot",
  "maxItemsPerFeed": 20
}' |
apify call zinin/rss-feed-scraper-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zinin/rss-feed-scraper-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/XlPEjF85pDPwow7Ys/builds/oRW0oJy8grfDMTbBA/openapi.json
