RSS, Atom & JSON Feed to JSON avatar

RSS, Atom & JSON Feed to JSON

Pricing

$2.00 / 1,000 feed item parseds

Go to Apify Store
RSS, Atom & JSON Feed to JSON

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

Kadev Code

Maintained by Community

Actor 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 error row and are not billed.
  • Duplicate GUIDs are removed within each feed. Duplicate links are removed across all feeds in the same run.

Input

FieldTypeDefaultDescription
feedUrlsarray of stringsrequired1–20 public HTTP(S) feed URLs. Ports 80 and 443 only.
maxItemsPerFeedinteger501–200 entries per feed, applied after filtering and deduplication.
sincestringomittedOptional 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.

FieldTypeMeaning
feedUrlstring | nullRequested feed URL; null for an invalid-input row
feedTitlestring | nullChannel or feed title
titlestring | nullEntry title as plain text
linkstring | nullEntry link, resolved against the final feed URL
guidstring | nullFeed identifier (guid, id, or Atom ID)
publishedstring | nullISO 8601 UTC, e.g. 2026-10-20T13:00:00Z
updatedstring | nullISO 8601 UTC
authorstring | nullFirst author name
summarystringSummary or content converted to text (HTML removed, entities decoded)
contentHtmlstring | nullEntry content HTML when the feed supplies it
categoriesarray of stringsEntry categories or tags, deduplicated
enclosuresarray of objects{url, type, length} podcast/media attachments
errorstring | nullSafe 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.
  • maxItemsPerFeed is applied after since filtering 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. maxItemsPerFeed does not bypass these limits. summary is truncated to 20,000 characters and contentHtml to 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 Skipped error 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.txt
python -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.