RSS, Atom & JSON Feed to JSON
Pricing
$2.00 / 1,000 feed item parseds
RSS, Atom & JSON Feed to JSON
Turn RSS 0.9x/1.0/2.0, Atom and JSON Feed URLs into clean JSON entries with ISO 8601 dates, plain-text summaries, categories and podcast enclosures. No article downloads.
Pricing
$2.00 / 1,000 feed item parseds
Rating
0.0
(0)
Developer
Kadev Code
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Parse public RSS 0.9x/1.0/2.0, Atom and JSON Feed documents into a clean, normalized dataset. Each row is one feed entry with ISO 8601 UTC dates, a plain-text summary, categories, author and podcast enclosures. This Actor reads the feed only; it never downloads the linked article.
- No API key, browser or AI model.
- Tolerant XML parsing for real-world feeds, with DOCTYPE/ENTITY documents rejected before parsing.
- Redirects and gzip/deflate responses are supported.
- Invalid feeds produce one
errorrow and are not billed. - Duplicate GUIDs are removed within each feed. Duplicate links are removed across all feeds in the same run.
Input
| Field | Type | Default | Description |
|---|---|---|---|
feedUrls | array of strings | required | 1–20 public HTTP(S) feed URLs. Ports 80 and 443 only. |
maxItemsPerFeed | integer | 50 | 1–200 entries per feed, applied after filtering and deduplication. |
since | string | omitted | Optional ISO 8601 date or timestamp. Entries with an effective date older than it are excluded. |
since uses published, or updated when published is absent. Values without
an offset are treated as UTC. When since is set, entries with neither date are
excluded because the filter cannot be applied to them.
Example:
{"feedUrls": ["https://hnrss.org/frontpage","https://blog.example.com/feed.xml"],"maxItemsPerFeed": 25,"since": "2026-10-01T00:00:00Z"}
Output
One row per entry, plus one row per failed feed. All fields are present on every
row; nullable fields are null when the source does not provide them.
| Field | Type | Meaning |
|---|---|---|
feedUrl | string | null | Requested feed URL; null for an invalid-input row |
feedTitle | string | null | Channel or feed title |
title | string | null | Entry title as plain text |
link | string | null | Entry link, resolved against the final feed URL |
guid | string | null | Feed identifier (guid, id, or Atom ID) |
published | string | null | ISO 8601 UTC, e.g. 2026-10-20T13:00:00Z |
updated | string | null | ISO 8601 UTC |
author | string | null | First author name |
summary | string | Summary or content converted to text (HTML removed, entities decoded) |
contentHtml | string | null | Entry content HTML when the feed supplies it |
categories | array of strings | Entry categories or tags, deduplicated |
enclosures | array of objects | {url, type, length} podcast/media attachments |
error | string | null | Safe diagnostic for a failed feed; null on success |
Dates are normalized to UTC with second precision. RSS dates with numeric
offsets, Atom dates with Z or offsets, RFC 822 dates and JSON Feed RFC 3339
dates are supported. An unparseable date becomes null; it is never invented.
summary selects the feed summary, then the first content block, and converts
HTML to text with scripts, styles and markup removed. contentHtml uses
feedparser's HTML sanitizer for both XML and JSON Feed content. Relative JSON
Feed entry and attachment URLs are resolved against the final feed URL. A typical row:
{"feedUrl": "https://example.com/feed.xml","feedTitle": "Example RSS","title": "First & Best","link": "https://example.com/posts/1","guid": "post-1","published": "2026-10-20T13:00:00Z","updated": null,"author": "Alice","summary": "Hello world & friends.","contentHtml": "<article><p>Full <em>content</em>.</p></article>","categories": ["Tech", "News"],"enclosures": [{"url": "https://example.com/ep1.mp3", "type": "audio/mpeg", "length": 12345}],"error": null}
Deduplication and limits
- Within a feed, entries are deduplicated by GUID, or by link when no GUID exists.
- Across all feeds in one run, duplicate links are returned once; the first feed wins. Link fragments are ignored. GUID matching stays within each feed.
- Duplicate feed URLs in the input are processed once.
maxItemsPerFeedis applied aftersincefiltering and deduplication, keeping the first entries in feed order.- Each decoded feed is limited to 5 MiB (5,242,880 bytes), even when compressed.
Feeds with more than 50,000 source entries are rejected before normalization
to bound parser memory. XML entry tags are counted conservatively, including
tags inside comments or CDATA.
maxItemsPerFeeddoes not bypass these limits.summaryis truncated to 20,000 characters andcontentHtmlto 50,000 characters. At most 50 categories and 50 enclosures per entry are returned. - A run has a 240-second processing budget and a 25-second per-feed timeout. Feeds
not attempted in time receive a
Skippederror row without a custom charge. Parsing runs in a worker thread. If parsing times out, its results are discarded and the run stops before another feed, to prevent overlapping parsers. Python cannot terminate an already running thread; it may finish while the process shuts down. - Set the Actor run timeout to 300 seconds in Apify Console before release.
This platform setting is separate from the internal 240-second budget and is
not configured by
actor.json.
Network and XML safety
Feed URLs must use HTTP or HTTPS on port 80 or 443, with no embedded credentials.
The host must resolve to public internet addresses; private, loopback, link-local,
reserved, multicast, NAT64 and unsafe embedded IPv4 addresses (including 6to4 and
Teredo) are rejected. The Actor dials
only the validated IP, validates the connected peer again, and keeps the original
hostname for the Host header and TLS SNI. Redirects are followed manually for at
most five hops and every hop is revalidated.
Downloaded XML is checked with defusedxml; any DOCTYPE or ENTITY declaration
is rejected to prevent entity expansion and external entity access. feedparser
then handles RSS 0.9x/1.0/2.0 and Atom, including relative links. JSON Feed 1.x is
parsed directly from UTF-8 JSON. Malformed HTML or arbitrary JSON returns a clear
error row.
Pricing
One item-parsed event costs $0.002 per normalized entry delivered. There is
no startup event. Error rows, duplicates and filtered entries are not charged. See
.actor/pricing.md for the cost model and assumptions.
On the Apify platform, configure only item-parsed and remove the synthetic
apify-actor-start and apify-default-dataset-item events. The runtime refuses to
push rows if either synthetic event has a positive price.
Local development
Python 3.12 is the runtime. With uv:
uv run --python 3.12 --no-project --with-requirements requirements-dev.txt python -m pytest tests -q -W error
Or with an existing Python 3.12 environment:
python -m pip install -r requirements-dev.txtpython -m pytest tests -q
Tests block outbound sockets and use temporary local storage. Network behavior is
tested with httpx.MockTransport; no live feed is contacted and no event is
charged. Run the Actor locally with python -m src after installing
requirements.txt. Deployment is not performed by the test suite.