Substack Newsletter, Author & Post Scraper
Pricing
from $1.00 / 1,000 extracted substack posts
Substack Newsletter, Author & Post Scraper
Extract newsletters, articles, likes, comments, author bios, and metrics from any Substack publication via direct API.
Pricing
from $1.00 / 1,000 extracted substack posts
Rating
0.0
(0)
Developer
Morgane Flamant
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
๐ฐ Substack Newsletter & Post Scraper โ Apify Actor
Extract posts, author bios, likes, comments, and engagement metrics from any Substack publication โ fast, via direct JSON API. No headless browsers. No overhead.
โจ Features
- Direct API access โ hits Substack's internal
/api/v1/archiveand/api/v1/publicationendpoints directly - Zero browser overhead โ pure async HTTP via
httpx; runs in ~128 MB RAM - Multi-publication โ scrape as many Substack slugs or URLs as you need in one run
- Pagination handled โ automatically pages through the full archive
- Clean output โ Pydantic-validated schema, ready for downstream pipelines
- Apify-native โ batched
push_data, structured input/output schemas, Docker-ready
๐ Project Structure
.โโโ .actor/โ โโโ actor.json # Actor manifestโ โโโ input_schema.json # Input configuration schemaโ โโโ output_schema.json # Output view / table schemaโโโ src/โ โโโ __init__.pyโ โโโ main.py # Apify Actor entry pointโ โโโ models.py # Pydantic data modelsโ โโโ substack_client.py # Async HTTP client + paginationโโโ Dockerfileโโโ requirements.txtโโโ README.md
๐ API Endpoints Used
| Purpose | Endpoint |
|---|---|
| Publication metadata | GET https://{slug}.substack.com/api/v1/publication |
| Posts archive | GET https://{slug}.substack.com/api/v1/archive?sort=new&limit=50&offset={n} |
All requests use standard browser headers โ no authentication required.
โ๏ธ Input Parameters
| Field | Type | Default | Description |
|---|---|---|---|
subdomains | string[] | ["lennysnewsletter", "thealgorithmicbridge"] | List of Substack slugs or full URLs to scrape |
maxPostsPerSubdomain | integer | 50 | Max posts per publication. Set to 0 for unlimited |
Example Input
{"subdomains": ["lennysnewsletter","thealgorithmicbridge","https://pragmaticengineer.substack.com"],"maxPostsPerSubdomain": 100}
๐ฆ Output Schema
Each item in the dataset represents one Substack post:
| Field | Type | Description |
|---|---|---|
id | int | Internal Substack post ID |
publication_name | str | Human-readable publication name |
publication_subdomain | str | Slug (e.g. lennysnewsletter) |
publication_url | str | Root URL of the publication |
title | str | Article title |
subtitle | str / null | Article subtitle |
slug | str | URL slug of the post |
post_url | str | Direct link to the article |
published_at | str / null | ISO 8601 publish date |
canonical_url | str / null | Canonical URL |
audience | str / null | Access tier (everyone, paid, etc.) |
cover_image_url | str / null | Cover image URL |
likes_count | int | Number of heart reactions |
comments_count | int | Number of comments |
word_count | int / null | Approximate word count |
reading_time_minutes | int / null | Estimated reading time in minutes |
authors | str[] | List of author names |
body_excerpt | str / null | Short description / excerpt |
Example Output Item
{"id": 123456789,"publication_name": "Lenny's Newsletter","publication_subdomain": "lennysnewsletter","publication_url": "https://lennysnewsletter.substack.com","title": "How to build a product people love","subtitle": "Lessons from 50 top product leaders","slug": "how-to-build-a-product-people-love","post_url": "https://lennysnewsletter.substack.com/p/how-to-build-a-product-people-love","published_at": "2024-03-15T09:00:00.000Z","canonical_url": "https://lennysnewsletter.substack.com/p/how-to-build-a-product-people-love","audience": "everyone","cover_image_url": "https://substackcdn.com/image/fetch/...","likes_count": 1842,"comments_count": 94,"word_count": 2300,"reading_time_minutes": 10,"authors": ["Lenny Rachitsky"],"body_excerpt": "After interviewing 50 of the world's best product leaders..."}
๐ Running Locally
1. Install dependencies
$pip install -r requirements.txt
2. Run as a standalone script
$python3 -m src.main
The actor will use default input (lennysnewsletter + thealgorithmicbridge, 50 posts each) when no Apify input is available.
3. Run with Docker
docker build -t substack-scraper .docker run substack-scraper
๐๏ธ Architecture Notes
src/models.py
Defines SubstackPost as a Pydantic BaseModel. All fields are strictly typed and optional where the API may omit them. model_dump(mode="json") serialises cleanly for Actor.push_data.
src/substack_client.py
clean_subdomain()normalises slugs, full URLs, and.substack.comdomainsfetch_substack_posts()is an async generator โ memory-efficient for large archives- Uses a single
httpx.AsyncClientper publication (connection pooling) - Gracefully handles publication metadata errors and empty pages
src/main.py
- Iterates over all requested subdomains sequentially
- Buffers results in batches of 25 before calling
Actor.push_datato minimise API round-trips
๐ Requirements
- Python 3.11+
apify >= 2.0.0httpx >= 0.27.0pydantic >= 2.0.0
๐ License
MIT