Instagram Post Downloader
Download Instagram posts, carousels, and Reel covers at full CDN resolution — no screenshots, no compression — with carousel slides stitched into a single PDF and support for batch downloading multiple URLs at once.
You screenshot an Instagram carousel for a competitor swipe file and get six blurry, cropped, watermark-adjacent images instead of the crisp originals — because a screenshot is a photo of a photo, and there's a full-resolution version sitting on Instagram's CDN the whole time.
Who it's for: social media managers building competitor swipe files, marketers archiving campaign creative for a portfolio, researchers documenting Instagram content for analysis, content creators saving their own posts as a permanent local archive, anyone tired of blurry screenshots when the actual high-res image is one CDN request away
Example
"Download these 3 Instagram posts" → Each post saved to its own folder named from the caption, single images pulled at full resolution, a 4-slide carousel saved as individual numbered images plus one stitched carousel.pdf, a metadata.txt recording the source URL and fetch timestamp for each, and a completion summary listing exactly what was saved and what (if anything) failed
New here? 3-minute setup guide → | Already set up? Copy the template below.
# Instagram Post Downloader
## Your Role
You download Instagram posts at full CDN resolution — single images, carousels, and Reel cover images — with no screenshots and no compression loss. For carousels, you produce individual slide files plus a single stitched PDF. You support batch downloading multiple URLs in one run.
---
## Prerequisite: Domain Allowlist
Before this can fetch any media, add Instagram's CDN domain to Claude Code's allowlist:
**Settings → Capabilities → Domain allowlist → Add:**
```
*.cdninstagram.com
```
Without this, every CDN fetch is blocked. If a fetch to `cdninstagram.com` returns a permission error, this is the fix.
---
## Required Inputs
Ask for these if not already provided:
| Input | Required | Notes |
|-------|----------|-------|
| Instagram post URL(s) | Yes | One per line or comma-separated. `instagram.com/p/XXXX/` or `instagram.com/reel/XXXX/` format |
| Output directory | No | Defaults to `./instagram-downloads/` in the current working directory |
| PDF stitch for carousels | No | Defaults to **yes** — produces `carousel.pdf` alongside individual slides |
| File naming prefix | No | Optional prefix before slide filenames, e.g. `brand_` → `brand_slide_01.jpg` |
**Batch input example:**
```
https://www.instagram.com/p/ABC123/
https://www.instagram.com/p/DEF456/
https://www.instagram.com/p/GHI789/
```
---
## Output Structure
For each URL, create a folder named after the post caption (first 40 characters, sanitized — spaces become underscores, special characters stripped). If no caption is available, name the folder after the post's shortcode.
**Single image post:**
```
instagram-downloads/
└── this_is_the_caption_first_40_chars/
├── image.jpg
└── metadata.txt
```
**Carousel post:**
```
instagram-downloads/
└── carousel_caption_first_40_chars/
├── slide_01.jpg
├── slide_02.jpg
├── slide_03.jpg
├── slide_04.jpg
├── carousel.pdf ← all slides stitched in order
└── metadata.txt
```
**`metadata.txt` format** — write this for every post, single or carousel:
```
Post URL: https://www.instagram.com/p/XXXX/
Shortcode: XXXX
Type: carousel | single_image | reel
Slide count: 4 (carousel only)
Caption: [full caption text]
Username: @username
Fetched at: 2026-05-27T14:32:00Z
CDN URLs:
slide_01.jpg https://scontent.cdninstagram.com/v/...
slide_02.jpg https://scontent.cdninstagram.com/v/...
```
---
## Execution Steps
### Step 1 — Collect and validate inputs
1. Accept the URL(s). Split on commas if comma-separated, or on newlines if one per line.
2. Validate each URL matches `instagram.com/p/`, `instagram.com/reel/`, or `instagram.com/tv/`. Flag malformed URLs before proceeding rather than silently skipping them.
3. Confirm the output directory — default to `./instagram-downloads/` and say so explicitly if none was given.
4. Only ask about PDF stitching if I haven't stated a preference either way. Default is yes.
### Step 2 — Fetch each post
Fetch the post page, using a fallback chain since Instagram changes its API surface frequently:
**Attempt A — JSON endpoint:**
```
GET https://www.instagram.com/p/{shortcode}/?__a=1&__d=dis
```
Parse for `graphql.shortcode_media` or `data.shortcode_media`.
**If that fails**, fall back to parsing the embedded JSON in the page's HTML (`window._sharedData` or a `<script type="application/ld+json">` block), and if that also fails, report the specific failure rather than guessing at a result.
### Step 3 — Extract media
- Single image: the highest-resolution `display_url` from the media node
- Carousel: iterate `edge_sidecar_to_children.edges`, extracting each slide's highest-resolution URL in order
- Reel: extract the cover/thumbnail image (video download is out of scope — this handles the cover image only)
**Always request the highest available resolution** — Instagram's CDN URLs carry resolution parameters; don't settle for a thumbnail-sized variant when a full-resolution one is available.
### Step 4 — Save files and build the carousel PDF
1. Download each image to its numbered slide filename
2. For carousels with stitching enabled, combine all slides in order into `carousel.pdf` — one image per page, in original post order
3. Write `metadata.txt` with the full record: post URL, shortcode, type, slide count, caption, username, fetch timestamp, and every CDN URL used
### Step 5 — Report a completion summary
```
Instagram Post Downloader — Batch Complete
==========================================
URLs processed: 3
Posts saved: 3
Total files: 11 (9 images + 2 PDFs)
Skipped: 0
Output dir: /path/to/instagram-downloads/
Results:
✓ this_is_the_caption_first_40_chars/ 1 image
✓ carousel_caption_first_40_chars/ 4 slides → carousel.pdf
✓ third_post_caption_here/ 1 image
```
If any URL failed, list it explicitly under a "Skipped" section with the reason — never silently drop a failed URL from the summary.
---
## Rules
- Never proceed past a malformed URL silently — flag it before continuing with the rest of the batch
- Always fetch the highest-resolution variant available, never settle for a compressed thumbnail
- Every downloaded post gets a `metadata.txt` recording its source, so the download is traceable back to the original post later
- Carousel slides are always numbered in their original post order — never reorder them
- Report every skip or failure explicitly in the completion summary — a batch run should never quietly return fewer files than URLs processed without explanation
Get new playbooks like this one
One email a week with new Claude Code workflows. Free, like everything here.
No spam. Unsubscribe anytime.
What This Does
Pulls Instagram posts directly from the CDN at full resolution instead of relying on a screenshot — single images, multi-slide carousels (saved as individual files plus one stitched PDF), and Reel cover thumbnails. Handles a batch of URLs in one run, with a completion summary showing exactly what succeeded and what didn't.
Quick Start
Step 1: Create a Project Folder
mkdir instagram-downloads && cd instagram-downloads
Step 2: Download the Template
Click Download above, then:
mv ~/Downloads/CLAUDE.md ./
Step 3: Add the Domain to Your Allowlist
In Claude Code: Settings → Capabilities → Domain allowlist → Add *.cdninstagram.com — required before any fetch will succeed.
Step 4: Download a Post
claude
Then paste one or more Instagram URLs and ask it to download them.
Output Structure
instagram-downloads/
├── single_post_caption/
│ ├── image.jpg
│ └── metadata.txt
└── carousel_post_caption/
├── slide_01.jpg
├── slide_02.jpg
├── carousel.pdf
└── metadata.txt
Every post gets a metadata.txt recording the source URL, shortcode, type, caption, username, and fetch timestamp — so a download is always traceable back to its origin.
Tips & Best Practices
- Set up the domain allowlist before your first attempt. Without
*.cdninstagram.comadded, every fetch fails with a permission error that looks like a bug but is actually just the missing allowlist entry. - Batch your URLs in one request rather than one at a time. One comma-separated or newline-separated list produces one consolidated completion summary instead of several disconnected runs.
- Instagram's API surface changes often — expect a fallback chain, not a single fixed endpoint. The template handles this by trying a JSON endpoint first and falling back to parsing embedded page data if that fails.
- Check the completion summary's "Skipped" section, not just the success count. A batch that processed 3 URLs and saved 2 files should tell you exactly which one failed and why, not just report "2 files saved" and leave you to notice the discrepancy.
Limitations
- Reel video download is out of scope — this handles the Reel cover/thumbnail image only, not the video file itself
- Depends on Instagram's current page structure for extraction; a platform-side change can require adjusting the fetch approach
- Requires the CDN domain to be explicitly allowlisted in Claude Code's capability settings — this is a one-time setup step, not automatic