Substack Posts API
Pull posts from any Substack publication by its URL, or pass a single post URL (…/p/slug) to fetch just that one article. Returns title, subtitle, publish date, paywall status, word count, engagement (reactions, comments, restacks), author profile, publication details, the full article body as text, HTML and Markdown, and optional comment threads.
$0.439604/1k results, pay per request, no subscription.
{"postId": "148302911","title": "Why roadmaps are guesses","url": "https://www.clearheaded.blog/p/why-roadmaps-are-guesses","slug": "why-roadmaps-are-guesses","postType": "newsletter","createdUtc": 1758585600,"wordcount": 2140,"authorName": "Priya Nakamura",... 22 more fields}
Pricing
Substack Posts API pricing
Try it
Get Substack posts in one request
This sample shows 58 of 69 fields. See every field
{
"found": true,
"data": {
"items": [
{
"postId": "148302911",
"title": "Why roadmaps are guesses",
"url": "https://www.clearheaded.blog/p/why-roadmaps-are-guesses",
"slug": "why-roadmaps-are-guesses",
"postType": "newsletter",
"createdUtc": 1758585600,
"wordcount": 2140,
"authorName": "Priya Nakamura",
"authorHandle": "priyanakamura",
"contentStatus": "full",
"text": "Every roadmap I have shipped was a guess with a deadline attached.",
"publication": {
"id": "pub_5521",
"name": "Clearheaded",
"url": "https://www.clearheaded.blog",
"subdomain": "clearheaded",
"customDomain": "www.clearheaded.blog",
"description": "Notes on building things without lying to yourself.",
"image": "https://substackcdn.com/image/fetch/clearheaded/logo.png",
"language": "en",
"paymentsEnabled": true,
"subscriberCount": 38400
},
"comments": [
{
"commentId": "c_88213",
"text": "This matches every planning meeting I have sat through for a decade.",
"createdUtc": 1758600000,
"authorName": "Devon Okafor",
"authorHandle": "devonokafor",
"authorUrl": "https://substack.com/@devonokafor",
"authorImage": "https://substackcdn.com/image/fetch/clearheaded/devon-avatar.png",
"reactionCount": 44,
"restackCount": 2,
"isPinned": true,
"isAuthor": false,
"replies": [
{
"commentId": "c_88240",
"text": "Glad it landed. Curious how your team names the guess out loud.",
"createdUtc": 1758603600,
"authorName": "Priya Nakamura",
"authorHandle": "priyanakamura",
"authorUrl": "https://www.clearheaded.blog/people/priya-nakamura",
"authorImage": "https://substackcdn.com/image/fetch/clearheaded/priya-avatar.png",
"reactionCount": 12,
"restackCount": 0,
"isPinned": false,
"isAuthor": true
}
]
}
]
},
{
"postId": "148190442",
"title": "The three-question standup",
"url": "https://www.clearheaded.blog/p/the-three-question-standup",
"slug": "the-three-question-standup",
"subtitle": "",
"description": "A shorter standup that actually surfaces risk",
"postType": "newsletter",
"createdUtc": 1757980800,
"isPaid": true,
"language": "en",
"wordcount": 1420,
"reactionCount": 501,
"commentCount": 29,
"replyCount": 6,
"restackCount": 18,
"authorName": "Priya Nakamura",
"authorHandle": "priyanakamura",
"authorUrl": "https://www.clearheaded.blog/people/priya-nakamura",
"contentStatus": "preview_only",
"text": "Most standups answer the same three questions badly. Here is a version that takes six minutes and actually works...",
"publication": {
"id": "pub_5521",
"name": "Clearheaded",
"url": "https://www.clearheaded.blog",
"subdomain": "clearheaded"
},
"comments": []
},
{
"postId": "148075310",
"title": "Ask me anything: hiring your first PM",
"url": "https://www.clearheaded.blog/p/ask-me-anything-hiring-your-first-pm",
"slug": "ask-me-anything-hiring-your-first-pm",
"subtitle": "Reader questions from last week's thread",
"description": "Reader questions from last week's thread",
"postType": "thread",
"createdUtc": 1757376000,
"isPaid": false,
"language": "en",
"wordcount": 640,
"reactionCount": 203,
"commentCount": 41,
"replyCount": 9,
"restackCount": 5,
"authorName": "Priya Nakamura",
"authorHandle": "priyanakamura",
"authorUrl": "https://www.clearheaded.blog/people/priya-nakamura",
"contentStatus": "metadata_only",
"publication": {
"id": "pub_5521",
"name": "Clearheaded",
"url": "https://www.clearheaded.blog",
"subdomain": "clearheaded"
},
"comments": []
}
]
}
}Full parameter and response reference - every field, type, and example for this endpoint.
Reference
One request, one response shape.
Every field you send and every field you get back, each with a real example.
Last verified 2026-10-31 · uptime and latency measured over 30d
Request body
JSON, posted to this endpoint.
- urlstring
https://www.clearheaded.blog
Either a Substack publication URL / custom domain to fetch its recent posts (e.g. https://www.astralcodexten.com), OR a single post URL to fetch just that one article with full content (e.g. https://www.astralcodexten.com/p/your-book-review).
- contentTypeenum
Restrict to a single post type, or 'all' (e.g. newsletter).
- endDatestring
Only return posts published on or before this date, format YYYY-MM-DD (e.g. 2024-12-31). Applied within the most recent 'limit' posts scanned.
- includeCommentsboolean
Include public comment threads and their direct replies on each post (e.g. true).
- includeContentboolean
true
Include the full article body as text, HTML and Markdown. Set false for metadata only, which is faster (e.g. false).
- limitinteger
3
Maximum number of recent posts to return when given a publication URL (1-100, default 25); ignored for a single post URL, which always returns that one post. You are billed per post returned, so a lower limit costs less.
- maxCommentsinteger
Maximum comments collected per post when 'includeComments' is true (0-500, default 20). Costs nothing extra (e.g. 50).
- minCommentsinteger
Only return posts with at least this many comments (e.g. 10).
- minReactionsinteger
Only return posts with at least this many reactions (e.g. 100).
- minWordCountinteger
Only return posts with at least this many words, which filters out short notes and announcements (e.g. 1000).
- onlyFreeboolean
Return only free (non-paywalled) posts (e.g. true).
- startDatestring
Only return posts published on or after this date, format YYYY-MM-DD (e.g. 2024-01-01). Applied within the most recent 'limit' posts scanned, so raise 'limit' to reach older date ranges.
Response
JSON, one example value per field.
Post records: title, subtitle, URL, publish date, paywall status, word count, engagement (reactions, comments, restacks), author profile, publication details, the article body as text, HTML and Markdown, and comment threads when requested. Populated whenever the provider has data for the entity.
- authorBiostring
Author bio as shown on their Substack profile.
- authorHandlestring
priyanakamura
Substack handle of the post author. Populated whenever the provider has data for the entity.
- authorImagestring
Profile photo URL of the post author.
- authorNamestring
Priya Nakamura
Display name of the post author. Populated whenever the provider has data for the entity.
- authorUrlstring
Substack profile URL of the post author.
- commentCountinteger
Number of top-level comments on the post.
- contentStatusstring
full
How much of the article body this record carries: 'full' for the whole article, 'preview_only' for the public excerpt of a paywalled post, 'metadata_only' when no body was requested or available, or 'failed' when extraction failed. Read this before trusting 'text', 'html', or 'markdown'. Populated whenever the provider has data for the entity.
- createdUtcnumber
1758585600
UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds. Populated whenever the provider has data for the entity.
- descriptionstring
Short post description, usually the subtitle or an excerpt.
- hasVoiceoverboolean
Whether the post carries a narrated audio version.
- htmlstring
Article body as HTML. Present when 'includeContent' is true and 'contentStatus' is 'full' or 'preview_only'.
- imagestring
Cover image URL.
- isPaidboolean
Whether the post is behind a paywall.
- languagestring
Two-letter language code of the post.
- markdownstring
Article body as Markdown. Present when 'includeContent' is true and 'contentStatus' is 'full' or 'preview_only'.
- podcastUrlstring
Audio URL for a podcast post or a narrated voiceover, when the post has one.
- postIdstring
148302911
Substack post identifier. Populated whenever the provider has data for the entity.
- postTypestring
newsletter
Post type (newsletter, podcast, or thread). Populated whenever the provider has data for the entity.
- reactionCountinteger
Number of reactions (likes) on the post.
- replyCountinteger
Number of replies to comments on the post.
- restackCountinteger
Number of times the post was restacked.
- slugstring
why-roadmaps-are-guesses
Post slug, the last path segment of the post URL. Populated whenever the provider has data for the entity.
- subtitlestring
Post subtitle or deck.
- textstring
Every roadmap I have shipped was a guess with a deadline attached.
Article body as plain text. Present when 'includeContent' is true and 'contentStatus' is 'full' or 'preview_only'.
- titlestring
Why roadmaps are guesses
Post title. Populated whenever the provider has data for the entity.
- updatedUtcnumber
UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds.
- urlstring
https://www.clearheaded.blog/p/why-roadmaps-are-guesses
Canonical post URL. Populated whenever the provider has data for the entity.
- wordcountinteger
2140
Approximate word count of the article.
Top-level comment threads on the post, each with its direct replies. Empty unless 'includeComments' is true. Replies nested more than one level deep are not returned.
- authorHandlestring
devonokafor
Substack handle of the comment author.
- authorImagestring
https://substackcdn.com/image/fetch/clearheaded/devon-avatar.png
Profile photo URL of the comment author.
- authorNamestring
Devon Okafor
Display name of the comment author.
- authorUrlstring
https://substack.com/@devonokafor
Substack profile URL of the comment author.
- commentIdstring
c_88213
Substack comment identifier.
- createdUtcnumber
1758600000
UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds.
- editedUtcnumber
UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds.
- isAuthorboolean
false
Whether the comment was written by the post author.
- isPinnedboolean
true
Whether the comment is pinned by the publication.
- reactionCountinteger
44
Number of reactions on the comment.
- restackCountinteger
2
Number of times the comment was restacked.
- textstring
This matches every planning meeting I have sat through for a decade.
Comment body text.
Direct replies to this comment.
- authorHandlestring
priyanakamura
Substack handle of the reply author.
- authorImagestring
https://substackcdn.com/image/fetch/clearheaded/priya-avatar.png
Profile photo URL of the reply author.
- authorNamestring
Priya Nakamura
Display name of the reply author.
- authorUrlstring
https://www.clearheaded.blog/people/priya-nakamura
Substack profile URL of the reply author.
- commentIdstring
c_88240
Substack comment identifier.
- createdUtcnumber
1758603600
UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds.
- editedUtcnumber
UTC epoch timestamp in seconds (Unix time). Multiply by 1000 for a JS Date in milliseconds.
- isAuthorboolean
true
Whether the reply was written by the post author.
- isPinnedboolean
false
Whether the reply is pinned by the publication.
- reactionCountinteger
12
Number of reactions on the reply.
- restackCountinteger
0
Number of times the reply was restacked.
- textstring
Glad it landed. Curious how your team names the guess out loud.
Reply body text.
The publication the post belongs to.
- customDomainstring
www.clearheaded.blog
Custom domain the publication is served on, when it has one.
- descriptionstring
Notes on building things without lying to yourself.
Publication tagline or hero text.
- idstring
pub_5521
Substack publication identifier.
- imagestring
https://substackcdn.com/image/fetch/clearheaded/logo.png
Publication logo URL.
- languagestring
en
Two-letter language code of the publication.
- namestring
Clearheaded
Publication name.
- paymentsEnabledboolean
true
Whether the publication sells paid subscriptions.
- subdomainstring
clearheaded
Publication subdomain on substack.com.
- subscriberCountinteger
38400
Subscriber count, when the publication publishes it.
- urlstring
https://www.clearheaded.blog
Publication home URL.
- foundboolean
true
Whether any posts were found for the request.
- reason"not_found"
Present only when `found` is false, and says why there is no result. `not_found`: the source states the target does not exist, or returned nothing for it. A `found: false` answer is a successful call, not an error, and `costUsd` is what it actually cost.
FAQ
About the Substack Posts API
The AnyAPI Substack Posts API returns Substack posts data as normalized JSON from one POST call to /v1/run/substack.posts. Pull posts from any Substack publication by its URL, or pass a single post URL (…/p/slug) to fetch just that one article. Returns title, subtitle, publish date, paywall status, word count, engagement (reactions, comments, restacks), author profile, publication details, the full article body as text, HTML and Markdown, and optional comment threads. AnyAPI returns one normalized schema whichever source serves it. It costs $0.44 per 1,000 results, and never more than $0.0444 for a single request, in US dollars with no subscription and no monthly minimum. Over the last 30 days, 94.1% of Substack posts calls through AnyAPI succeeded, with a median response time of 3.3 seconds across 304 measured calls.