Substack Newsletter Posts, Dates and Engagement
Pricing
$2.00 / 1,000 result rows
Substack Newsletter Posts, Dates and Engagement
Substack scraper, Substack posts scraper, newsletter monitoring: give a list of Substack publications and get one row per public post with title, date, free-or-paid flag, reactions, comments and word count.
Pricing
$2.00 / 1,000 result rows
Rating
0.0
(0)
Developer
US Tech Automations
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Substack scraper, Substack posts scraper, newsletter monitoring: list the Substack publications you track and get one row per public post -- title, date, free-or-paid status, reactions, comments, and word count -- without opening each newsletter by hand.
Introduction
Give this Actor one or more Substack addresses. For each one it reads the
publication's own public post-archive list (the same JSON the publication's
/archive page loads), and returns one row per post: what it's called, when
it went out, whether it's free or paid, how many reaction hearts and
comments it has, its word count, and its byline. Nothing is read from
behind a login. A publication this run cannot read -- because it does not
exist, or blocks this run -- gets exactly one row saying so, with a real
reason, never a silent gap.
Tutorial
- Set Publications to one or more Substack addresses:
name.substack.com, a barename(short forname.substack.com), or a custom domain such aslennysnewsletter.com. - Optionally set Since date to only include posts on or after that day.
- Optionally set Maximum posts per publication and Maximum rows -- both control cost; every row bills, so these are your spend caps.
- Optionally turn on Include free post body text to also copy the
plain text of each free post. Paid and founding-only posts always get
body_text: null-- see "What this does not do." - Optionally paste Previously seen post IDs from a prior run's
post_idfield to getis_new: trueonly on posts you have not seen. - Click Start. Results land in the dataset.
Pricing
$0.002 per row. No start fee.
A run that reads 500 qualifying post rows costs $1.00. A publication that does not exist, or that this run could not read, still produces one row (so you can see what happened) and that one row still bills at $0.002.
Maximum rows is the spend cap: the run cannot cost more than that number multiplied by $0.002, whatever the publications hold.
Input and output examples
Input:
{"publications": ["importai", "lennysnewsletter.com"], "maxPostsPerPublication": 5, "maxItems": 10, "includeBodyText": true}
Output (one row, from a live run on 2026-09-21):
{"publication": "importai","publication_host": "importai.substack.com","post_id": 216673688,"title": "Import AI 473: The US's superintelligence strategy; human brains vs LLMs at Othello; and why loving your work might be the best moat.","subtitle": "What matters in AI for 21 Sep 2026","post_url": "https://importai.substack.com/p/import-ai-473-the-uss-superintelligence","post_date": "2026-09-17T11:30:37.435Z","audience": "everyone","is_paid": false,"type": "newsletter","reaction_count": 24,"comment_count": 5,"wordcount": 3876,"cover_image": "https://substackcdn.com/image/fetch/...","authors": ["Jack Clark"],"body_text": "...24,931 characters of plain text...","is_new": true,"source": "archive","fetched_at": "2026-09-21T16:40:12Z","error": false,"error_type": null,"message": null}
A publication this run could not read (from the same run family, made-up address):
{"publication": "this-publication-does-not-exist-zz999.substack.com","publication_host": "thispublicationdoesnotexistzz999.substack.com","post_id": null, "title": null, "subtitle": null, "post_url": null,"post_date": null, "audience": null, "is_paid": null, "type": null,"reaction_count": null, "comment_count": null, "wordcount": null,"cover_image": null, "authors": null, "body_text": null, "is_new": null,"source": null,"fetched_at": "2026-09-21T16:40:12Z","error": true,"error_type": "NOT_FOUND","message": "Archive unreadable (... HTTP 404); feed fallback also answered HTTP 404."}
Every field in a row:
| Field | What it holds |
|---|---|
publication | The address you gave, exactly as given |
publication_host | The resolved host this run actually read |
post_id | Substack's own numeric post id (null on the RSS-fallback path -- RSS does not carry it) |
title / subtitle | The post's headline and dek |
post_url | The post's canonical public URL |
post_date | Publish date/time, ISO 8601 |
audience | everyone, only_paid, or founding, as Substack's own archive reports it |
is_paid | true unless audience is everyone |
type | newsletter, podcast, or thread |
reaction_count | Sum of all reaction-emoji counts Substack shows on the post |
comment_count | Public comment count (comment text is never read) |
wordcount | Substack's own word count for the post |
cover_image | Cover image URL, when the post has one |
authors | Public byline display name(s) only -- no handle, bio, or photo |
body_text | Plain text of the post, free posts only, only when Include free post body text is on; otherwise null |
is_new | true when post_id is not in Previously seen post IDs; null when unknown (RSS-fallback rows) |
source | archive, feed_fallback, or null on an error row -- which endpoint this row came from |
fetched_at | When this run read the source, in UTC |
error | true for the one row a publication gets when it could not be read |
error_type / message | Present only when error is true: a short code and the real reason |
Recommendations
Run weekly or monthly per publication list, and set Previously seen post
IDs from the last run's post_id values to see only what changed.
Limitations
body_text only exists for free posts, and only from the RSS feed's
recent window. Substack's archive JSON (the main source for every other
field) never carries full post text -- only a short preview. Free-post text
comes from the publication's public /feed, which typically holds the
~20 most recent items across all post types (including separate podcast
episodes). An older free post can fall outside that window; it gets
body_text: null, not a guess.
Paid text is never read, full stop. A post the archive itself marks
only_paid or founding always gets body_text: null, even though
Substack's own RSS feed happens to also carry a truncated preview of paid
posts. This tool does not read it.
A blocked or unreadable archive falls back to the RSS feed, per the
contract. On that fallback path, post_id, audience, is_paid,
reaction_count, comment_count, and wordcount are all null -- RSS
does not carry them, and this tool does not invent them. source is set to
feed_fallback on every row from that path so it's never mistaken for a
full archive row.
No reader, subscriber, or comment content. Only the public post-level counts and public byline names are read. Comment text, reader names, and email addresses are never requested.
A source outage or a blocked host returns one honest row, not guesses.
If neither the archive nor the feed can be read for a publication, that
publication gets one row with error: true and a real reason -- the run
still bills for that one row, and produces no invented posts.
FAQ
Where does the data come from?
Each publication's own public post-archive JSON (/api/v1/archive) and, as
a fallback or for free-post text, its own public RSS feed (/feed). Both
are read live on every run, never from a stored copy.
Does it need an API key or a login? No. Both endpoints are served to a signed-out visitor.
Why did I get fewer rows than Maximum rows? Either the publications held that many posts and no more, or the run hit Maximum rows first, or a publication returned one error row instead of its posts.
What happens if a publication is blocked or does not exist?
That publication gets exactly one row with error: true and a real reason
in error_type / message. It never gets invented posts.
Is there support? Yes. Raise it through the Apify Store issues tab for this actor, or email operations@ustechautomations.com. Replies within 2 business days.
Refunds and support
Refunds: if a run returns only error rows for a publication you confirmed is real and public, email operations@ustechautomations.com within 7 days and we refund that run. Support: same address, replies within 2 business days.