Pinterest media downloader CLI.
✨ Inspired by pinterest-dl.
pnpm i -g pin-dl # or npm i -g pin-dl
# Download a board — bare URL, no subcommand needed
pin-dl https://www.pinterest.com/user/board-name/ -o ./output
# Search — bare query, no subcommand needed
pin-dl "dark academia interiors" -o ./refs -n 200
# Words without quotes also work:
pin-dl dark academia interiors -o ./refs -n 200
# Log in for private boards
pin-dl login
# Download a board section
pin-dl https://www.pinterest.com/user/board/ --section "Kitchen Ideas" -o ./kitchen
# Resume an interrupted download
pin-dl download board.json -o ./output --resume
loginOpens a browser to log in to Pinterest and saves session cookies for use with private boards.
pin-dl login [options]
| Option | Alias | Default | Description |
|---|---|---|---|
--output |
-o |
cookies.json |
Path to save the cookies file |
--client |
chromium |
Browser engine: chromium | firefox | webkit |
|
--browser-executable |
Path to a custom browser binary (skips Playwright install check). Also reads pin-dl_BROWSER_EXECUTABLE env var. |
||
--headful |
false | Show the browser window | |
--incognito |
false | Use an isolated browser context | |
--wait |
10 |
Seconds to wait after login for cookies to settle | |
--verbose |
false | Enable verbose output |
The browser binary is downloaded automatically on first use if not found (requires confirmation). To install manually:
npx playwright install chromium
undefinedExample:undefined
pin-dl login -o cookies.json --headful --wait 20
# On success, prints the next step:
# Use: pin-dl scrape <url> -o ./output --cookies cookies.json
scrapeScrapes images (and optionally videos) from Pinterest board, section, or pin URLs.
pin-dl <url>... # bare URL shortcut — no subcommand needed
pin-dl get <url>... # alias for scrape
pin-dl similar <url> # alias for scrape --related
pin-dl scrape <url>... [options]
pin-dl scrape --file urls.txt [options]
undefinedShortcuts: Bare URLs and
getboth dispatch toscrape.similardispatches toscrape --related.
| Option | Alias | Default | Description |
|---|---|---|---|
--output |
-o |
Output directory for downloaded files. If omitted, prints JSON to stdout. | |
--file |
-f |
File containing URLs, one per line. Use - for stdin. |
|
--stdin |
false | Read URLs from stdin (alias for --file -) |
|
--cookies |
-c |
Path to cookies JSON for private boards | |
--limit |
-n |
100 |
Maximum number of items to scrape |
--min-res |
-r |
Minimum resolution filter, e.g. 512x512 — skips smaller images |
|
--section |
Board section to scrape by name, e.g. --section "Kitchen Ideas" |
||
--related |
false | For pin URLs: scrape related/similar pins instead | |
--video |
false | Download video streams if available | |
--skip-remux |
false | Skip ffmpeg remux; output raw .ts file |
|
--reencode |
false | Force re-encode HLS to MP4 (slower; overrides --skip-remux) |
|
--timeout |
10 |
Request timeout in seconds | |
--delay |
0.2 |
Delay between requests in seconds | |
--save-urls |
Save scraped URLs to a JSON file for later use with pin-dl download |
||
--filename |
{id} |
Filename template. Variables: {id}, {alt}, {index}. See below. |
|
--dry-run |
false | Print found URLs to stdout without downloading anything | |
--skip-existing |
--resume, --continue |
false | Skip files that already exist in the output directory |
--caption |
none |
Caption format: txt | json | metadata | none |
|
--ensure-cap |
false | Only include items that have alt text | |
--cap-from-title |
false | Use image title as caption instead of auto_alt_text |
|
--verbose |
false | Print each downloaded URL | |
--dump |
Save raw API request/response pairs to a directory for debugging | ||
--log-file |
Append all log output to this file |
undefinedExamples:undefined
# Bare URL — no subcommand needed
pin-dl https://www.pinterest.com/user/board-name/ -o ./output --limit 200
# Download a specific board section
pin-dl https://www.pinterest.com/user/home-ideas/ --section "Kitchen" -o ./kitchen
# Find similar pins (alias: pin-dl similar <url>)
pin-dl https://www.pinterest.com/pin/123456789/ --related -n 50 -o ./similar
# Batch from a file; read from stdin
pin-dl scrape --file urls.txt -o ./output -c cookies.json
cat urls.txt | pin-dl --stdin -o ./output
# Save scraped URLs for later download (no download yet)
pin-dl https://www.pinterest.com/user/board/ --save-urls board.json
# Filter by minimum resolution
pin-dl scrape <url> -o ./output --min-res 1024x768
# Dry run — inspect URLs without downloading
pin-dl https://www.pinterest.com/user/board/ --dry-run
# Resume after interruption
pin-dl scrape <url> -o ./output --resume
# Download videos
pin-dl scrape <url> -o ./output --video
searchSearches Pinterest for a keyword and downloads results.
pin-dl "query" # bare query shortcut — no subcommand needed
pin-dl dark academia interiors # unquoted words are joined into one query
pin-dl search <query>... [options]
pin-dl search --file queries.txt [options]
undefinedShortcut: A bare string (no
https://, no subcommand) automatically dispatches tosearch. Multiple unquoted words are joined into a single query.
Accepts the same options as scrape (see table above).
undefinedExamples:undefined
# Bare query — no subcommand needed
pin-dl "minimalist interior" -o ./output -n 50
# Multiple queries processed sequentially
pin-dl search "cats" "dogs" -o ./output
# Search from a file of queries
pin-dl search --file queries.txt -o ./output --save-urls results.json
downloadDownloads media from a previously saved --save-urls file.
pin-dl download <cache.json> [options]
Passing a URL instead of a cache file prints a helpful error pointing to the correct command.
| Option | Alias | Default | Description |
|---|---|---|---|
--output |
-o |
filename without extension | Output directory |
--min-res |
-r |
Minimum resolution filter, e.g. 512x512 |
|
--filename |
{id} |
Filename template. Variables: {id}, {alt}, {index} |
|
--video |
false | Download video streams if available | |
--skip-remux |
false | Skip ffmpeg remux; output raw .ts file |
|
--reencode |
false | Force re-encode HLS to MP4 | |
--skip-existing |
--resume, --continue |
false | Skip files that already exist in the output directory |
--caption |
none |
Caption format: txt | json | metadata | none |
|
--ensure-cap |
false | Only download items that have alt text | |
--verbose |
false | Print each downloaded file path | |
--log-file |
Append all log output to this file |
undefinedExamples:undefined
# Download from a saved URL list
pin-dl download board.json -o ./output
# Resume an interrupted download
pin-dl download board.json -o ./output --resume
# Download only captioned items
pin-dl download board.json -o ./output --ensure-cap --caption txt
# 1. Log in and save cookies
pin-dl login -o cookies.json
# 2. Scrape a private board using the cookies
pin-dl scrape https://www.pinterest.com/user/private-board/ -o ./output -c cookies.json
When you run pin-dl scrape <url> -o <dir>, scraped URLs are automatically saved to <dir>.json alongside the download. If the download is interrupted:
# Option 1: resume from the auto-saved URL list
pin-dl download ./output.json -o ./output --resume
# Option 2: re-run the scrape with --resume (re-fetches URLs, skips existing files)
pin-dl scrape <url> -o ./output --resume
If you used --save-urls explicitly, that file takes precedence over the auto-save.
undefinedCtrl+C hint: pin-dl prints the exact resume command when interrupted.
By default, files are named by their Pinterest pin ID:
<pin_id>.jpg (or .png, etc. based on source)<pin_id>.mp4 (or <pin_id>.ts if ffmpeg is missing)<pin_id>.txt or <pin_id>.jsonUse --filename to customise the naming pattern:
| Variable | Value |
|---|---|
{id} |
Pinterest pin ID (default) |
{alt} |
Sanitized alt text (spaces → _, special chars stripped). Falls back to {id} if empty. |
{index} |
1-based position in the batch |
# Sequential numbering
pin-dl <url> -o ./output --filename "{index}-{id}"
# → 1-123456.jpg, 2-789012.jpg, ...
# Name by alt text
pin-dl <url> -o ./output --filename "{alt}"
# → dark_moody_aesthetic.jpg, minimalist_desk_setup.jpg, ...
The --caption option writes metadata alongside each downloaded file:
| Mode | Output |
|---|---|
txt |
<filename>.txt containing alt text |
json |
<filename>.json containing {src, alt, origin} |
metadata |
Attempts to embed EXIF metadata (shows warning if unsupported) |
none |
No caption files (default) |
Use --cap-from-title to prefer the pin title over auto_alt_text.
Use --ensure-cap to skip items that have no caption text.
Pinterest pins may include HLS (.m3u8) video streams. Pass --video to download them.
.mp4 using ffmpeg (must be in PATH).--skip-remux to save the raw .ts segment file instead.--reencode to force a full re-encode to MP4 (slower, maximum compatibility).Save scraped media metadata to a JSON file with --save-urls, then download later:
pin-dl <url> --save-urls board.json # scrape only, save URLs
pin-dl download board.json -o ./output # download from saved list
pin-dl download board.json -o ./output --resume # resume if interrupted
This is useful for inspecting results before downloading, or deferring large downloads.
You need vite-plus installed to use vp globally.
After cloning the repository, run:
vp install
vp run build # outputs to dist/
node dist/index.mjs # run directly
We use cookies
We use cookies to analyze traffic and improve your experience. You can accept or reject analytics cookies.