Substack Posts Scraper: Whole Archive & Comments avatar

Substack Posts Scraper: Whole Archive & Comments

Pricing

from $0.70 / 1,000 posts

Go to Apify Store
Substack Posts Scraper: Whole Archive & Comments

Substack Posts Scraper: Whole Archive & Comments

Every post of a Substack newsletter, back to its first, with its subscriber count and paid plans. $1 per 1,000 posts. Full text and whole comment threads on request.

Pricing

from $0.70 / 1,000 posts

Rating

0.0

(0)

Developer

Luzi

Luzi

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

7 days ago

Last modified

Security

Limited permissions

Share

Get every post of a Substack newsletter, newest first, all the way back to its first post: title, subtitle, dates, audience (free or paid), word count, likes, reactions, comment and restack counts, authors, tags, cover image, podcast and audio details, and the publication's own details: its subscriber count as Substack shows it, its paid plans and prices, and its sections and contributors. 124 fields a post. Turn on post text for each post's full body, and comments for its whole comment thread, replies included. Works with publications on their own domain. No Substack account needed.

Tested daily on a sample of two newsletters. Latest full read: 4,142 posts from three whole newsletters in about three minutes, every post each one lists, its sections and podcast included.

What does this Actor do?

You give it Substack newsletters: a publication's link, a writer's profile, or a post's link. For each publication it reads the archive from the newest post back, and saves one row per post until it reaches your limit, your date, or the publication's first post.

Each post comes with:

  • its link, id, title, subtitle, description, type (newsletter, podcast, thread or video) and when it was published
  • who can read it (everyone or paying subscribers) and how many words it has
  • its likes, each reaction's count, how many comments and replies it has, and how many times it was restacked
  • every author: name, handle, photo, bio, X (Twitter) name, bestseller tier, and the publications they write for
  • its tags, its section, its cover image and the image's colours
  • for a podcast: the audio link, length, episode and season numbers
  • the publication's name, link, own domain, logo, tagline, owner, when it started and its first post
  • the publication's subscriber count and paid-subscriber range, as Substack shows them, its paid plans with their prices, what each plan offers, its podcast, sections and contributors

With Include post text on, each post also gets:

  • its full body, as HTML and as plain text (a paid post gives the free preview only)
  • when it was last updated, and the posts before and after it

With Include comments on, each post also gets its whole comment thread, replies included, in reading order: each comment's text, date, likes, the commenter's name, handle and photo, whether they are the author, and the comment it answers.

Why use it?

  • The whole archive. It keeps going to a publication's very first post, or stops at your limit or your date. Posts filed in sections the publication keeps off its main archive page (a podcast, a weekly digest) are read too.
  • Subscribers and prices. Every row carries the publication's subscriber count and paid plans, so one export compares newsletters.
  • Every comment, not a sample. With comments on, a post's whole thread comes back, replies at every depth, in the post's own row.
  • One price. The same price per post with or without the text and the comments; no start fee.
  • Any Substack. Publications on substack.com and on their own domain, a writer's profile, or single posts.
  • Use the data anywhere. Download as JSON, CSV or Excel, or send it to your own tools through Apify's API and integrations.

How to use it

  1. Paste one or more newsletters into Substack links: https://example.substack.com, a publication's own domain such as https://www.lennysnewsletter.com, a writer's profile (https://substack.com/@handle), or a post's link (…/p/post-name) for that one post.
  2. Optionally, set Only posts newer than, Max posts per publication, Include post text and Include comments.
  3. Click Start. When the run ends, open the Output tab to view the posts or download them.

Run it with no input to see a sample: up to 50 posts each from two newsletters, charged as usual.

Input

FieldWhat it doesDefault
Substack links (startUrls)Publications, writers' profiles or posts to read. Required.—
Only posts newer than (onlyPostsNewerThan)A date (YYYY-MM-DD, UTC) or a period (7 days, 2 weeks, 3 months, 1 year). Empty = the whole archiveempty
Max posts per publication (maxPostsPerPublication)Stop reading a publication after this many posts, from 1 to 50,000100
Include post text (includeBody)Also read each post's full textoff
Include comments (includeComments)Also read each post's whole comment threadoff
Only new posts (onlyNewItems)Return only the posts an earlier run did not; see New items only belowoff

Example input:

{
"startUrls": ["https://www.lennysnewsletter.com", "https://noahpinion.substack.com"],
"onlyPostsNewerThan": "6 months",
"maxPostsPerPublication": 1000,
"includeBody": true
}

New items only

Turn on Only new posts (onlyNewItems) for a scheduled run, or a re-run of the same task. Each run then returns only the posts it has not returned before: it reads each publication back to a day before the last run that read it, and skips the posts an earlier run saved. Skipped posts are neither saved nor charged; each new post is charged as usual.

  • Runs with other settings, or from another task, keep their own list, so two schedules on one publication never skip each other's posts.
  • If a publication stops at Max posts per publication, the posts past that point are not returned later. Set the maximum above what one publication gets between runs.
  • A post's comments are read when the post is new; comments added after that are not read again.
  • The list lives in your Apify account, in a key-value store named substack-posts-scraper-monitor. Delete it to start afresh.

Output

One row per post. An example, shortened, with made-up values:

{
"post_id": 900000001,
"url": "https://example.substack.com/p/made-up-post",
"slug": "made-up-post",
"title": "A made-up post",
"subtitle": "What a made-up subtitle says",
"type": "newsletter",
"audience": "everyone",
"is_paid": false,
"published_at": "2026-10-20T12:00:00.000Z",
"word_count": 1850,
"author_name": "Made-up Writer",
"author_handle": "madeupwriter",
"reaction_count": 245,
"reactions": { "❤": 245 },
"comment_count": 31,
"restack_count": 12,
"tags": [{ "id": "a1b2", "name": "Essays", "slug": "essays" }],
"body_text": "The post's text, as plain text.",
"publication_name": "Made-up Newsletter",
"publication_url": "https://example.substack.com",
"body_read": true,
"comments_read": true,
"comments": [
{
"comment_id": 5001,
"parent_id": null,
"depth": 0,
"body": "A made-up comment.",
"posted_at": "2026-10-20T14:00:00.000Z",
"name": "Made-up Reader",
"handle": "madeupreader",
"is_author": false,
"reaction_count": 4
}
],
"source_url": "https://example.substack.com"
}

Every field:

FieldWhat it holds
post_idSubstack's id for the post
urlThe post on its publication
slugThe post's name in its link
titleThe title
subtitleThe subtitle
descriptionThe post's short description, as shared
social_titleThe title shown when the post is shared, when the writer set one
search_engine_titleThe title for search engines, when the writer set one
search_engine_descriptionThe description for search engines, when the writer set one
typenewsletter, podcast, thread or video
audienceWho can read it: everyone, only_paid, founding or only_free
is_paidtrue for a post only paying subscribers can read in full
published_atWhen it was published
updated_atWhen it was last updated (body on, or a post link)
languageThe post's language code
word_countHow many words it has, paid part included
author_idThe first author's Substack id
author_nameThe first author's name
author_handleThe first author's handle (substack.com/@handle)
authorsEvery author: id, name, handle, photo, bio, X (Twitter) name, guest or not, bestseller tier, subscriber tier, badge, when the profile was set up, their own publication, and the publications they write for with their role
reaction_countHow many likes (hearts) it has
reactionsEach reaction with its count
comment_countHow many comments it has, replies included
child_comment_countHow many of those are replies
restack_countHow many times it was restacked
tagsIts tags: id, name and slug
section_idThe section it is filed in, when the publication has sections
section_nameThat section's name
section_slugThat section's name in its link
is_section_pinnedtrue when it is pinned in its section
cover_image_urlThe cover image's link
cover_image_is_squaretrue when the cover image is square
cover_image_is_explicittrue when the cover image is marked explicit
cover_image_paletteThe cover image's main colours, as RGB values
truncated_body_textThe post's opening lines, when the publication shows them in its archive
body_htmlThe post's body as HTML (body on, or a post link). For a paid post, the free preview
body_textThe same body as plain text
has_dynamic_contenttrue when the body has parts that change for each reader
post_preview_limitHow much of a paid post is shown free
should_send_free_previewtrue when free subscribers got a preview by email
free_unlock_requiredtrue when a free subscriber must unlock it
teaser_post_eligibleSubstack's flag for posts that may be shown as a teaser
meter_typeHow the paywall counts reads, when the post has one (body on, or a post link)
exempt_from_archive_paywalltrue when it stays free after the publication's archive goes paid (body on, or a post link)
write_comment_permissionsWho may comment: everyone, paid or none
default_comment_sortThe comment order the writer chose, when they chose one
podcast_urlThe episode's audio link, for a podcast post
podcast_durationThe episode's length in seconds
podcast_art_urlThe episode's own artwork (body on, or a post link)
podcast_episode_numberThe episode number
podcast_season_numberThe season number
podcast_episode_typefull, trailer or bonus
free_podcast_urlThe free preview's audio link, for a paid episode
free_podcast_durationThe free preview's length in seconds
podcast_upload_idSubstack's id for the episode's audio
podcast_preview_upload_idSubstack's id for the episode's free preview
video_upload_idSubstack's id for the post's video
voiceover_upload_idSubstack's id for the writer's own reading of the post
has_voiceovertrue when the writer recorded a reading of the post
audio_itemsThe post's audio versions (Substack's read-aloud and others): type, voice, status and link
restacked_post_idFor a restack: the original post's id
restacked_post_slugFor a restack: the original post's slug
restacked_publication_nameFor a restack: the original post's publication
restacked_publication_logo_urlThat publication's logo
previous_post_slugThe slug of the publication's post before it (body on, or a post link)
next_post_slugThe slug of the post after it (body on, or a post link)
is_publishedtrue for a published post (body on, or a post link)
is_hiddentrue when the writer hid it from the publication's home page
is_geoblockedtrue when it is blocked in some countries
country_blocksThe countries it is blocked in
has_cashtagtrue when it mentions a stock by its $ symbol
has_shareable_clipstrue when it has video or audio clips readers can share (body on, or a post link)
live_stream_idThe live stream it came from, when it did (body on, or a post link)
publication_idSubstack's id for the publication
publication_nameThe publication's name
publication_urlThe publication's home page, on its own domain when it has one
publication_subdomainThe publication's name in its substack.com address
publication_custom_domainThe publication's own domain, when it has one
publication_logo_urlThe publication's logo
publication_hero_textThe publication's tagline
publication_typenewsletter or podcast
publication_created_atWhen the publication was created
publication_first_post_dateWhen the publication's first post was published
publication_languageThe publication's language code
publication_subscribersThe publication's subscriber count as Substack shows it ("Over 1,200,000 subscribers"), when the writer shows it
publication_subscribers_roundedThe same count, rounded as Substack shows it ("1.2M+")
publication_subscribers_rangeSubstack's range for all its subscribers, free and paid ("Millions of subscribers")
publication_subscribers_range_minThat range's lower end as a number (1000000)
publication_paid_subscribers_rangeSubstack's range for its paid subscribers ("Tens of thousands of paid subscribers"), when it shows one
publication_paid_subscribers_range_minThat range's lower end as a number (10000)
publication_bestseller_tierThe owner's Substack bestseller tier (100, 1000, 10000...), when they have one
publication_author_idThe Substack id of the publication's owner
publication_owner_nameThe owner's name
publication_owner_handleThe owner's handle
publication_owner_bioThe owner's bio
publication_owner_photo_urlThe owner's photo
publication_twitter_screen_nameThe publication's X (Twitter) name, when it has one
publication_cover_photo_urlThe publication's cover photo, when it has one
publication_payments_stateWhether the publication takes paid subscriptions (enabled, disabled)
publication_plansIts paid plans: name, month or year, the price (20 for $20) and currency, and whether it is on sale
publication_founding_plan_nameThe name of its top paid plan
publication_free_benefitsWhat a free subscriber gets, as the writer lists it
publication_paid_benefitsWhat a paid subscriber gets
publication_founding_benefitsWhat a founding member gets
publication_paywall_free_trial_enabledtrue when the publication offers a free trial
publication_has_podcasttrue when the publication has a podcast
publication_podcast_titleThe podcast's title
publication_podcast_descriptionThe podcast's description
publication_podcast_bylineThe podcast's byline
publication_podcast_feed_urlThe podcast's own feed link, when it has one
publication_podcast_art_urlThe podcast's artwork
publication_sectionsThe publication's sections: id, name, slug, and whether it is a podcast
publication_contributorsThe people who write for it: id, name, handle, role, whether they own it, bio and photo
publication_has_recommendationstrue when the publication recommends other publications
publication_email_from_nameThe sender name on the publication's emails
publication_copyrightThe publication's copyright line
publication_homepage_typeThe publication's home page layout
publication_community_enabledtrue when the publication has a community (chat) for readers
publication_invite_onlytrue when the publication is invite only
publication_explicittrue when the publication is marked explicit
body_readWith body on: true when the post's body was read. Empty with body off
comments_readWith comments on: true, the post's comments were read (a post whose thread cannot be read is not saved). Empty with comments off
commentsWith comments on: the post's whole comment thread, replies included, in reading order. Each comment has its id, the comment it answers (parent_id), its depth, text, dates, the commenter's id, name, handle and photo, whether they are the author, their membership, likes, restacks, reply count, score, status and whether it is pinned or deleted
source_urlThe link you gave that this post was read from

Not included: the paid part of a paid post (the text gives the free preview), comments on a paid post (Substack shows them to subscribers only), Substack Notes, exact paid-subscriber numbers (Substack shows a range), and search across all of Substack.

How much does it cost?

No surcharges and no paid options; an item that fails is not charged.

You pay for what you get:

  • $1 per 1,000 posts ($0.001 a post), with or without the text and the comments. Each post is charged once per run.
  • No start fee, and nothing per publication.

So a publication's 400 posts cost $0.40. If you set a maximum cost for a run, the Actor stops when it reaches it and keeps every post saved so far.

How long does a run take?

About 100 posts in 10 to 20 seconds, and three whole newsletters, 4,142 posts, in about three minutes. With the text and comments on, each post takes longer: 200 posts in about four minutes.

Good to know

  • Check your input first. A link that is not a publication, profile or post link stops the run before anything is read, and the message names it.
  • One link that cannot be read does not stop the run. A domain that is not a Substack, a post that was taken down or a writer with no publication ends with a message, the others carry on, and the run's status lists them. The run fails only when no link could be read.
  • Paid posts. Their row has every field but the paid part of the text; is_paid marks them.
  • Large threads. A post with hundreds of comments makes a large row; keep comments off when you only need the counts.
  • Counts move. Likes, comments and restacks are the numbers at the moment the post was read.
  • Remove repeats by post_id. A daily run reads recent posts again, unless Only new posts is on.

More Actors from us

Use it from an AI agent or the API

One call starts a run and returns its items (Apify waits up to 300 seconds; for a longer run, start it with POST /v2/acts/…/runs and read its dataset):

POST https://api.apify.com/v2/acts/luzivanlt~substack-posts-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>

With the sample input as its JSON body:

{
"startUrls": ["https://www.lennysnewsletter.com", "https://noahpinion.substack.com"],
"maxPostsPerPublication": 50,
"includeBody": false,
"includeComments": false
}

Its inputs, in the order of the input form:

InputWhat it takesDefault
startUrls (Substack links)Substack newsletters to read: a publication's link (https://example.substack.com, or its own domain such as https://www.example.com), any page of it, a writer's profile (https://substack.com/@handle or @handle) for their own publication, or a post's link (…/p/post-name) for that one post.—
onlyPostsNewerThan (Only posts newer than)Keep only posts published from this date on: a date (YYYY-MM-DD, UTC) or a period, e.g. "7 days", "2 weeks", "3 months" or "1 year".—
maxPostsPerPublication (Max posts per publication)Stop reading a publication after this many posts, newest first, from 1 to 50,000.100
includeBody (Include post text)Also read each post's full text, as HTML and as plain text, with its last update date and the posts before and after it.false
includeComments (Include comments)Also read each post's whole comment thread, replies included, into the post's row: each comment's text, date, likes, the commenter's name and handle, and the comment it answers.false
onlyNewItems (Only new posts)Turn this on for a scheduled run, or a re-run of the same task: each run then returns only the posts it has not returned before, reading each publication back to a day before the last run that read it.—

Apify's MCP server, https://mcp.apify.com, lists it as luzivanlt/substack-posts-scraper. To give an AI agent this Actor as a tool, add the server to the agent's MCP config:

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com?tools=luzivanlt/substack-posts-scraper"
}
}
}

The client signs in with OAuth, or add "headers": { "Authorization": "Bearer <APIFY_TOKEN>" }.

Charged per post delivered; the sample input above costs at most $0.10.

FAQ

Do I need a Substack account? No. The Actor reads what anyone can see on Substack without signing in.

Does it work with a newsletter on its own domain? Yes. Paste the domain, such as https://www.lennysnewsletter.com, or the newsletter's substack.com address.

Can I run it from my own code, on a schedule, or with other apps? Yes. Like any Apify Actor, you can start it through the Apify API, run it on a schedule, and connect it to other apps with Apify's integrations and webhooks.

This Actor reads only public posts and comments. Authors' and commenters' names, handles and photos are personal data; treat them as such. Check Substack's terms and the laws that apply to you (such as the GDPR or the CCPA) before you use the data. If you are unsure, ask a lawyer.

Support

Found a problem or missing a feature? Open an issue on the Issues tab and include the run's link.


This Actor is not affiliated with, endorsed by or sponsored by Substack.