Feed to Markdown for AI Agents avatar

Feed to Markdown for AI Agents

Pricing

from $1.60 / 1,000 feed item as markdowns

Go to Apify Store
Feed to Markdown for AI Agents

Feed to Markdown for AI Agents

Returns one row per feed item: title, url, published_at, author, and markdown, the article as one Markdown document. Reads RSS, Atom and JSON Feed. Opens the linked page when the feed only has a teaser. Deduplicated, optionally across runs.

Pricing

from $1.60 / 1,000 feed item as markdowns

Rating

0.0

(0)

Developer

Comrade Coding

Comrade Coding

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

8 days ago

Last modified

Security

Limited permissions

Share

Returns one row per feed item: title, url, published_at, author, and markdown, the whole article as one Markdown document that starts with the title, date and link. Give it RSS, Atom or JSON Feed URLs. When a feed only carries a teaser, the actor opens the linked page and converts the main text instead. Items are deduplicated within a run, and across runs if you name a store.

It is built for pipelines that hand news, blog posts or release notes to a language model: one field holds everything the model needs, with no HTML, scripts or site navigation.

What it does with each feed URL

  1. Checks the site's robots.txt. A disallowed URL is skipped. A Crawl-delay is waited out between requests to that site.
  2. Downloads the feed (up to 20 MB) and works out its text encoding, including servers that send the wrong charset header.
  3. If the URL is a web page rather than a feed, it follows the first feed the page declares in a <link rel="alternate"> tag, so https://github.blog/ works as well as https://github.blog/feed/.
  4. Sorts items newest first when every item has a date, otherwise keeps the feed's order. Drops items older than publishedAfter, items already returned in this run (same id, or same link once utm_* parameters are removed), and items returned by earlier runs when seenItemsStore is set. Then keeps up to maxItemsPerFeed.
  5. For each item, picks the text (see contentMode), converts it to Markdown, and saves one row.

Where the text comes from

contentMode decides:

  • auto (default): if the feed's own text for the item has 150 words or more, that is used and no page is fetched. Otherwise the linked page is opened and its main text is extracted the way a reader view does it. If the page gives fewer words than the feed, the feed text is kept.
  • page: always open the linked page.
  • feed: never open pages. Fastest, and the only mode that makes one request per feed instead of one per item.

If the page cannot be used (HTTP error, disallowed by robots.txt, not HTML, timed out), the row still comes back with the feed text and page_error says why. An item with no text from either source is not saved and not charged; it is listed in FAILED_URLS.

Input

FieldTypeDefaultWhat it does
feedUrlsarrayrequired, no defaultFeed URLs, or web pages that link to a feed. Plain strings or { "url": "..." } objects. A bare host like example.com/feed gets https://. Duplicates are dropped.
maxItemsinteger100Stop after this many items in total. Caps what a run costs.
maxItemsPerFeedinteger20At most this many items from each feed.
contentModestringautoauto, page, or feed, as described above.
publishedAfterstringemptySkip items older than a date (2026-10-01) or an age (24 hours, 7 days, 2 weeks, 3 months). Items with no date are kept.
includeImagesbooleantrueKeep images as Markdown image links. Turn off for shorter, text-only output.
seenItemsStorestringemptyName of a key-value store in your account. Items returned by earlier runs with the same name are skipped, so a scheduled run returns only new items.

Example input:

{
"feedUrls": [
{ "url": "https://blog.rust-lang.org/feed.xml" },
{ "url": "https://github.blog/" }
],
"maxItems": 50,
"maxItemsPerFeed": 10,
"contentMode": "auto",
"publishedAfter": "7 days",
"includeImages": false,
"seenItemsStore": "my-news-feeds"
}

Output

One dataset item per feed item. This is a real row from a test run. The feed only had a short summary, so the actor read the page (content_source is page). The markdown value is cut short here.

{
"source_url": "https://www.jsonfeed.org/feed.json",
"fetched_at": "2026-10-29T13:18:30.338Z",
"feed_url": "https://www.jsonfeed.org/feed.json",
"feed_title": "JSON Feed",
"item_key": "cfdf7e9e1353c0b50d3e77ee",
"url": "https://www.jsonfeed.org/2020/08/07/json-feed-version.html",
"title": "JSON Feed version 1.1",
"author": null,
"published_at": "2020-08-07T16:44:36.000Z",
"updated_at": null,
"categories": [],
"content_source": "page",
"page_url": "https://www.jsonfeed.org/2020/08/07/json-feed-version.html",
"page_error": null,
"word_count": 89,
"truncated": false,
"markdown": "# JSON Feed version 1.1\n\n- Published: 2020-08-07 16:44 UTC\n- Link: https://www.jsonfeed.org/2020/08/07/json-feed-version.html\n- Feed: JSON Feed\n\n---\n\nWe’ve updated the spec to [version 1.1](https://jsonfeed.org/version/1.1). ..."
}

Fields:

  • source_url: the URL you gave. feed_url: the feed actually read, after redirects or after following a page's feed link.
  • fetched_at: ISO 8601 time the row was made.
  • feed_title: the feed's own title, or null.
  • item_key: a stable 24-character key, the same on every run. Use it to deduplicate in your own database.
  • url: the item's link. title, author: plain text, null when the feed has none.
  • published_at, updated_at: ISO 8601 UTC, or null when the feed gives no readable date.
  • categories: RSS categories, Atom terms, or JSON Feed tags.
  • content_source: feed or page, where the body text came from.
  • page_url: the page that was read, after redirects. null when the feed text was used.
  • page_error: why the page was not used, for example HTTP 404 or Disallowed by robots.txt. null when no page was tried or it worked.
  • word_count: words in the body, not counting the header.
  • truncated: true when the body was cut at 1,000,000 characters.
  • markdown: the document. A # title line, a short list with Published (or Updated), Author, Link and Feed (lines with no value are left out), a --- rule, then the body. Links and images are absolute. Lists, tables and code blocks are kept. A heading at the top of the body that repeats the title is removed.

Feeds that fail and items with no text are not added to the dataset and are not charged. They are listed in the FAILED_URLS record in the run's key-value store with a reason. A SUMMARY record gives the counts for the run: items pushed, how many came from pages and how many from feed text, page errors, items skipped as too old, duplicates, seen before, or over the per-feed cap, and why the run stopped (done, maxItems, or charge limit).

Limits

  • Public feeds and pages only. It does not log in or send cookies.
  • It reads what the feed holds at run time. Most feeds hold the last 10 to 50 items; older posts are not fetched from the site's archive.
  • Feeds over 20 MB and pages over 5 MB are skipped.
  • Page extraction works well on articles and blog posts. Pages built entirely with JavaScript return little text, and in auto mode the feed text is kept for those. Image posts such as web comics are a weak spot: the feed holds only the image, so auto opens the page, and on some sites the page's main text is the site's own boilerplate. Use contentMode: "feed" with images on for those feeds.
  • Pages are not fetched from sites whose robots.txt asks for more than 10 seconds between requests. Those rows use the feed text and get a page_error.
  • Requests go out one at a time, with a 300 to 800 ms pause between requests to the same site, or the site's Crawl-delay if longer. Sites asking for more than 120 seconds are skipped. A run that opens pages takes about one second per item.
  • Redirects are followed up to 5 times, and robots.txt is checked for each target before it is requested. A site whose robots.txt cannot be reached (server error or timeout) is skipped.
  • seenItemsStore keeps the most recent 100,000 item keys per store name, in your Apify account until you delete it. Items cut off by maxItems, and items that failed, are not marked as seen, so a later run picks them up.
  • Link titles are written on one line and cut at 300 characters.
  • Output keeps whatever the feed and pages contain, including author names. Check that you may use the content you collect.

Pricing

Pay per event: $0.002 per item saved ($2 per 1,000). Silver plan users pay $0.0018 and Gold plan users $0.0016. Failed feeds, items with no text, and skipped items cost nothing.

RunCost at the Free plan price
100 items (the default maxItems)about $0.20
1,000 itemsabout $2
10,000 itemsabout $20

A schedule that checks 20 feeds every hour with seenItemsStore set and finds about 5 new items an hour saves about 3,600 items a month: about $7.20.

Set maxItems to cap a run, or set a maximum cost per run in Apify; the actor stops when either is reached.

This actor is not affiliated with any of the sites used in the examples.

Use it from an AI agent

Add it as a tool through Apify's MCP server: https://mcp.apify.com?tools=comradecoding/feed-to-markdown. The tool takes a list of feed URLs and returns one row per item with the article as a Markdown document in markdown, plus title, url and published_at.

Integrations

Anything that can call the Apify API can run this actor: the Apify API and client libraries, and Apify's integrations for Make, n8n, Zapier and LangChain. Results come back as a dataset you can download as JSON, CSV or Excel.

FAQ

How is this different from RSS and Atom to JSON?

That actor returns many structured fields per item with a plain-text summary, and full text as an add-on. This one returns a single Markdown document per item, reading the page by default when the feed is short.

How do I get only new items on a schedule?

Set seenItemsStore to a name and keep it the same on every run.

Am I charged when a page fails?

The row is still saved with the feed text and page_error, and that row is charged once. If there is no text at all, nothing is saved or charged.

Can it read paywalled or members-only posts?

No. It reads public pages only, so a paywalled post gives whatever the feed itself contains.