Substack Scraper
Pricing
from $0.40 / 1,000 posts
Substack Scraper
Substack posts with their likes, comments, restacks and full text, found by keyword across all of Substack or read from the archives of newsletters you name. Comment threads, newsletter details and a new-posts-only mode are there for daily monitoring.
Pricing
from $0.40 / 1,000 posts
Rating
0.0
(0)
Developer
deriverge s.r.o.
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
9 days ago
Last modified
Categories
Share
Substack Scraper returns Substack posts as rows: title, date, newsletter, authors, word count, likes, comments, restacks and, with Include post text on, the text itself. Type a keyword such as artificial intelligence to search all of Substack, or enter newsletters such as lenny or https://www.noahpinion.blog to read their archives, then click Start. For the newsletters you name it also returns comments and a header row per newsletter. Paid posts give only the text above the paywall. It costs $0.80 per 1,000 posts on the Free plan ($0.40 on Business), and the $5 of monthly credit in Apify's Free plan covers about 6,200 posts.
What data does it return?
Every row has a type: post, comment or newsletter, and each type fills its own fields.
Posts
| Field | Description |
|---|---|
title, subtitle | Title and subtitle of the post. |
url, slug | Canonical address of the post and the part of it after /p/. |
publishedAt | Publication time as an ISO 8601 timestamp in UTC. |
audience | everyone for free posts, only_paid or founding for posts behind the paywall. |
postType | newsletter, podcast, thread or video. |
authors | Bylines with name and handle. Guest posts list the guest authors. |
wordCount | Length of the whole post in words as Substack counts it, paid posts included. |
likes, comments, restacks | Likes, comments and restacks (shares inside Substack) at the time of the run. |
description, previewText | The post's description, usually its subtitle, and the opening lines Substack shows in listings. |
bodyText, bodyHtml, bodyIsPreview | With Include post text on: the post as plain text with its paragraphs, and as HTML. bodyIsPreview is true for paid posts, whose text stops at the paywall. |
podcastUrl, podcastDurationSeconds | Audio file and its length in seconds, for podcast posts. |
section, tags, coverImage | Section, the author's tags and the cover image, where the post has them. |
newsletter, newsletterSubdomain, newsletterUrl | The newsletter the post belongs to and its home address. |
searchQuery, searchRank | For posts found by keyword: the keyword and the post's position in Substack's results, 1 being the first. null for posts read from a newsletter archive. |
Comments
| Field | Description |
|---|---|
author, authorHandle | The commenter's public display name and Substack handle, as shown under the post. |
body | Text of the comment. |
publishedAt, edited | When the comment was posted, and true if it was edited afterwards. |
likes, replies | Likes on the comment and the number of direct replies to it. |
parentId, depth | The comment it answers, null at the top level, and its level in the thread, 0 at the top. |
postId, postUrl, postTitle | The post the comment is under. |
Newsletters
| Field | Description |
|---|---|
name, author, authorHandle | Name of the newsletter and its author's public name and handle. |
subdomain, customDomain, url | The substack.com subdomain, the custom domain if there is one, and the home page. |
description, language | Tagline from the home page and the language code, for example en. |
subscriberTier | Paid audience in Substack's words, for example Tens of thousands of paid subscribers. null for small newsletters, where Substack shows only how long ago they launched. |
freeSubscribers | The subscriber count Substack displays, often rounded, such as 458000 for Noahpinion. null when hidden. |
launchedAt, firstPostAt | When the publication was created on Substack and when its first post appeared. |
paidEnabled, hasPodcast, explicit | Whether it sells subscriptions, publishes a podcast and is marked as explicit. |
searchQuery | The keyword that found the newsletter, with Include newsletters of found posts on. |
Newsletter rows also have category, categoryId, rank and rankingList, which are always null in this actor.
How to scrape Substack
- In Search Substack by keyword, add one word or phrase per line, for example
artificial intelligenceorclimate tech. Leave it empty if you only want specific newsletters. - Pick the Search order: Top for the most relevant and popular posts of any age, Recent for newer posts. Maximum posts per keyword counts posts after your filters, and Include newsletters of found posts adds a row per newsletter.
- In Newsletters, add one newsletter per line in any form: a subdomain (
lenny), asubstack.comaddress, a custom domain (https://www.noahpinion.blog) or a link to one of its posts. Set the Archive order and Maximum posts per newsletter. - Narrow the search results and the newsletter posts with Published after, Audience and Keywords (any of), which looks in the title, subtitle, description and preview.
- Keep Include post text on for the body. Include comments and Include newsletter header work for the newsletters you named.
- Click Start. All rows land in one dataset in the Output tab, where the Search results view lists keyword matches; filter the
typecolumn before you export CSV, Excel or JSON.
As input for the API:
{"searchQueries": ["artificial intelligence", "climate tech"],"searchSort": "recent","maxPostsPerSearch": 100,"publishedAfter": "2026-10-01","includeBody": true,"newsletters": ["lenny", "https://www.noahpinion.blog"],"maxPostsPerNewsletter": 20,"includeComments": true}
Over the API, text needs "includeBody": true.
Example output
A post found by the keyword artificial intelligence in a run on 11 October 2026. bodyText and bodyHtml are cut after the opening lines here, marked […]; the dataset has the whole post:
{"type": "post","key": "post:219292314","id": 219292314,"slug": "complementary-remarks-from-gary-marcus","url": "https://garymarcus.substack.com/p/complementary-remarks-from-gary-marcus","newsletter": "Marcus on AI","newsletterSubdomain": "garymarcus","newsletterUrl": "https://garymarcus.substack.com","title": "Complementary remarks from Gary Marcus and Terence Tao on OpenAI’s giant math drop","subtitle": "Making sense of a new regime in math","description": "The real news here isn’t the result; it’s what we were not told.","publishedAt": "2026-10-07T17:16:44.374Z","audience": "everyone","postType": "newsletter","section": null,"authors": [{"name": "Gary Marcus","handle": "garymarcus"}],"wordCount": 220,"likes": 284,"comments": 118,"restacks": 34,"coverImage": "https://substackcdn.com/image/fetch/$s_!fWh9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F313f319a-7df8-41d6-9c6f-0e18e43c2ce6_1206x1746.png","podcastUrl": null,"podcastDurationSeconds": null,"tags": [],"previewText": "OpenAI’s massive new math drop:","bodyText": "OpenAI’s massive new math drop:\n[This essay was written in extreme haste before a very long wifi-less flight; please forgive typos.]\nPart One: My take\nThe real news here isn’t the result; it’s not what we were told. […]","bodyHtml": "<p>OpenAI’s massive new math drop:</p> […]","bodyIsPreview": false,"searchQuery": "artificial intelligence","searchRank": 16}
Searching Substack by keyword
Top, Substack's main search, returns about 7 posts per request, ranked by relevance and popularity, and includes paid posts and posts several years old. Recent, its post search, returns 20 posts per request and favors posts from the last few weeks; with Published after set, Substack narrows the dates on its side too. In October 2026, 30 requests gave 217 posts for artificial intelligence with Top, and 577 posts for inflation with Recent, which still had more.
Substack's search also matches post bodies and related words, so not every result has the keyword in its title; Keywords (any of) keeps only those that do. A post found by several keywords appears once, under the first. Search results come without comments, and the people profiles and notes in Substack's search are not returned.
Paid posts and comments
Substack marks each post as everyone, only_paid or founding. Paid posts have the same numbers as free ones, but without a subscription Substack serves only the text above the paywall, and every author places it differently. In September 2026 a paid Noahpinion post returned 1,406 of its 4,936 words, and a paid post in Lenny's Newsletter 3,170 of 3,807. Such rows carry bodyIsPreview: true. Comments under paid posts are not served without a subscription at all: a paid post that showed 87 comments returned none.
New posts only
A daily schedule with Return only rows new since the last run turned on, as a saved task or with a Watch name, works as a new-post alert. Each run returns only rows whose key was not in the previous run: new posts in the newsletters you follow or for your keywords, new comments under the posts it reads when Include comments is on, and newsletter rows on the first run only. Rows a run could not return because of Maximum rows or its spending limit come in the next one.
How much does it cost to scrape Substack?
| Free plan | Starter | Scale | Business | |
|---|---|---|---|---|
| 1,000 posts | $0.80 | $0.64 | $0.52 | $0.40 |
| Post text, per 1,000 | $0.50 | $0.40 | $0.325 | $0.25 |
| Comment, per 1,000 | $0.30 | $0.24 | $0.195 | $0.15 |
| Newsletter, per 1,000 | $1.00 | $0.80 | $0.65 | $0.50 |
You pay only for the events in the table. There is no start fee, and compute time and proxies are included.
A keyword search that returns 50 posts costs $0.04 on the Free plan and their text $0.025 more. Twenty posts from each of 10 newsletters cost $0.16, their text another $0.10 and the 10 header rows $0.01. Search results are charged as posts, and newsletters found by search as newsletter rows. Posts removed by a filter and rows already sent in new-only mode are not charged.
Limits
- How far back each archive is read depends on Maximum posts per newsletter, counted from the newest or the most liked post according to Archive order.
- A keyword search stops at Maximum posts per keyword, at the end of Substack's results, after 100 requests, or after 10 requests in a row whose posts your filters all removed. Top waits about 2.5 seconds between requests, so 50 posts take around 20 seconds. With post text on, a run of 2 keywords and 100 posts took 2.5 minutes in October 2026.
- Deleted comments and comments Substack hides are not returned.
- An address that is not a Substack newsletter returns no rows; the
SUMMARYrecord in the key-value store names it innotes. - A run ends after 10,000 rows unless you raise Maximum rows.
FAQ
Is it legal to scrape Substack?
The actor reads what Substack shows to any visitor without an account: posts, the free part of paid posts, search results, public comments and newsletter home pages. It collects no subscriber lists or email addresses, and commenters appear only under the display name and handle they publish with. Posts belong to their authors, so you are responsible for how you use and republish them.
Can I find newsletters about a topic?
Yes. Search for the topic and turn on Include newsletters of found posts. Each newsletter behind a matching post comes back once with its author, description, subscriber tier and launch date.
Does it work with custom domains?
Yes. lenny and https://www.lennysnewsletter.com are the same newsletter: the actor follows Substack's redirect and reads from the domain the newsletter uses, which newsletterUrl shows.
Can I get exact subscriber numbers?
No. Substack publishes paid subscribers only as a tier, such as Tens of thousands of paid subscribers, and for many newsletters a rounded count, which lands in freeSubscribers: 1200000 for Lenny's Newsletter in September 2026.
Related scrapers
Support
This actor is built and maintained by deriverge s.r.o., a software company based in the Czech Republic. If a run fails or a field you need is missing, please open an issue in the Issues tab or write to us at info@deriverge.com. We respond in English and Czech. Runs can be scheduled in Apify Console or started from the API tab, which has examples for Python, JavaScript and cURL and works with Make, Zapier, n8n and the Apify MCP server. If the actor saves you time, a short review helps other people find it.