Skip to main content

CLI tool to download posts from Substack blogs

Project description

Substack Downloader

Simple CLI tool to download one or all the posts from a Substack blog.

Installation

Using uvx (recommended, no install needed)

uvx sbstck-dl download --url https://example.substack.com

Using pip / pipx

pip install sbstck-dl
# or
pipx install sbstck-dl

Downloading the binary

Check in the releases page for the latest version of the binary for your platform. We provide binaries for Linux, MacOS and Windows.

Using Go

go install github.com/alexferrari88/sbstck-dl

Your Go bin directory must be in your PATH. You can add it by adding the following line to your .bashrc or .zshrc:

export PATH=$PATH:$(go env GOPATH)/bin

Usage

Usage:
  sbstck-dl [command]

Available Commands:
  download    Download individual posts or the entire public archive
  help        Help about any command
  list        List the posts of a Substack
  version     Print the version number of sbstck-dl

Flags:
      --after string             Download posts published after this date (format: YYYY-MM-DD)
      --before string            Download posts published before this date (format: YYYY-MM-DD)
      --cookie_name cookieName   Either substack.sid or connect.sid, based on your cookie (required for private newsletters)
      --cookie_val string        The substack.sid/connect.sid cookie value (required for private newsletters)
  -h, --help                     help for sbstck-dl
  -x, --proxy string             Specify the proxy url
  -r, --rate int                 Specify the rate of requests per second (default 2)
  -v, --verbose                  Enable verbose output

Use "sbstck-dl [command] --help" for more information about a command.

Downloading posts

You can provide the url of a single post or the main url of the Substack you want to download.

By providing the main URL of a Substack, the downloader will download all the posts of the archive.

When downloading the full archive, if the downloader is interrupted, at the next execution it will resume the download of the remaining posts.

Usage:
  sbstck-dl download [flags]

Flags:
      --add-source-url         Add the original post URL at the end of the downloaded file
      --create-archive         Create an archive index page linking all downloaded posts
      --download-files         Download file attachments locally and update content to reference local files
      --download-images        Download images locally and update content to reference local files
  -d, --dry-run                Enable dry run
      --file-extensions string Comma-separated list of file extensions to download (e.g., 'pdf,docx,txt'). If empty, downloads all file types
      --files-dir string       Directory name for downloaded file attachments (default "files")
  -f, --format string          Specify the output format (options: "html", "md", "txt" (default "html")
  -h, --help                   help for download
      --image-quality string   Image quality to download (options: "high", "medium", "low") (default "high")
      --images-dir string      Directory name for downloaded images (default "images")
  -o, --output string          Specify the download directory (default ".")
  -u, --url string             Specify the Substack url

Global Flags:
      --after string    Download posts published after this date (format: YYYY-MM-DD)
      --before string   Download posts published before this date (format: YYYY-MM-DD)
      --cookie_name cookieName   Either substack.sid or connect.sid, based on your cookie (required for private newsletters)
      --cookie_val string        The substack.sid/connect.sid cookie value (required for private newsletters)
  -x, --proxy string    Specify the proxy url
  -r, --rate int        Specify the rate of requests per second (default 2)
  -v, --verbose         Enable verbose output

Adding Source URL

If you use the --add-source-url flag, each downloaded file will have the following line appended to its content:

original content: POST_URL

Where POST_URL is the canonical URL of the downloaded post. For HTML format, this will be wrapped in a small paragraph with a link.

Downloading Images

Use the --download-images flag to download all images from Substack posts locally. This ensures posts remain accessible even if images are deleted from Substack's CDN.

Features:

  • Downloads images at optimal quality (high/medium/low)
  • Creates organized directory structure: {output}/images/{post-slug}/
  • Updates HTML/Markdown content to reference local image paths
  • Handles all Substack image formats and CDN patterns
  • Graceful error handling for individual image failures

Examples:

# Download posts with high-quality images (default)
sbstck-dl download --url https://example.substack.com --download-images

# Download with medium quality images
sbstck-dl download --url https://example.substack.com --download-images --image-quality medium

# Download with custom images directory name
sbstck-dl download --url https://example.substack.com --download-images --images-dir assets

# Download single post with images in markdown format
sbstck-dl download --url https://example.substack.com/p/post-title --download-images --format md

Image Quality Options:

  • high: 1456px width (best quality, larger files)
  • medium: 848px width (balanced quality/size)
  • low: 424px width (smaller files, mobile-optimized)

Directory Structure:

output/
├── 20231201_120000_post-title.html
└── images/
    └── post-title/
        ├── image1_1456x819.jpeg
        ├── image2_848x636.png
        └── image3_1272x720.webp

Downloading File Attachments

Use the --download-files flag to download all file attachments from Substack posts locally. This ensures posts remain accessible even if files are removed from Substack's servers.

Features:

  • Downloads file attachments using CSS selector .file-embed-button.wide
  • Optional file extension filtering (e.g., only PDFs and Word documents)
  • Creates organized directory structure: {output}/files/{post-slug}/
  • Updates HTML content to reference local file paths
  • Handles filename sanitization and collision avoidance
  • Graceful error handling for individual file download failures

Examples:

# Download posts with all file attachments
sbstck-dl download --url https://example.substack.com --download-files

# Download only specific file types
sbstck-dl download --url https://example.substack.com --download-files --file-extensions "pdf,docx,txt"

# Download with custom files directory name
sbstck-dl download --url https://example.substack.com --download-files --files-dir attachments

# Download single post with both images and file attachments
sbstck-dl download --url https://example.substack.com/p/post-title --download-images --download-files --format md

File Extension Filtering:

  • Specify extensions without dots: pdf,docx,txt
  • Case insensitive matching
  • If no extensions specified, downloads all file types

Directory Structure with Files:

output/
├── 20231201_120000_post-title.html
├── images/
│   └── post-title/
│       ├── image1_1456x819.jpeg
│       └── image2_848x636.png
└── files/
    └── post-title/
        ├── document.pdf
        ├── spreadsheet.xlsx
        └── presentation.pptx

Creating Archive Index Pages

Use the --create-archive flag to generate an organized index page that links all downloaded posts with their metadata. This creates a beautiful overview of your downloaded content, making it easy to browse and access your Substack archive.

Features:

  • Creates index.{format} file matching your selected output format (HTML/Markdown/Text)
  • Links to all downloaded posts using relative file paths
  • Displays post titles, publication dates, and download timestamps
  • Shows post descriptions/subtitles and cover images when available
  • Automatically sorts posts by publication date (newest first)
  • Works with both single post and bulk downloads

Examples:

# Download entire archive and create index page
sbstck-dl download --url https://example.substack.com --create-archive

# Create archive index in Markdown format
sbstck-dl download --url https://example.substack.com --create-archive --format md

# Build archive over time with single posts
sbstck-dl download --url https://example.substack.com/p/post-title --create-archive

# Complete download with all features
sbstck-dl download --url https://example.substack.com --download-images --download-files --create-archive

# Custom directory structure with archive
sbstck-dl download --url https://example.substack.com --create-archive --images-dir assets --files-dir attachments

Archive Content Per Post:

  • Title: Clickable link to the downloaded post file
  • Publication Date: When the post was originally published on Substack
  • Download Date: When you downloaded the post locally
  • Description: Post subtitle or description (when available)
  • Cover Image: Featured image from the post (when available)

Archive Format Examples:

HTML Format: Styled webpage with images, organized post cards, and hover effects Markdown Format: Clean markdown with headers, links, and image references Text Format: Plain text listing with all metadata for maximum compatibility

Directory Structure with Archive:

output/
├── index.html                     # Archive index page
├── 20231201_120000_post-title.html
├── 20231115_090000_another-post.html
├── images/
│   ├── post-title/
│   │   └── image1_1456x819.jpeg
│   └── another-post/
│       └── image2_848x636.png
└── files/
    ├── post-title/
    │   └── document.pdf
    └── another-post/
        └── spreadsheet.xlsx

Listing posts

Usage:
  sbstck-dl list [flags]

Flags:
  -h, --help         help for list
  -u, --url string   Specify the Substack url

Global Flags:
      --after string    Download posts published after this date (format: YYYY-MM-DD)
      --before string   Download posts published before this date (format: YYYY-MM-DD)
      --cookie_name cookieName   Either substack.sid or connect.sid, based on your cookie (required for private newsletters)
      --cookie_val string        The substack.sid/connect.sid cookie value (required for private newsletters)
  -x, --proxy string    Specify the proxy url
  -r, --rate int        Specify the rate of requests per second (default 2)
  -v, --verbose         Enable verbose output

Private Newsletters

In order to download the full text of private newsletters you need to provide the cookie name and value of your session. The cookie name is either substack.sid or connect.sid, based on your cookie. To get the cookie value you can use the developer tools of your browser. Once you have the cookie name and value, you can pass them to the downloader using the --cookie_name and --cookie_val flags.

Example

sbstck-dl download --url https://example.substack.com --cookie_name substack.sid --cookie_val COOKIE_VALUE

Thanks

  • wemoveon2 and lenzj for the discussion and help implementing the support for private newsletters

TODO

  • Improve retry logic
  • Implement loading from config file
  • Add support for downloading images
  • Add support for downloading file attachments
  • Add archive index page functionality
  • Add tests
  • Add CI
  • Add documentation
  • Add support for private newsletters
  • Implement filtering by date
  • Implement resuming downloads

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

sbstck_dl-0.7.0-py3-none-win_arm64.whl (8.7 MB view details)

Uploaded Python 3Windows ARM64

sbstck_dl-0.7.0-py3-none-win_amd64.whl (9.4 MB view details)

Uploaded Python 3Windows x86-64

sbstck_dl-0.7.0-py3-none-musllinux_1_2_x86_64.whl (9.1 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

sbstck_dl-0.7.0-py3-none-musllinux_1_2_aarch64.whl (8.5 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

sbstck_dl-0.7.0-py3-none-manylinux_2_17_x86_64.whl (9.1 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

sbstck_dl-0.7.0-py3-none-manylinux_2_17_aarch64.whl (8.5 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

sbstck_dl-0.7.0-py3-none-macosx_11_0_arm64.whl (8.8 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

sbstck_dl-0.7.0-py3-none-macosx_10_9_x86_64.whl (9.3 MB view details)

Uploaded Python 3macOS 10.9+ x86-64

File details

Details for the file sbstck_dl-0.7.0-py3-none-win_arm64.whl.

File metadata

  • Download URL: sbstck_dl-0.7.0-py3-none-win_arm64.whl
  • Upload date:
  • Size: 8.7 MB
  • Tags: Python 3, Windows ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for sbstck_dl-0.7.0-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 da7c2ba587b852410c7c63d468c38287e63f9008223853708849a0080d6b4cfd
MD5 802c474f661c761ff5b1916a9b54b854
BLAKE2b-256 47a65066ce94b2aa7e47e6b97a04107a6b6f9bd292db76e0499dd35a4cf8c97c

See more details on using hashes here.

File details

Details for the file sbstck_dl-0.7.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: sbstck_dl-0.7.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 9.4 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for sbstck_dl-0.7.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 e0dba6980d70e1f710a0ed978c63d7fc1dbcd491a9439d00f5fc157c54c022a5
MD5 ff58c24e1c276121b24c5f80d18c7b0c
BLAKE2b-256 81f0032b8036e9f66fe16ec4bc6839e95e38bbf17b83140186f1c5f4a61ee10f

See more details on using hashes here.

File details

Details for the file sbstck_dl-0.7.0-py3-none-musllinux_1_2_x86_64.whl.

File metadata

  • Download URL: sbstck_dl-0.7.0-py3-none-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 9.1 MB
  • Tags: Python 3, musllinux: musl 1.2+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for sbstck_dl-0.7.0-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 3dcaa57bac82b0b2b64f5ec2dd06ea1f028af30da90a7e5a2ef56829c9f5f284
MD5 a46fbbaddbcd1014049fa34ca06d00e8
BLAKE2b-256 eebbd5fdcbf04990fd1468a5ff02f84f36d6ea98c86a815a893833833ea91631

See more details on using hashes here.

File details

Details for the file sbstck_dl-0.7.0-py3-none-musllinux_1_2_aarch64.whl.

File metadata

  • Download URL: sbstck_dl-0.7.0-py3-none-musllinux_1_2_aarch64.whl
  • Upload date:
  • Size: 8.5 MB
  • Tags: Python 3, musllinux: musl 1.2+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for sbstck_dl-0.7.0-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 ecd188e21b8448c2567a0f6d63e31d8b1320fe64eb8f3c4d992b611bcdbd29e0
MD5 6106598a9d72fb0d7c79dae050e6ed8c
BLAKE2b-256 a89596b247cb86c6fcd5fe4d2bc86a3df0793f0e608d816136363f6eab98ca53

See more details on using hashes here.

File details

Details for the file sbstck_dl-0.7.0-py3-none-manylinux_2_17_x86_64.whl.

File metadata

  • Download URL: sbstck_dl-0.7.0-py3-none-manylinux_2_17_x86_64.whl
  • Upload date:
  • Size: 9.1 MB
  • Tags: Python 3, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for sbstck_dl-0.7.0-py3-none-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 2304dcd354e35fdc68bc2cea8e57923de9785e1b11c6a4bbf0fd58b0d968cf19
MD5 f1cff930ed0f30a97f1ddda25592950d
BLAKE2b-256 2923a59b833c2eb3c9830913c0706bce1bf340645d3d2bfe2260daeb757d5f3d

See more details on using hashes here.

File details

Details for the file sbstck_dl-0.7.0-py3-none-manylinux_2_17_aarch64.whl.

File metadata

  • Download URL: sbstck_dl-0.7.0-py3-none-manylinux_2_17_aarch64.whl
  • Upload date:
  • Size: 8.5 MB
  • Tags: Python 3, manylinux: glibc 2.17+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for sbstck_dl-0.7.0-py3-none-manylinux_2_17_aarch64.whl
Algorithm Hash digest
SHA256 183e3fed01c19c0fea461d40cc4312ba4f4e19a614d608acfb574bb789d67d19
MD5 08effc3bed61846364eac3d2969e2c44
BLAKE2b-256 763f1add31a8df165b03c4f45ff42fc3f5cb521a8dc30504616075250546ea02

See more details on using hashes here.

File details

Details for the file sbstck_dl-0.7.0-py3-none-macosx_11_0_arm64.whl.

File metadata

  • Download URL: sbstck_dl-0.7.0-py3-none-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 8.8 MB
  • Tags: Python 3, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for sbstck_dl-0.7.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b8d4d4610290f8aca1fe8da8c4dd669f31da3df0519ea7a8685a27aad9489bd9
MD5 2f814769e3ab690f39f596b0ded9f89b
BLAKE2b-256 3f433d8d13135b70faf6ff1a5c5e3f683d12ce18a64dc5daaa0914ba268a6d8e

See more details on using hashes here.

File details

Details for the file sbstck_dl-0.7.0-py3-none-macosx_10_9_x86_64.whl.

File metadata

  • Download URL: sbstck_dl-0.7.0-py3-none-macosx_10_9_x86_64.whl
  • Upload date:
  • Size: 9.3 MB
  • Tags: Python 3, macOS 10.9+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for sbstck_dl-0.7.0-py3-none-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 95bf1decd9d2745ac36b4773f42bb54da7d08bd10c48ddc70951e5a3c5e30fe5
MD5 39e3b9ca49366bba4de6e2cdae613f22
BLAKE2b-256 4505f3d9ce23f3928b3766c55173b627f58afd6e1a8e6f0ffae2e9b097902e4c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page