InstaYolo
·by torrance·dashmpdstreamingffmpegig cdn

Inside Instagram's DASH manifest — from MPD to segment to stitched MP4

The first surprise when you reverse-engineer an Instagram Reel download is that there's no single MP4 to fetch. There's an MPD file, separate video and audio representations, and a bunch of segment URLs with TTL-bound signatures. We walk through a real DASH manifest from our 2026-04-22 testing, show the fields that matter, and explain why a one-line ffmpeg command is the bridge between what Instagram stores and what ends up on your disk.

Why 'download this Reel' is harder than it sounds

Pick any Reel on Instagram, open your browser's DevTools Network tab, and watch the requests as the video plays. You won't see a single `video.mp4` being fetched. You'll see one MPD (Media Presentation Description) file, then a flurry of 2-5 second `.m4s` segments, some labeled video, some labeled audio, pulled from scontent-*.cdninstagram.com in parallel.

That's DASH — Dynamic Adaptive Streaming over HTTP, the standard Netflix helped popularize around 2012 and that every major video platform now uses. Instagram sits on top of it. Which means our downloader's job isn't just to copy a file; it's to read the MPD, pick the right representations, pull the right segments, and glue them back into a single MP4. MDN has a clean primer on the Media Source API side of the story at https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_API/Transcoding_assets_for_MSE — we're on the other end of the same pipeline.

The MPD: what's actually in it

An MPD is plain XML. Here's a trimmed, real Instagram MPD we pulled on 2026-04-22 for the @natgeo Reel DH56yy7p3lZ (we're eliding the signature tokens but keeping the structural fields):

<MPD type="static" mediaPresentationDuration="PT60.060S" minBufferTime="PT2.0S"><Period><AdaptationSet contentType="video" mimeType="video/mp4"><Representation id="v720" codecs="avc1.64001f" width="720" height="1280" bandwidth="1356000"><BaseURL>https://scontent-*.cdninstagram.com/.../v720.m4s?oe=...</BaseURL></Representation><Representation id="v360" codecs="avc1.4d001e" width="360" height="640" bandwidth="309000"/></AdaptationSet><AdaptationSet contentType="audio" mimeType="audio/mp4"><Representation id="a72" codecs="mp4a.40.5" audioSamplingRate="48000" bandwidth="72000"><BaseURL>https://scontent-*.cdninstagram.com/.../a72.m4s?oe=...</BaseURL></Representation></AdaptationSet></Period></MPD>

Three fields do most of the work. `codecs` tells a client what codec profile to expect — `avc1.64001f` is H.264 High Profile Level 3.1, which every browser and every player since 2012 handles. `bandwidth` is the mean bitrate in bits per second — the client uses it to pick which representation matches the connection speed. And `<BaseURL>` is the actual segment URL, signed with an expiry parameter that expires in about an hour.

Why video and audio are separate representations

The historical version: multiplexed MP4s (video and audio interleaved in one file) were the default until streaming platforms realized they were wasteful. Many clients could download the same video track but pick different audio languages, or pair high-quality audio with a lower-bandwidth video. Separating the two lets the client mix and match.

For Instagram specifically there's usually just one audio representation per Reel — they don't ship multiple language tracks. The separation still matters though: the video and audio streams are encoded with completely different codecs (H.264 vs HE-AAC), different framerates, different segment durations. Trying to muxes them in a single file at encode time would mean one codec dictates the segment boundary strategy of the other, which hurts streaming efficiency.

The practical consequence is what we've called out on the reels-downloader tool page: drop the video representation alone and you get a silent clip. Drop the audio alone and you get an M4A with no picture. The merge step isn't optional; it's what makes the download useful.

ffmpeg as the glue

Every download we serve passes through one ffmpeg invocation on our VPS. The command is short enough to paste here without eliding anything:

`ffmpeg -i <video-segment-url> -i <audio-segment-url> -c copy -movflags frag_keyframe+empty_moov -f mp4 pipe:1`

Three flags that matter. `-c copy` tells ffmpeg to copy both streams into the output container without re-encoding. The bytes on disk are identical to what Instagram serves — no quality loss, no generation loss, no bitrate change. `-movflags frag_keyframe+empty_moov` makes the output a fragmented MP4, which means ffmpeg can pipe bytes to the HTTP response as they're produced instead of seeking back to the file's header to write an index. Without this flag ffmpeg wants a seekable output, which a network pipe isn't. And `pipe:1` directs the bytes to stdout, which our Next.js handler streams straight to the requesting browser.

The whole thing takes 2-5 seconds on a typical Reel, most of which is fetch time from Instagram's CDN via our residential proxy. The actual remux is CPU-bound at maybe 10-15% of a single core. The bottleneck is the network.

Segment TTL and why your old Reel URLs stop working

Look again at the `oe=...` query parameter on the segment URLs in the MPD snippet. That's a Unix timestamp in hex, and when you decode it against today's production URLs you get roughly 108 hours (about 4.5 days) after the MPD was generated. After that timestamp, Instagram's CDN rejects requests to those exact segment URLs with a 403 Forbidden response. (An earlier version of this post claimed ~1 hour based on outdated assumptions — we re-measured on 2026-04-23 and the real TTL is closer to 4.5 days. We keep a separate write-up of the full URL signature anatomy at https://instayolo.com/blog/instagram-cdn-url-signature-anatomy.)

Even with a multi-day window, a naive downloader that caches the parsed MPD indefinitely will start returning 403s eventually, and you can't tell a valid URL from an expired one without making the request. Our solution is to not cache the MPD at all — every user's paste call runs a fresh /api/parse that fetches a new MPD with fresh signatures. It's a little wasteful in proxy cost but it means the download buttons the user sees always reference URLs that haven't expired. The cost tradeoff is explained in https://instayolo.com/blog/how-residential-proxies-bypass-instagram-cdn.

For the technically curious: the signing is HMAC, with a secret bound to Meta's infrastructure. The `oh=...` parameter is the actual signature, covering the path, the expiration, and request context. Anyone claiming they can forge these is selling something that breaks the first time Meta rotates the secret.

What we verified on 2026-04-22

Test: @natgeo public Reel DH56yy7p3lZ. macOS + Chrome + residential fiber. Our logs recorded the sequence:

Our /api/parse called Instagram's GraphQL endpoint at www.instagram.com, received the MPD URL, fetched the MPD (3.1 KB XML document), parsed out one video representation at 720×1280 + one at 360×640, plus one audio representation at 72 kbps HE-AAC. Total parse latency: 1308 ms.

User clicked Download 720p. Our /api/merge received the video + audio URLs, spawned ffmpeg, piped the output back through our Next.js handler. Total time click-to-file: 10,603 ms. Resulting file: 10,625,751 bytes (~10.6 MB). ffprobe on the file confirmed H.264 [email protected] avc1.64001f at 720×1280, 23.976 fps, 1.35 Mbps video bitrate, HE-AAC stereo 48 kHz audio, duration 60.060 seconds — numbers matching the MPD exactly.

Bit-for-bit, no quality loss from remux — verified via hash of the elementary streams extracted with `ffmpeg -i <file> -c copy -bsf:v h264_mp4toannexb video.h264` from both our output and a direct DASH fetch. Identical.

Why this post is longer than it needs to be

Most "how Instagram downloader works" posts stop at "we fetch the MP4". That's never been true of any modern Instagram download. The platform has used DASH since 2018, and if your downloader isn't reading an MPD it's either missing quality variants or downloading silent video. We wanted to write the post we wish we'd found when we started.

Practical takeaway for anyone building something similar: start by reading a real MPD. Paste a Reel URL into any DASH-aware tool (ExoPlayer's test harness, Shaka Player, even `yt-dlp --verbose`), grab the MPD, look at the fields. Everything downstream — residential proxy sourcing, ffmpeg invocation, CDN TTL handling — becomes obvious once you've seen the manifest once.

FAQ

Can I just download the MPD file and watch it offline?
No — an MPD is only the manifest; it contains URLs to the actual video and audio segments. Opening an .mpd file in a video player would fetch the referenced segments, which expire about an hour after the MPD was generated. To watch offline you need a downloader that fetches the segments and remuxes them into an MP4, which is what instayolo.com does.
Why not re-encode to a smaller file?
Re-encoding loses quality — even a careful 1080p → 1080p re-encode introduces H.264 quantization artifacts that weren't in the source. Our default is ffmpeg remux (`-c copy`) precisely to avoid this. If you want a smaller file after download, tools like Handbrake let you re-encode locally with full control over the tradeoff.
Do the MPD segments include subtitles?
Not for Reels or ordinary feed videos. Instagram's auto-captioning runs client-side on the user's device, not as a separate text track in the MPD. If the creator burned captions into the video (baked into the pixels, common for Reels), they'll be in the downloaded MP4. If captions were only shown in-app via Instagram's rendering layer, they won't be in the file.
Does your downloader support DASH-over-HLS fallback?
Instagram hasn't served HLS for Reels in the time we've monitored. The Live Streaming product uses a different pipeline, but that's not something we attempt to download. If Instagram switches primary delivery to HLS we'll write an update post.
Is `avc1.64001f` the only video codec Instagram ships?
Not anymore. H.264 High Profile Level 3.1 was the default through early April 2026, but on 2026-04-23 we caught Instagram rolling VP9 variants for the same Reel we'd been testing all day (vencode_tag flipped from dash_baseline_1_v1 to dash_vp9-basic-gen2 between morning and evening parse calls). Full write-up at https://instayolo.com/blog/instagram-vp9-transition-caught-live. Meta had previously tested HEVC in 2023 and AV1 in 2024, rolling both back; VP9 is the current live rollout. MDN's H.264 reference at https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/Video_codecs#avc_h.264 covers the profile/level conventions for the H.264 side.

Related tools