Substack Newsletter, Author & Post Scraper avatar

Substack Newsletter, Author & Post Scraper

Pricing

from $1.00 / 1,000 extracted substack posts

Go to Apify Store
Substack Newsletter, Author & Post Scraper

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

Morgane Flamant

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

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/archive and /api/v1/publication endpoints 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

PurposeEndpoint
Publication metadataGET https://{slug}.substack.com/api/v1/publication
Posts archiveGET https://{slug}.substack.com/api/v1/archive?sort=new&limit=50&offset={n}

All requests use standard browser headers โ€” no authentication required.


โš™๏ธ Input Parameters

FieldTypeDefaultDescription
subdomainsstring[]["lennysnewsletter", "thealgorithmicbridge"]List of Substack slugs or full URLs to scrape
maxPostsPerSubdomaininteger50Max 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:

FieldTypeDescription
idintInternal Substack post ID
publication_namestrHuman-readable publication name
publication_subdomainstrSlug (e.g. lennysnewsletter)
publication_urlstrRoot URL of the publication
titlestrArticle title
subtitlestr / nullArticle subtitle
slugstrURL slug of the post
post_urlstrDirect link to the article
published_atstr / nullISO 8601 publish date
canonical_urlstr / nullCanonical URL
audiencestr / nullAccess tier (everyone, paid, etc.)
cover_image_urlstr / nullCover image URL
likes_countintNumber of heart reactions
comments_countintNumber of comments
word_countint / nullApproximate word count
reading_time_minutesint / nullEstimated reading time in minutes
authorsstr[]List of author names
body_excerptstr / nullShort 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.com domains
  • fetch_substack_posts() is an async generator โ€” memory-efficient for large archives
  • Uses a single httpx.AsyncClient per 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_data to minimise API round-trips

๐Ÿ“‹ Requirements

  • Python 3.11+
  • apify >= 2.0.0
  • httpx >= 0.27.0
  • pydantic >= 2.0.0

๐Ÿ“„ License

MIT