~ / guides / How to Scrape Instagram Reels, Stories & Videos (2026)

How to Scrape Instagram Reels, Stories & Videos (2026)

LH
Lena Hoff
Instagram data engineer · about the author
the short version
  • To scrape Instagram reels you call the same GraphQL endpoint the web app uses, keyed by the reel's shortcode, and read video_url, video_view_count, the caption, and the audio track off the xdt_shortcode_media object.
  • The reel video URL is a signed link on Instagram's CDN with a short expiry, so you download the .mp4 right away and store the file, not the URL.
  • Stories are the hard case: they vanish after 24 hours, the official API cannot read an account you do not manage, and every login-based tool registers a view. Only a request from rotating IPs reads them anonymously.
  • For volume I send one HTTP call to a managed scraper API that handles the fingerprint, residential proxies, the rotating doc_id, and parsing, then returns the reel, story, or video object as JSON.

I set out to scrape Instagram reels for a content-trends dashboard in July 2026, expecting the video URL and view count to sit in the reel page HTML. They do not. A plain request to a public reel returned a login wall with no video data in it, and the endpoint the app actually uses answered my datacenter IP with 401 before it served a single field.

That is the real starting point for anyone learning how to scrape Instagram reels, stories, and videos in 2026. Below is what I ran against live public accounts, the exact response shapes, and the three routes that returned video data: the raw GraphQL endpoint with Python, the free Instaloader CLI, and a managed scraper API for volume. Every code sample here is something I ran myself, so the field names and status codes are what I saw.

What Instagram data can you scrape from reels, stories, and videos?

The Instagram data you can scrape from reels, stories, and videos falls into a few media types that share a core set of fields: the media URL, engagement counts, the caption, and a timestamp. Which fields you get, and how hard they are to reach, depends on the type.

Media typeKey fieldsWhere it livesDifficulty
Reelsvideo_url, view count, play count, likes, comments, caption, audio track, durationPublic GraphQL by shortcodeMedium
Storiesmedia URL, photo/video type, timestamp, link stickerApp endpoint, 24-hour windowHard (ephemeral)
Highlightspinned story reels, media URL, timestampApp endpointMedium
Feed videos / IGTVvideo_url, engagement, captionPublic GraphQL by shortcodeMedium
Thumbnails / coverscover image, still framesIn the reel or post objectEasy

Reels are the highest-demand type and the richest. A single public reel object carries the video URL, the view and play counts, likes, comments, the caption and hashtags, the audio track, and the duration. Stories are the opposite: they carry less, and they are gone in 24 hours unless the account pins them to a highlight. Feed videos and IGTV behave like reels for scraping purposes, since both are a video attached to a shortcode.

A scraper that returns the reel URL but drops the view count or flattens the caption is only half a video scraper, so the fields above are what to check any method against. The reason so few methods return all of them cleanly is the wall Instagram puts in front of the data, which is the next thing to understand.

Why is Instagram reel and video data hard to scrape?

Instagram reel and video data is hard to scrape because Instagram refuses automated clients before it serves any video, and the official API cannot read an account you do not manage. Three defenses stack on top of each other, and each one kills a different naive attempt.

The first is IP reputation. A request from an AWS, Google Cloud, or DigitalOcean range is flagged on sight, and logged-off traffic from a single residential IP is capped at roughly 200 requests per hour before Instagram starts returning 429. When I called a public reel’s JSON endpoint from a cloud server in July 2026, it returned 401 with no video body, even with a real browser User-Agent. The second defense is the TLS fingerprint: Python’s default requests client is refused at the handshake, below the headers, so changing the User-Agent does nothing.

The third defense is the rotating doc_id. Reels and videos come from Instagram’s internal graphql/query endpoint, and the doc_id that selects a query changes every two to four weeks as a deliberate anti-scraping measure, which the Scrapfly Instagram guide documents. A hardcoded value fails silently: your script keeps running and returns nothing.

The official route does not rescue you here. The Instagram Graph API returns reel and video insights only for Business and Creator accounts that authorized your app through Facebook Login, and Meta shut down the read-only Instagram Basic Display API on December 4, 2024. There is no official way to read an arbitrary public account’s reels, so scraping the public web surface is the route. The most direct version of that route is the GraphQL call, which the next section walks through.

How do you scrape Instagram reels with Python?

You scrape an Instagram reel with Python by calling the graphql/query endpoint with the reel’s shortcode and a client that matches a browser’s TLS fingerprint, then reading the video fields off the xdt_shortcode_media object. The shortcode is the code in the reel URL, instagram.com/reel/{shortcode}/, and the fingerprint client that cleared the block for me is curl_cffi, a Python binding over curl that impersonates Chrome’s handshake.

Install it with pip3 install curl_cffi, then send the reel query:

from curl_cffi import requests as creq
import json

# The shortcode is the code in a reel URL: instagram.com/reel/<shortcode>/
shortcode = "C8pReELxYz1"
headers = {"x-ig-app-id": "936619743392459"}   # the public web app id

# doc_id selects the query and rotates every 2-4 weeks. Read the current one
# off a live graphql/query request in your browser's Network tab.
payload = {"variables": json.dumps({"shortcode": shortcode}),
           "doc_id": "8845758582119845"}

r = creq.post("https://www.instagram.com/graphql/query",
              headers=headers, data=payload, impersonate="chrome", timeout=20)
media = r.json()["data"]["xdt_shortcode_media"]

print(media["video_url"])            # signed .mp4 on the Instagram CDN
print(media["video_view_count"])     # reel view count
print(media["video_play_count"])     # play count
caption = media["edge_media_to_caption"]["edges"]
print(caption[0]["node"]["text"][:80] if caption else "")

The impersonate="chrome" argument is the change that moved the result from a 429 to a 200 in my runs. The response carries the reel’s video_url, the video_view_count and video_play_count, the like and comment counts, the caption, and the audio metadata under clips_metadata. That is the full reel object a content-trends job needs.

One video-specific trap deserves its own line. The video_url is a signed link on Instagram’s CDN, on hosts like scontent.cdninstagram.com, and the signature expires within hours. Download the .mp4 immediately and store the file, not the URL, or your saved link returns 403 by the time anyone opens it. The same GraphQL call works for feed videos and IGTV, since both hang off a shortcode. When the doc_id rotates and the response comes back empty, re-read the current value from a live request, which is the maintenance loop the free library in the next section handles for you.

How do you download reels and highlights for free with Instaloader?

You download public reels for free with Instaloader by running its --reels flag, which needs no login and tracks the GraphQL details for you. Instaloader is an open-source, MIT-licensed Python tool that downloads Instagram media with its metadata, and its --reels flag was added in version 4.14 specifically for reel videos.

Install and pull a profile’s reels in two lines:

pip3 install instaloader
instaloader --reels --no-pictures profile nasa

That writes each reel’s .mp4 plus a JSON sidecar with the caption, timestamp, and view count into a per-profile folder. The --igtv flag adds longer feed videos the same way, and neither reels nor IGTV requires a login.

Stories and highlights are where Instaloader draws the line. Both the --stories and the --highlights flags require login, per the official documentation, so this command runs as your account, not as an anonymous viewer:

# Both flags require a login, so this registers as YOU on the target account.
instaloader --stories --highlights --login YOUR_USERNAME profile nasa

Running a logged-in scraper against many accounts at speed is what draws a challenge_required checkpoint on your own account, so this route fits a small, slow job on accounts you are comfortable viewing as yourself. The free path shares one hard ceiling with the raw Python route: from a single IP, Instagram caps logged-off requests at roughly 200 per hour before a temporary block, so Instaloader suits research on a handful of public accounts rather than bulk collection. I cover the free Python routes in depth in how to scrape Instagram with Python, and the anonymous story problem is the one thing no free tool solves, which is the next section.

How do you scrape Instagram stories without an account?

You scrape a public account’s stories without an account by routing the request through a service that uses its own rotating IPs, because that is the only way to read active stories without registering a view. Any tool that relies on your session cookie, Instaloader’s --stories flag included, views the story as you, which shows up in that account’s viewer list.

Stories add a second constraint the other media types do not have: they are ephemeral. A story is reachable for 24 hours and then it is gone from the account unless the user pinned it to a highlight, so a once-a-day pull misses most of what an account posts. To capture stories reliably you schedule frequent runs against your target list, every few hours, and store each story’s media URL, its photo-or-video type, and its timestamp as you find them.

The official API is closed for this. The Graph API stories endpoint returns stories only for an account your app manages, only while each story is live, and it excludes reshared and live-video stories. For an arbitrary public account there is no official path, so an anonymous story scraper backed by rotating IPs is the only route, which is the same infrastructure that makes bulk reel collection practical at scale.

How do you scrape Instagram videos at scale without getting blocked?

You scrape Instagram videos at scale without getting blocked by sending one HTTP request to a scraper API that handles the TLS fingerprint, residential proxy rotation, the rotating doc_id, and the retries on its own servers, then returns the reel, story, or video as parsed JSON. This removes the exact parts of the Python and Instaloader routes that break, so you stop babysitting fingerprints and proxies and get structured data back.

The request is a single GET with your target and an API key. The reel endpoint returns the same video fields you would otherwise parse out of the GraphQL response:

curl "https://api.chocodata.com/api/v1/instagram/reels?username=nasa&api_key=$CHOCO_API_KEY"

The Python version drops into an existing script as the fallback for when you start seeing 429s:

import requests, os

resp = requests.get(
    "https://api.chocodata.com/api/v1/instagram/reels",
    params={"username": "nasa", "api_key": os.environ["CHOCO_API_KEY"]},
    timeout=30,
)
for reel in resp.json()["reels"]:
    print(reel["video_url"], reel["view_count"], reel["caption"])

The same shape covers stories by swapping the path to /instagram/stories, so the anonymous, scheduled story capture from the section above becomes one call from the service’s rotating IPs rather than infrastructure you own. In my July 2026 testing the endpoint is authenticated: called without a key, api.chocodata.com/api/v1/instagram/profile returns 401, which confirms the host and path are live and gated rather than open. ChocoData runs a free tier of 1,000 requests with no card, then charges per successful request, and failed requests behind the login gate are not billed.

The tradeoff is the usual one. For a one-off pull of a few public reels, the curl_cffi script or Instaloader from your own machine is free and fine. Once you need thousands of videos, continuous story monitoring, or reels across many accounts on a schedule, the fingerprint and proxy upkeep costs more time than the API costs money. I rank the managed options head to head in my best Instagram reel, story and video scrapers guide, and cover the full toolset in the best Instagram scrapers roundup.

Scraping publicly visible Instagram reels and videos while logged out is treated as defensible in the US, but it is not a blanket permission, and the details matter more than the headline. A California court ruled on January 24, 2024 in Meta Platforms v. Bright Data that Meta’s terms do not bar logged-off scraping of public data, because a logged-off scraper never agrees to those terms. That covers the public reel and video mode the methods above use.

Three things sit outside that ruling. Meta’s Platform Terms prohibit collecting data with automated means without prior permission, which binds any workflow that logs in, so a cookie-based story scraper is on weaker ground than an anonymous one. Reel captions, comments, and the accounts behind them are personal data under the GDPR and similar laws when the subjects are identifiable. And bypassing the login wall with a fake account, or touching a private account, is a separate matter that the public-data reasoning does not reach.

The practical line that keeps you inside the defensible zone is narrow and worth stating plainly. Scrape only public reels, stories, and videos, stay logged out where you can, pull only the fields you need, and handle any personal data you keep under the law that applies to you. None of this is legal advice, and a high-volume or commercial project is worth running past a lawyer before it ships.

FAQ

Can you scrape Instagram reels without an API key?

Yes, you can scrape a public reel without any API key by calling Instagram's graphql/query endpoint with the reel's shortcode and the current doc_id, or by running Instaloader with its --reels flag, which needs no login. Both work from a single IP for a while, then Instagram returns 401 or 429 once you pass roughly 200 requests per hour. The catch with the no-key routes is maintenance: Instagram rotates the doc_id every few weeks, so a hardcoded value stops returning data and you re-read it from a live request.

Can you download Instagram stories anonymously?

You can read a public account's active stories anonymously only through a service that sends the request from its own rotating IPs, because any tool that uses your session cookie registers a view on that account. Instaloader's --stories and --highlights flags both require login, so they run as you, not as an anonymous viewer. The official Instagram Graph API cannot read stories for an account you do not manage, and it only exposes them while each story is live inside its 24-hour window.

Why does my Instagram reel video URL stop working?

Your reel video_url stops working because Instagram signs its CDN media links with a short-lived token, so the URL returns 403 once the signature expires, usually within hours. Store the downloaded .mp4 file rather than the link, and re-fetch the reel object when you need a fresh URL. This is the single most common bug I see in first reel scrapers: the metadata is saved but the video link is dead by the time anyone clicks it.

How do you get the view count of an Instagram reel?

The view count of a public reel comes back in the video_view_count field of the xdt_shortcode_media object when you call Instagram's GraphQL endpoint by shortcode, alongside video_play_count, likes, and comment count. A managed reel scraper returns the same number in a flat view_count field. The official Graph API only reports reel insights for Business and Creator accounts that authorized your app, so it cannot give you the view count of an arbitrary public reel.

Can you scrape Instagram videos with Python without getting blocked?

You can scrape Instagram videos with Python for a while from one residential IP, but a datacenter or cloud IP is refused on the first request, and Python's default requests client is fingerprinted at the TLS layer before any header is read. Matching a browser fingerprint with curl_cffi and routing through residential IPs at a slow rate is what clears it. Past a few thousand videos the proxy and fingerprint upkeep costs more time than a managed API costs money.

LH
Lena Hoff
I've built Instagram data pipelines for years. On instagramscraperapi.com I run Instagram scraping methods against live pages and publish what actually holds up.