Versions follow MAJOR.MINOR.PATCH (src/version.py); Apify shows MAJOR.MINOR from .actor/actor.json.
Every run logs its version and records it in the RUN_STATS key-value record.
About future changes in output: main-content detection is heuristic, and sites redesign without notice. The
run log and RUN_STATS name every input and article that failed and why; one broken site never affects the others.
- A run that returns nothing (every input bad, private, blocked or unreachable, or an empty input) now ends
SUCCEEDED with an empty dataset and the reason as its status message and in the log, instead of FAILED. Only an
unexpected error still fails the run.
RUN_STATS keeps the reason as before.
- No change to output fields or price.
- A run no longer fails when the store it remembers earlier runs in can't be opened (403: the account already has
a store of that name that this actor didn't create). It falls back to
humble-echidna-<name>, and if that is
refused too it runs without memory for that run. Users' existing memory is untouched: the usual name is tried first.
- Waits out longer
Retry-After answers: when a site or API asks it to wait before retrying, it now waits up
to 90 seconds (was 30), once per site, with at most 3 minutes of such long waits per run; a longer wait, or a
site that asks again after it was waited out, is reported with the reason instead of stalling the run.
- Narrower AI opt-out rule: a page is skipped as opting out of AI use only when the site's robots.txt
disallows it for a general AI token (Google-Extended, Applebot-Extended, CCBot, or OpenAI's, Anthropic's or
Perplexity's crawlers and user fetchers). Blocking only one company's crawler (Bytespider, Meta-ExternalAgent,
Amazonbot, Diffbot, Omgilibot, cohere-ai) no longer counts: GitHub and Notion pages, for two, are read now.
- No change to input, output or price.
Chain it after another actor, e.g. RSS & Atom Feed Reader or a news scraper: the full text of every article it found.
- New inputs Or: article URLs from a dataset (
datasetId, a dataset picker with read access) and Field with
the article URL (datasetUrlField). With a dataset, each item's link is extracted like an article URL, and
sites and articleUrls are ignored, defaults included: a run started from an integration (which gets the input
schema's defaults filled in) never fetches or charges for the Rust blog. The field is found automatically when left
empty: the first of url (RSS & Atom Feed Reader's and GDELT News Search's link), link, articleUrl with a web
address in the first 100 items, then the same names one level down (article.link); a dotted path names a nested
one.
- Items without a link and values that aren't a public web address are skipped, never fetched or charged; an article
listed twice is extracted and charged once; tracking parameters (
utm_*, gclid, fbclid, ...) are dropped. All
counted in the new RUN_STATS.dataset record (itemsRead, websites (the article URLs kept), withoutWebsite,
notAWebsite, duplicates, the field used and whether it was found automatically). A dataset with no article URL
at all, or one that can't be read, fails the run with the reason.
- New nullable fields on every article:
sourceTitle (the item's title, else name) and sourceIndex (its
position in the dataset, 0 = the first), to join articles back to the items; null for articles not read from a
dataset. Such articles have discoveredVia: "dataset" and the dataset id in discoveredFrom.
- Read with the run's own token (the user's access, read-only), only the fields needed; at most the first 20,000
items and 10,000 article URLs per run. "Only new articles" applies to sites only, as with article URLs.
- No change to the price, the charged events, or runs without a dataset.
- 2 ready-to-run examples published on Apify Store and linked from the README.
- Cheaper on paid Apify plans: $0.90 per 1,000 articles on Starter, $0.80 on Scale and $0.70 on Business ($1.00 on the Free plan).
- Input field descriptions rewritten for AI agents (Apify's MCP server shows agents only the description, not the
form): each now states its default, its allowed range, the value of each option and how it combines with other
fields. No change to field names, types, defaults or behaviour.
sites and articleUrls say that a left-out sites takes its default (the Rust blog), so an agent extracting only
its own article URLs passes "sites": []; the README's MCP answer says the same.
First release.
- Article pages to one clean row each: title, author(s), published and modified dates (ISO 8601 UTC), lead image,
language, site name, description, canonical URL, main text as plain text and as Markdown, word count.
- Input is article URLs, or sites: a website (its RSS/Atom/JSON feed is found from its page), a feed URL, or a
sitemap URL. Auto mode reads the feed, else the sitemap (robots.txt
Sitemap: lines, then the usual locations;
indexes and news sitemaps included). Newest first, up to "Max articles per site"; URL patterns keep sitemaps to
articles; undated sitemap entries are ordered by a date in their URL (/2026/09/22/), never output as one;
discovered pages under 50 words (tag pages, listings) are skipped and not charged.
- "Only new articles since the last run", remembered per site and per search (discovery mode + URL patterns) in a
named store in the user's account; only articles actually written to the dataset are remembered.
An article is remembered under both the link the feed/sitemap lists and the URL it redirects to, and the final URL
is checked before anything is charged, so feeds whose links redirect (http to https, trailing slash) never
re-charge an article on later runs (tested across runs with two-hop 301s).
- Metadata from schema.org JSON-LD (
@graph included), Open Graph / article:*, common publisher meta tags,
microdata and <time>; the feed or news sitemap fills in title, author and date when the page doesn't say.
- Extraction, feed discovery and sitemap discovery are the shared code of page-to-markdown, rss-feed-reader and
sitemap-urls (
mms_extract, mms_feeds, mms_sitemap), not copies.
- Charged per article, through Apify's standard
apify-default-dataset-item event. "Max articles per run" and the
maximum cost per run are honoured before each article is fetched; a failed article gives its place back.
- robots.txt, AI-crawler opt-outs (same policy as page-to-markdown), Crawl-delay and the private-network guard
from the shared fetching code. No presets for sites whose terms forbid crawling (e.g. Medium, Substack).
- Sources checked before building (ACTOR-CHECKLIST section 0): no fixed source. It fetches only pages the user
names and the feeds and sitemaps those sites publish for machines, as a logged-out visitor, honouring robots.txt
and AI opt-outs; the legal-hold rule in PLAN.md allows "tools that only process URLs the user supplies" and
"RSS/JSON feeds". Demand (store_merged.json, 2026-09-24): ~375 users/30 days across generic article extractors;
the leader (lukaskrivka/article-extractor-smart, 267) is free, the paid ones charge $1.00-$5.00 per 1,000
(easyapi $2.99 + $0.09 start, tugelbay $5.00, automation-lab $2.30 + $0.005 start, tropical_prune $1.00).