The best Substack APIs in 2026, tested and priced per 1,000 post reads

If you only need to know when a publication posts, use Substack RSS: it's the one post-reading route Substack documents, it needs no key, and it answered in about 0.14 seconds. If your responses need paywall and engagement fields, the undocumented Direct Substack JSON archive route returned all of them at the same speed for no fee, and you own the breakage when it changes. Of the hosted options, Apify Substack Scraper returned public body text and nested comments, and our own AnyAPI cost about five times Apify's rate at five posts a call while returning no article HTML in my test. Substack's new official Developer API doesn't change any of this, because its Authorized Data is profile and publication metadata, not post bodies.
Built on AnyAPI
363 scraping and data APIs behind one key.
Every endpoint in this post is one call away. Pay per request in USD from a prepaid wallet, with automatic failover when a provider breaks. No subscription, no card to start.
Get ~100 free requests$0.10 of credit on signup.Reading a Substack publication from code sounds like one GET. Give it a URL, get the recent posts back as JSON, write them to a table, poll again tomorrow. Substack now has an official Developer API, but its terms list public creator and publication data as Authorized Data, and post titles and bodies aren't on the list. So the job still splits across the feed, an undocumented archive route, an open-source wrapper, and hosted scrapers.
AnyAPI, which sells Substack post records by the result, is one of the five options here, and it's ours. I called every route against the same three public publications - Astral Codex Ten, Lenny's Newsletter, and Not Boring - asked each one for the five most recent posts, and timed what came back. I also installed substack-api for Python in a clean environment and ran the same reads through its objects. Where a free route or a competitor did the job better, I say so.
The short version: the free routes returned richer records faster than either paid option, and the two paid options differ a lot on both price and what content actually arrives.
The best Substack APIs for reading posts
- Substack RSS for recent-post monitoring
- Direct Substack JSON for rich reads with owned maintenance
- substack-api for Python for Python projects that value convenience
- Apify Substack Scraper for hosted archives and nested comments
- AnyAPI for normalized managed post records
What makes a good Substack post API?
I'm assuming you have a list of publications and a job that polls them, and that you're reading public posts, not publishing them. Here's what I graded on.
- Public post records without a publisher login. If a route needs you to be the publication owner, it can't read anyone else's archive.
- Archive depth and pagination. The five most recent posts is one thing. Walking a whole archive means knowing how paging works before you write the loop.
- Body, paywall, and engagement fields. Title and link are the floor. Whether a post is paywalled, how many reactions and comments it has, and whether the body text is there decide what you can build.
- Price per 1,000 returned posts, in dollars. Every vendor bills in a different unit. I normalized each one to what a thousand post records actually cost at the batch size I used.
- Response time on the same input. Three publications, five posts each, timed.
- Maintenance exposure. What happens to your job the day Substack changes a route.
The best Substack post APIs at a glance
| Tool | Best for | Standout feature | Median response, five posts | Price per 1,000 post reads |
|---|---|---|---|---|
| Substack RSS | Recent-post monitoring | Official feed path with no key | 142 ms | $0 API fee, excluding compute |
| Direct Substack JSON | Rich reads with owned maintenance | Archive metadata, content, paywall, and engagement fields | 135 ms | $0 API fee, excluding compute |
| substack-api for Python | Python projects that value convenience | Typed objects for publications and posts | 3,216 ms | $0 API fee, excluding compute |
| Apify Substack Scraper | Hosted archives and nested comments | Public content fields plus nested comment threads | 3,675 ms | $0.57 observed at 5 posts per run |
| AnyAPI | Normalized managed post records | One HTTP contract with USD metering | 4,450 ms | $1.78 at 100 per call; $2.82 at 5 per call |
Three requests per option, five recent posts each, recorded on 2026-09-02. The direct routes were timed with Node's fetch timer; the Python wrapper, Apify, and AnyAPI figures are end to end, so they include the wrapper's deliberate pause, actor startup, and CLI overhead respectively.
Substack RSS (best for recent-post monitoring)

Substack RSS pros:
- The only post-reading route here that Substack documents, and it needs no key
- All 15 records came back, each with title, URL, date, author, and content or an excerpt
- Fast: about 0.14 seconds per feed in my run
Substack RSS cons:
- No paywall, reaction, or comment fields on any of the 15 records
- No cursor, so paging is "remember what you've seen and poll again"
If your job is "tell me when this publication posts something new", this is the answer and I'd stop here. Append /feed to the publication URL, GET it, and you get RSS XML items back. Nothing to sign up for, nothing that can be revoked, and Substack's own help page tells publication owners it exists.
curl -sS https://www.astralcodexten.com/feedThe feed is a list of recent items, not an archive. There's no cursor in the response, so you store the item identifiers you've already ingested and poll for new ones. And the body field is whatever Substack chose to put in the feed, which on a paid post may be an excerpt rather than the article. If you need to know which posts are paywalled or how much engagement they got, the feed doesn't say, and that's what sends you to the next option.
Substack RSS pricing: $0 API fee. You pay for your own compute and nothing else.
Direct Substack JSON (best for rich reads with owned maintenance)

Direct Substack JSON pros:
- All eight fields I scored on all 15 records, including paywall, reactions, and comments
- The fastest median in my run, about 0.14 seconds, and no authentication for the public archive
- Real pagination: offset and limit, walk until the response is empty
Direct Substack JSON cons:
- It's not in Substack's post-reading documentation, so it can change or vanish without notice
This is the route I'd build on if I could live with owning it. Every publication host answers at /api/v1/archive with sort, search, offset, and limit query parameters, and the response is a plain JSON array of post records. The same five-post read that RSS answered with titles and excerpts came back here with the paywall flag, the reaction count, and the comment count on every record.
curl -sS 'https://www.astralcodexten.com/api/v1/archive?sort=new&search=&offset=0&limit=5'Paging is arithmetic you do yourself: request a limit, advance the offset by that limit, repeat until you get an empty array. That's the whole loop, and it means a full archive walk is possible here in a way it isn't through the feed.
The cost is that nobody promised you this endpoint. Substack documents the feed for publication owners and lists profile and publication data in its Developer API terms, and this archive route is in neither. It worked on all three publications the day I tried it. Wrap it in something you can swap out, and keep the feed as a fallback for the day it stops answering.
Direct Substack JSON pricing: $0 API fee. Your own compute, and your own maintenance.
substack-api for Python (best for Python projects that value convenience)

substack-api for Python pros:
NewsletterandPostobjects, so you never write the endpoint glue yourself- Returned all 15 requested posts, with bodies on 14 of them
- Accepts your own cookies for content you're entitled to read, and works without them for public posts
substack-api for Python cons:
- About 3.2 seconds median for a five-post read, roughly 24 times the direct JSON median
- Unofficial, and its request pattern is sequential by design
- One of the 15 records came back with no body
I installed version 1.2.0 in an isolated environment and ran it with no cookies. Newsletter.get_posts returned the posts, and the Post methods gave me metadata and content backed by the same JSON the archive route returns. The library advances the archive offsets for you, which is the convenience you're paying for.
You pay for it in wait. The package adds a courtesy pause after newsletter requests, and when you ask for a body it goes back for that post's metadata, one post at a time. Five posts took about 3.2 seconds end to end against a hair over a tenth of a second for the raw archive call. For a nightly script that's nothing. In a request path I'd call the archive endpoint directly and skip the objects.
substack-api for Python pricing: $0 API fee. A pip install and your own compute.
Apify Substack Scraper (best for hosted archives and nested comments)

Apify Substack Scraper pros:
- HTML, Markdown, and plain-text body fields populated on 14 of the 15 records
- Nested comment threads: 102 public comments came back across the six records that had them
- The cheapest managed price I measured, $0.57 per 1,000 posts at five posts a run
Apify Substack Scraper cons:
- 9 of the 15 records were tagged
preview_only; only 6 were taggedfull - About 3.7 seconds median including actor startup, roughly 27 times the direct JSON median
Apify is the one to pick when the job has to run on someone else's machine and needs the body text Substack exposes publicly, or the comments. You POST an actor run with substackUrls, maxPostsPerSubstack, includeContent, includeComments, maxCommentsPerPost, and postConcurrency, authenticated with your Apify token, and each post lands in the dataset as one item with the article text in three formats, an access status, engagement fields, publication details, and the comment thread if you asked for it. Setting maxPostsPerSubstack to 0 requests the full archive, which is the easiest archive walk on this page.
Read the content_type field before you trust the body. All three of my runs came back complete, and 14 of the 15 records had body text in them, but only 6 were tagged full and 9 were tagged preview_only. That's Substack showing the public part of a paid post, and the actor faithfully reporting it. I capped comments at 20 per post; six records had threads, and the actor collected 102 comments across them.
The price is the other reason to look here. The actor charges $0.00035 per run start plus $0.0005 per saved post on the Free plan, and my three five-post runs billed $0.00855 in total, which works out to $0.57 per 1,000 posts at that batch size.
Apify Substack Scraper pricing: $0.00035 per run start plus $0.0005 per saved post on the Free plan. Three runs of five posts each billed $0.00855, an observed $0.57 per 1,000 returned posts.
AnyAPI (best for normalized managed post records)

AnyAPI pros:
- One POST, one normalized
foundanddataenvelope, and paywall and engagement fields on every record returned - Metered in dollars per result, with no monthly plan
- Bearer key in the header, price on the endpoint page
AnyAPI cons:
includeContentreturned no HTML in any of the 15 descriptions, so I can't call full-body access verified- The slowest median in my run, about 4.5 seconds including CLI overhead, roughly 33 times the direct JSON median
- A base fee on every call, so five-post calls cost about 60 percent more per thousand than 100-post calls
The request is a POST to /v1/run/substack.posts with a publication URL, a result limit, a content type, and optional date filters, and every response has the same shape whatever publication you send. All three of my calls succeeded and every record carried the paywall and engagement fields.
curl -sS -X POST 'https://api.getanyapi.com/v1/run/substack.posts' -H "Authorization: Bearer $ANYAPI_API_KEY" -H 'Content-Type: application/json' -d '{"url":"https://www.astralcodexten.com","limit":5,"includeContent":true}'What didn't come back was article HTML. I sent includeContent on every call, and none of the 15 descriptions contained any. I'm not going to claim body coverage I didn't see, and neither should you until your own request shows the field populated. If the body is the point of your job, Apify returned it and we didn't.
The pricing is $0.0055 per call plus $0.00172 per result, and that shape rewards batching. My three test calls asked for five posts each and paid $0.0423 in total, which is $2.82 per 1,000 and about five times Apify's rate at the same batch size. The same records cost this at other batch sizes:
| Results per call | Price per 1,000 posts |
|---|---|
| 1 | $7.22 |
| 5 | $2.82 |
| 25 | $1.94 |
| 50 | $1.83 |
| 100 | $1.78 |
The endpoint page shows $1.76 per 1,000, which is the same 100-result batch rounded the other way. So raise the limit and widen the date window rather than calling once per post; there's no cursor in the response, and the limit and the date filters are how you page.
I'd pick this when you want the same normalized response and dollar metering across sources and the post metadata, paywall flag, and engagement counts are what you're storing. I wouldn't pick it for body text yet, and I'd batch.
AnyAPI pricing: $0.0055 per call plus $0.00172 per result, capped at $0.178 per call. Prepaid, metered per result, no monthly plan.
Which Substack post API should you use?
- You need new-post alerts with titles, links, dates, and authors: Substack RSS.
- You need paywall and engagement fields and can maintain an undocumented route: Direct Substack JSON.
- You're in Python and want objects instead of endpoint glue, and a few seconds per read is fine: substack-api for Python.
- You need a hosted job that returns public body text or nested comments: Apify Substack Scraper, and check
content_typeon every record. - You want one normalized managed response with dollar metering and don't need the body: AnyAPI, batched at 100 results a call.
The limits of this comparison
- Three publications and one call per publication per option is a smell test. The medians tell you the rough speed and the response shape, not uptime.
- The timers aren't equal. AnyAPI's figure includes local CLI and network overhead, Apify's includes actor startup and orchestration, and the direct routes used the Node fetch timer.
- RSS body fields may hold an excerpt rather than the full text of a paid post.
- Apify tagged 9 of 15 records
preview_onlyand 6full; body fields were populated on 14, and only six records had non-empty comment threads. - No route was tested with private subscriber data or an authenticated paid-reader session, and nothing here is about getting past a paywall.
- Prices checked on 2026-09-02. The free routes exclude whatever your own infrastructure costs to run them.
The Amazon API for web scraping comparison walks the same choice between official access and managed structured data on a different source, and the API catalog lists what's callable if you're wiring up more than one.
Use data responsibly and follow AnyAPI's Acceptable Use Policy.