Substack Archive Scraper | Public Posts & Clean Text avatar

Substack Archive Scraper | Public Posts & Clean Text

Pricing

from $0.75 / 1,000 substack posts

Go to Apify Store
Substack Archive Scraper | Public Posts & Clean Text

Substack Archive Scraper | Public Posts & Clean Text

Export public Substack newsletter archives and post URLs with dates, authors, engagement and optional public text. Custom domains, filters, deduplication and continuation. Paid posts return metadata and short preview only.

Pricing

from $0.75 / 1,000 substack posts

Rating

0.0

(0)

Developer

Apivault Labs

Apivault Labs

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

12 hours ago

Last modified

Share

Substack Archive Scraper — Public Posts & Clean Text

Substack Archive Scraper — generated newsletter archive illustration

Turn public newsletter archives into structured post data for content research, publication audits and editorial monitoring. Supply Substack publication URLs, custom domains or specific post URLs. Export titles, source dates, author names, available engagement and optional public HTML/clean text to CSV, Excel or JSON.

This is an independent tool. It does not log in, access subscriber accounts or unlock paid articles. A restricted post returns metadata and a short source preview only. Keyword discovery, comments, subscriber counts, audio downloads and immutable archive snapshots are outside this version.

First export: five articles

{"urls":["https://www.experimental-history.com"],"maxItems":5}

Defaults request up to five metadata rows, at most 20 archive pages. The limit is global across inputs. Real output depends on matching posts, source availability, your spending cap and run resources. Some publishers or regions can return403/429; an empty Dataset alone does not prove an empty archive.

What it does

GoalSupported result
Audit a newsletter archivePost IDs, canonical URLs, titles, source publication dates and audience flags.
Export a reading corpusPublic/free bodyHtml and bodyText if returned by the anonymous source.
Track a publication windowInclusive start/end date filters plus available author/engagement fields.
Compare several newslettersUp to20inputs, one global result limit and cross-input identity deduplication.
Resume a capped collectionCONTINUATION.resumeInput keeps source offsets and previously accepted IDs.

Input settings

SettingDefault / limitMeaning
urlsOne sample publication;1–20Public HTTPS homepage, /archive, or /p/post-slug.
maxItems5; maximum10000Maximum useful post rows across all inputs.
includeContentfalseReturn public body HTML/text when present; never paid-body content.
onlyFreefalseSkip restricted audience/unlock-required posts.
startDate / endDateomittedInclusive source post_date filters, YYYY-MM-DD.
maxPages20; maximum5000Global cap on archive/post input pages; up to20posts per archive page. Requested public content can add one detail request per accepted free post.
cursoromittedCopy from continuation only. Keep URLs/content/date/free settings unchanged.

Five ready-to-copy scenarios

Public text corpus

{"urls":["https://www.experimental-history.com"],"maxItems":5,"includeContent":true,"onlyFree":true}

Several publications with one limit

{"urls":["https://www.experimental-history.com","https://www.lennysnewsletter.com"],"maxItems":10}

Publication date window

{"urls":["https://www.experimental-history.com"],"startDate":"2026-09-01","endDate":"2026-10-21","maxItems":10,"maxPages":10}

Specific article URL

{"urls":["https://www.experimental-history.com/p/there-is-no-epidemic-of-loneliness"],"maxItems":1,"includeContent":true}

Metadata from a custom domain

{"urls":["https://www.lennysnewsletter.com/archive"],"maxItems":5,"includeContent":false}

Five corresponding saved Tasks are prepared for verification. Public one-click Task availability must be checked separately; private Tasks are not published templates.

Output fields

Field groupWhat it means
postId, publicationId, urlSource identity; publicationId/postId deduplicate overlapping inputs.
title, subtitle, authors, tagsSource editorial metadata; optional values can be absent.
publishedAt, updatedAtSource timestamps; updatedAt is unknown when not supplied.
isPaid, audience, contentStatusAccess classification; paid_preview_only never contains bodyHtml/bodyText.
previewTextShort available preview, capped at1000characters.
bodyHtml, bodyTextOnly requested public/free body, without an assertion that it is the complete copyrighted article.
reactionCount, commentCount, restackCountAvailable source counters, not extracted comments. Missing values remain unknown.
sourceUrl, scrapedAtActual anonymous endpoint and observation time.

No sample customer data or fabricated article output is inserted into the Dataset. wordCount is the source's article count when provided; it can exceed the available public text length. contentStatus distinguishes not_requested, public_body_returned, public_body_unavailable and paid_preview_only.

Larger archives and continuation

Use asynchronous runs for larger exports. Increase maxItems and maxPages within the schema; allow enough time and memory. This is a finite, bounded exporter, not a promise of unlimited archive depth or a500Kexport. Public content can require an additional identity-checked detail request per accepted free post. The first source version reads pages sequentially and writes accepted rows as they are processed.

When capped by items/pages/budget, read CONTINUATION.resumeInput in the run's Key-Value Store and submit that object as the next Actor input. Previously accepted IDs in the cursor prevent repeated billing across an unchanged continuation chain up to its100000IDwindow. Source updates can move offsets: this is not an immutable snapshot and cannot guarantee no omissions during concurrent index changes. Also deduplicate at your destination. Independent runs begin a new collection.

Check SUMMARY.isComplete and ERRORS. COMPLETE means the requested source list was exhausted without recorded source errors, not that every historical Substack post exists in the source. ITEM_LIMIT_REACHED, PAGE_LIMIT_REACHED and CHARGE_LIMIT_REACHED retain continuation. A source failure preserves its current offset; mixed results can be useful but incomplete. A complete source outage fails the run with no fabricated paid rows.

Pricing and run resources

Candidate result tariff: $1.00 per1,000 post rows on Free; $0.75 per1,000 on all paid tiers. Metadata and requested public body use the same post event; there is no extra body event. Actor Start is $0.00005 per GB of memory, minimum one; platform resources are additional and paid by the user. Read the Pricing tab for effective account rates.

Useful post rows are the only lead/result event. Source errors, duplicates, filtered-out posts and diagnostics are not billed as posts. Actor platform resources can still apply on empty or failed runs. An available paid-post metadata row is still a useful result and can be billed; it is not a full paid article. Downloading an existing Dataset in another format does not start a new export.

Start with512MB and900seconds for a small export. A source response is limited to8MB, individual requests to25seconds and redirects are checked for public HTTPS destinations. The source may block this service or return incomplete text. No external API key, proxy subscription or publisher login is required by this version; reliability and constant speed are not guaranteed.

API, MCP and integrations

Actor slug: apivault_labs/substack-archive-scraper. Start an asynchronous Actor run, wait for a terminal status, read the default Dataset and its diagnostics, and download Dataset items. Keep your Apify token in secure client credentials.

Official Python client, JavaScript client and Apify MCP can call this exact slug with its input. They are generic clients, not dedicated packages for this product. No matching dedicated npm/PyPI package is currently verified. n8n/Make can use HTTP integration; execution and n8n verification are not claimed.

FAQ

Does it bypass a paywall? No. Restricted/unlock-required posts are metadata plus a short preview only; body fields are omitted even if an anonymous endpoint happens to supply them.

Can I search all Substack by keyword? Not in this version. Supply known publications or post URLs. A keyword option must not silently substitute an unrelated source.

Does it support custom domains? It queries the public Substack archive/post shape at the supplied domain and follows safe redirects. A custom domain that no longer uses Substack is reported as an error, not scraped as an unrelated website.

Why are there fewer rows than maxItems? Date/free filters, overlap, source exhaustion, request/page limits and spending cap can all reduce the result. Inspect the summary and continuation.

Are missing counters zero? No. Unknown source fields are null, not guessed zeros. Comments themselves and subscriber counts are not collected.

Is this a monitoring snapshot? No. Continuation is offset-based with identity deduplication. Store your own observations by source identity to compare runs; editing/deletion history is not reconstructed.