Subhoard archives a Substack publication to yearly Markdown digests, PDFs, or HTML email. Public posts work without credentials. Subscriber content can be archived using a browser session exported by a user who is authorized to access it.
Use Subhoard only for content you are permitted to access and retain. Do not redistribute subscriber content without the publisher's permission.
- Markdown and PDF archives grouped by year
- HTML email delivery through any standard SMTP provider
- Public-only mode with no cookies
- Optional subscriber-content mode using an exported Substack session
- Resumable, private local cache
- Date filtering and dry-run previews
- Multiple output formats in one run
- Python 3.9 or newer
- A supported Camoufox browser
- A valid subscription for any subscriber content being archived
git clone https://github.com/pabooth/subhoard.git
cd subhoard
python3 -m venv .venv
source .venv/bin/activate
python -m pip install .
python -m camoufox fetchFor development, replace python -m pip install . with:
python -m pip install -e ".[dev]"Archive public posts as yearly Markdown files:
subhoard --url https://example.substack.comPreview matching posts without writing output:
subhoard --url https://example.substack.com --dry-runCreate Markdown and PDF output:
subhoard \
--url https://example.substack.com \
--output digest \
--output pdfThe original source invocation remains supported:
python subhoard.py --url https://example.substack.comRun subhoard --help for every option.
Subscriber mode is for content the current user is authorized to access.
-
Export a Netscape-format
cookies.txtfile from an authenticatedsubstack.combrowser session using a local-only cookie export tool. -
Restrict access to the file:
chmod 600 cookies.txt -
Run:
subhoard \ --url https://example.substack.com \ --subscriber-content \ --cookies-file cookies.txt
Cookie exports grant access to your account. Never commit or share them. Revoke the browser session if an export may have been exposed.
SMTP passwords are intentionally accepted only through the
SUBHOARD_SMTP_PASSWORD environment variable or a hidden interactive prompt.
export SUBHOARD_SMTP_PASSWORD='your-app-password'
subhoard \
--url https://example.substack.com \
--output email \
--smtp-host smtp.example.com \
--smtp-port 587 \
--smtp-username you@example.com \
--from-address 'Archive <you@example.com>' \
--to-address 'You <you@example.com>'The default SMTP security mode is STARTTLS. Use --smtp-security ssl for
implicit TLS, commonly on port 465.
Command-line arguments take precedence over their corresponding environment variables.
| Variable | Purpose |
|---|---|
SUBHOARD_URL |
Publication root URL |
SUBHOARD_OUTPUT |
Comma-separated digest, pdf, and/or email |
SUBHOARD_COOKIES_FILE |
Cookie export path |
SUBHOARD_START_DATE |
Earliest post date in YYYY-MM-DD format |
SUBHOARD_FETCH_DELAY |
Delay between archive requests |
SUBHOARD_EMAIL_DELAY |
Delay between emails |
SUBHOARD_CACHE_DIR |
Resumable cache directory |
SUBHOARD_DIGEST_DIR |
Markdown output directory |
SUBHOARD_PDF_DIR |
PDF output directory |
SUBHOARD_LOG_FILE |
Optional log path |
SUBHOARD_SMTP_HOST |
SMTP hostname |
SUBHOARD_SMTP_PORT |
SMTP port |
SUBHOARD_SMTP_USERNAME |
SMTP username |
SUBHOARD_SMTP_PASSWORD |
SMTP password |
SUBHOARD_SMTP_SECURITY |
starttls or ssl |
SUBHOARD_FROM_ADDRESS |
Sender address |
SUBHOARD_TO_ADDRESS |
Recipient address |
By default, Subhoard creates:
post_cache/for resumable contentdigest_export/for Markdownpdf_export/for PDF files
These paths are ignored by Git and created with owner-only permissions on POSIX systems. They may contain subscriber content, so handle backups and cloud synchronisation accordingly.
python -m pip install -e ".[dev]"
python -m unittest discover -s tests -v
python -m py_compile subhoard.py tests/test_subhoard.py
ruff check subhoard.py tests
python -m buildSee CONTRIBUTING.md, SECURITY.md, and SUPPORT.md.
MIT © 2026 Paul Booth. See LICENSE.
