Save any public Dropbox DocSend deck as a PDF — one command, full quality.
pip install docsend-dl && playwright install chromium
docsend-dl https://docsend.com/view/XXXXXXThat's it. You'll get a PDF named after the deck in your current directory.
$ docsend-dl https://dbx.docsend.com/view/n43v89r
Found deck: "docsend-n43v89r" (21 slides)
Got 21/21 image URLs
Downloading slides ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 21/21 0:00:00
╭──────────────────────── Done in 32.2s ─────────────────────────╮
│ Slides saved: 21/21 │
│ PDF size: 9.5 MB │
│ Output: /home/user/docsend-n43v89r.pdf │
╰────────────────────────────────────────────────────────────────╯
- PDF by default — slides are assembled into a single PDF at full resolution with no re-encoding
- PNG export — pass
--imagesto save individual slide images instead - Smart output paths — pass a
.pdffilename, a directory, or let it default to{deck title}.pdf - Fast — downloads all slides in parallel with automatic retries
- Works with both
docsend.comanddbx.docsend.comURLs - Headless — runs in the background by default; use
--no-headlessto watch the browser
You need the package itself plus a Chromium browser for Playwright:
pip install docsend-dl
playwright install chromiumOr if you use uv:
uv tool install docsend-dl
uv run playwright install chromium# Download as PDF (default)
docsend-dl https://docsend.com/view/XXXXXX
# Save PDF to a specific path
docsend-dl https://docsend.com/view/XXXXXX --output "My Deck.pdf"
# Save PDF into a directory (uses deck title as filename)
docsend-dl https://docsend.com/view/XXXXXX --output ./downloads
# Download as individual PNG images instead
docsend-dl https://docsend.com/view/XXXXXX --images
# Show the browser window during download
docsend-dl https://dbx.docsend.com/view/XXXXXX --no-headlessimport asyncio
from docsend_dl import download_deck
# Download as PDF (default)
result = asyncio.run(download_deck(
url="https://docsend.com/view/XXXXXX",
))
print(f"Saved PDF to {result.output_path} ({result.total_bytes} bytes)")
# Download as individual images
result = asyncio.run(download_deck(
url="https://docsend.com/view/XXXXXX",
output="My Deck",
images_only=True,
))
print(f"Saved {result.successes}/{result.slide_count} slides")For finer control you can call the extraction and download steps separately:
import asyncio
from pathlib import Path
from docsend_dl import extract_slide_urls, download_slides
async def main():
deck_info = await extract_slide_urls(url="https://docsend.com/view/XXXXXX")
result = await download_slides(
urls=deck_info.image_urls,
output_dir=Path("slides"),
)
print(f"Downloaded {result.successes} slides")
asyncio.run(main())- Opens the DocSend page in a headless Chromium browser (via Playwright)
- Extracts each slide's image URL from the page data
- Downloads all slide images in parallel (via httpx)
- Assembles the images into a single PDF (via img2pdf) or saves them as PNGs
- Only works with public decks — email-gated and passcode-protected decks are not supported
- Requires Chromium to be installed via
playwright install chromium
# Clone and install dev dependencies
git clone https://github.com/captivus/docsend-dl.git
cd docsend-dl
uv sync
# Run unit tests (fast, no network needed)
uv run pytest -m "not integration"
# Run integration tests (downloads real decks, verifies checksums)
uv run pytest tests/test_integration.py
# Run everything
uv run pytestBug reports and pull requests are welcome on GitHub.
Releases are cut from the project root with release X.Y.Z (or release for an interactive version prompt). The tool bumps pyproject.toml, commits, tags, pushes -- which triggers the PyPI publish workflow via GitHub Actions Trusted Publishing. Release notes auto-generate from commit messages between tags, so commits should read well as changelog entries.