Substack Posts API: Newsletter Posts, Likes, Comments
Pricing
from $1.00 / 1,000 post rows
Substack Posts API: Newsletter Posts, Likes, Comments
Get posts from any Substack newsletter (substack.com or custom domain): title, date, free or paid, likes, comments, word count, authors and optional full text of free posts. Public API, no login.
Pricing
from $1.00 / 1,000 post rows
Rating
0.0
(0)
Developer
Ailu Studio
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
15 hours ago
Last modified
Categories
Security
Limited permissionsShare
Substack Posts API: Newsletter Posts, Likes, Comments and Full Text
Get the posts of any Substack newsletter as clean JSON rows: title, subtitle, URL, publish time, free or paid, likes, comments, restacks, word count, authors, section, cover image and preview text, plus the optional full text of free posts. Works with name.substack.com publications and custom domains (for example newsletter.pragmaticengineer.com). Unlike the most used Substack Actor in the Store, full text of free posts costs nothing extra and there is no start fee; you can filter by free/paid and by date, and the price is $1.00 per 1,000 rows.
Use cases
- Track what competitors, analysts or creators publish and how readers react (likes, comments, restacks).
- Build a reading list or RAG corpus from the free posts of a set of newsletters.
- Compare publishing cadence, free vs paid mix and post length across newsletters.
- Schedule a daily run with
sinceDays: 1to collect new posts from many publications.
Input
| Field | Default | Meaning |
|---|---|---|
publications | ["newsletter.pragmaticengineer.com", "astralcodexten.substack.com"] | Subdomain (astralcodexten), *.substack.com address, custom domain or any post URL. |
sinceDays | 0 | Only posts from the last N days. 0 = no date limit. |
audience | all | all, free (open to everyone) or paid (paying or founding members). |
includeBody | false | Also fetch the full text of free posts into bodyText. |
maxBodiesPerPublication | 20 | Cap on full-text requests per publication, newest first. |
maxPostsPerPublication | 50 | Cap on posts per publication, newest first. |
maxItems | 200 | Cap on rows for the whole run. |
Output
One row per post, newest first per publication.
| Field | Meaning |
|---|---|
id | Stable key <publicationHost>:<postId>. |
publication, publicationId | Publication host (from the canonical URL) and Substack ID. |
title, subtitle, url, slug | Post identity and canonical link. |
publishedAt | Publish time, ISO 8601 UTC. |
postType | newsletter, podcast, thread or video. |
audience, isPaid | Substack audience value and a paid flag. |
likes, comments, restacks | Engagement counts at fetch time. |
wordCount | Word count of the full post (also for paid posts). |
authors, authorHandles | Byline names and Substack handles. |
sectionName, coverImageUrl, description, previewText, podcastUrl | Extra metadata. |
bodyText | Plain-text body of free posts when includeBody is on, else null. |
fetchedAt | When the row was fetched. |
{"id": "newsletter.pragmaticengineer.com:223351496","postId": 223351496,"publication": "newsletter.pragmaticengineer.com","publicationId": 458709,"title": "How software engineering is changing: a tale of two eras","subtitle": "We invited readers to write about how building software changed, in an essay challenge. Many of you took part, and today we’re sharing the winning piece from a strong set of entries","url": "https://newsletter.pragmaticengineer.com/p/how-software-engineering-is-changing","slug": "how-software-engineering-is-changing","publishedAt": "2026-11-03T16:58:22.397Z","postType": "newsletter","audience": "everyone","isPaid": false,"likes": 156,"comments": 2,"restacks": 11,"wordCount": 5921,"authors": ["Gergely Orosz", "Jessica Salmon", "Ivan Klaric"],"authorHandles": ["pragmaticengineer", "thejessicasalmon", "ivanklaric182781"],"sectionName": "Deepdives","coverImageUrl": "https://substackcdn.com/image/fetch/$s_!WYGs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25c874c2-9b02-4a24-9046-879cb1500023_1920x899.png","description": "We invited readers to write about how building software changed, in an essay challenge. Many of you took part, and today we’re sharing the winning piece from a strong set of entries","previewText": "Before we start: I’m in San Francisco this week and there are a few remaining spots for these live events (registration required):","podcastUrl": null,"bodyText": null,"fetchedAt": "2026-11-04T12:00:00.000Z"}
How it works and limits
- Reads each publication's archive listing (
/api/v1/archive?sort=new, up to 50 posts per request) and, for full text,/api/v1/posts/<slug>. These are the public JSON endpoints the Substack website itself uses; no login or cookies are sent. - Requests to one publication are spaced at least 0.5 s apart with retries on 429/5xx. Substack rate-limits aggressive clients, so keep
maxBodiesPerPublicationmoderate. - Paid posts are listed with full metadata, but their body is never fetched or returned (only Substack's short
previewText). - Counts (likes, comments, restacks) are a snapshot at fetch time.
- Not covered: Substack Notes, comments text, subscriber counts, profile pages (
substack.com/@name), private or deleted posts.
Pricing
Pay per result: $1.00 per 1,000 rows (result event). Platform usage is billed separately by Apify. Removed/free rows are not charged.
Legal
Not affiliated with Substack Inc. Uses the public Substack publication JSON endpoints only. Data licence/terms: https://substack.com/tos. Post texts are copyrighted by their authors; respect their rights when you republish anything.
FAQ
Does it work with custom domains?
Yes. Enter the domain (e.g. newsletter.pragmaticengineer.com); astralcodexten.substack.com style addresses are followed to their custom domain automatically.
Can I get the full text of paid posts?
No. Only free posts include bodyText; paid posts return metadata and Substack's preview line.
How do I pull a whole archive?
Set maxPostsPerPublication and maxItems high (e.g. 5000). The Actor pages through the archive newest to oldest.
Why is publication different from what I entered?
It is taken from each post's canonical URL, so astralcodexten.substack.com becomes www.astralcodexten.com. This keeps id stable however you write the input.