Substack Public Posts API
Undocumented but public Substack endpoint listing a publication's posts as JSON, with titles, dates, reaction counts, word counts and paywall status. Tested example included.
Endpoint tested and returned HTTP 200 on 2026-08-21
What is the Substack Public Posts API?
Every Substack publication serves its post list as JSON at `/api/v1/posts` on its own domain, with no API key. Each post returns its title, subtitle, slug, publication date, audience setting, reaction and comment counts, word count and cover image.
Substack has never shipped a documented public API, but the endpoint its own web front-end calls is open on every publication and needs nothing to read. Point it at any Substack domain — the `*.substack.com` subdomain or a custom domain like the one in the example — and the post list comes back as JSON, ordered newest first.
The metadata is richer than the equivalent RSS feed by a wide margin. `audience` tells you whether a post is public or subscriber-only; `wordcount`, `reactions`, `restacks` and `comment_count` give you engagement figures RSS simply does not carry; `truncated_body_text` provides a preview even for paywalled posts. The obvious caveat is that undocumented means unstable — the field set can change without warning, and Substack's terms govern what you do with the content regardless of how easy it is to fetch. Custom-domain publications redirect off `*.substack.com`, so follow redirects or use the canonical domain directly.
Quick facts
- Base URL
https://www.astralcodexten.com/api/v1- Authentication
- No API key. Paywalled post bodies stay truncated without a subscriber session; metadata for them is still returned.
- Rate limit
- No published limit. It backs a live site — keep requests slow and cache results.
- Pricing
- Free to read. Content remains the publication's copyright.
- CORS
- Not enabled — call it from your server
- Official docs
- Read the docs
How to use the Substack Public Posts API
Every request below was executed against the live API on 2026-08-21, and the response shown is the real body it returned — not an illustration.
1. List a publication's most recent posts
GET https://www.astralcodexten.com/api/v1/posts?limit=1
curl 'https://www.astralcodexten.com/api/v1/posts?limit=1'const res = await fetch("https://www.astralcodexten.com/api/v1/posts?limit=1");
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
const data = await res.json();
console.log(data);import requests
res = requests.get("https://www.astralcodexten.com/api/v1/posts?limit=1", timeout=20)
res.raise_for_status()
print(res.json())[
{
"audience": "everyone",
"audience_before_archived": null,
"canonical_url": "https://www.astralcodexten.com/p/college-ea-meetups-everywhere-call",
"default_comment_sort": null,
"editor_v2": false,
"exempt_from_archive_paywall": false,
"free_unlock_required": false,
"id": 212052999,
"podcast_art_url": null,
"podcast_duration": null,
"podcast_preview_upload_id": null,
"podcast_upload_id": null,
"podcast_url": null,
"post_date": "2026-08-20T19:55:39.806Z",
"updated_at": "2026-08-20T19:58:27.477Z",
"publication_id": 89120,
"search_engine_description": null,
"search_engine_title": null,
"section_id": null,
"should_send_free_preview": false,
"show_guest_bios": true,
"slug": "college-ea-meetups-everywhere-call",
"social_title": "College EA Meetups Everywhere",
"subtitle": "...",
"teaser_post_eligible": true,
"title": "College EA Meetups Everywhere: Call For Organizers",
"type": "newsletter",
"video_upload_id": null,
"write_comment_permissions": "everyone",
"meter_type": "none",
"live_stream_id": null,
"is_published": true,
"restacks": 9,
"reactions": {
"❤": 76
},
"top_exclusions": [],
"pins": [],
"section_pins": [],
"has_shareable_clips": false,
"previous_post_slug": "why-im-staying-out-of-the-substack",
"next_post_slug": null,
"cover_image": "https://substack-post-media.s3.amazonaws.com/public/images/27645a54-8b36-4494-9413-62d22a675cd7_1999x1268.png",
"cover_image_is_square": false,
"cover_imageParameters
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | query | Optional | How many posts to return. Twelve by default; large values are throttled. 1 |
offset | query | Optional | How many posts to skip, for paging back through the archive. 12 |
sort | query | Optional | `new` for reverse-chronological, `top` for most popular. new |
(publication) | base URL | Required | The publication's own domain — either `name.substack.com` or its custom domain. https://www.astralcodexten.com |
Response fields
(root)array- Posts, newest first. There is no envelope object.
title / subtitle / slugstring- Headline, standfirst and URL slug. Build the post URL from `canonical_url`, which is already absolute.
post_date / updated_atstring- ISO 8601 UTC timestamps for first publication and last edit.
audiencestring- `everyone` for public posts, `only_paid` for subscriber-only. This is how you detect the paywall.
typestring- `newsletter`, `podcast` or `thread` — determines which of the podcast and audio fields are populated.
wordcountinteger- Word count of the full post, present even when the body is truncated.
reactions / reaction_count / restacksobject / integer- Engagement figures. `reactions` is a map keyed by reaction type.
comment_count / child_comment_countinteger- Top-level comments and replies.
truncated_body_textstring- Plain-text preview. For paywalled posts this is all you get.
cover_image / coverImagePalettestring / object- Header image URL and its extracted colour palette.
publishedBylinesarray- Authors, with names, handles and profile images.
What you can build with the Substack Public Posts API
- Mirror a newsletter's archive into your own site or search index
- Track publishing cadence and engagement across several publications
- Build a reading list spanning multiple Substacks in one place
- Detect which posts are public before linking to them
Common errors and how to fix them
Redirect to a different host
The publication uses a custom domain.
Fix: Follow redirects, or call the canonical domain directly. `*.substack.com` 301s to the custom domain when one is configured.
Body text is cut off
The post is subscriber-only.
Fix: `audience` will read `only_paid`. Metadata and `truncated_body_text` are all that is available without a subscriber session.
Fields disappear after a Substack deploy
The endpoint is undocumented and covered by no compatibility promise.
Fix: Read defensively, treat every field as optional, and do not build anything load-bearing on a specific key.
Requests start failing
You are hitting infrastructure that serves a live site.
Fix: Slow down, cache, and page with `offset` rather than requesting huge limits.
Substack Public Posts API — frequently asked questions
Is there an official Substack API?
No. This endpoint powers Substack's own front-end and is publicly readable, but it is undocumented and unsupported, so treat its stability as a courtesy rather than a guarantee.
Can I read paywalled posts through it?
No. Posts marked `audience: only_paid` return metadata and a truncated preview only. The full body needs a subscriber session.
Which URL do I use for a publication?
Its own domain. `example.substack.com/api/v1/posts` works, and publications on a custom domain redirect there — the example uses a custom domain directly to skip the hop.
Is it legal to use this data?
Reading public metadata is straightforward, but the content is copyright the publication and Substack's terms of service apply. Republishing post bodies is a very different proposition from linking to them.
Tools that pair with this API
RSS Feed Generator
Build a valid RSS 2.0 XML feed from channel details and item rows with title, link, description and pubDate. Copy or download the ready-to-publish feed.
Word Counter
Count words, characters, sentences, paragraphs and estimated reading time instantly. Free online word counter for essays, blogs and social media.
Strip HTML Tags
Remove all HTML tags and convert markup to clean plain text. Decode HTML entities and keep line breaks for block elements like paragraphs and headings.
Markdown Link Extractor
Extract every link from Markdown into a table of anchor text, URL and title. Splits internal from external and flags duplicates and empty anchors.
Substack Public Posts is an independent third-party service and is not affiliated with ByteTools or ByteVancer. Details on this page were verified on 2026-08-21; always check the official documentation before relying on this API in production, as terms and limits can change.