Instagram Reel Scraper - $0.35 per 1,000 Reels
Pricing
from $0.35 / 1,000 reel scrapeds
Instagram Reel Scraper - $0.35 per 1,000 Reels
Creators can pin reels to the top of their own tab, so the first rows it returns are not the newest. This keeps Instagram's order and flags the pinned ones. Play counts, likes, comments, caption, hashtags, duration, a direct MP4, cover image, audio. No login. $0.35/1,000 reels.
Pricing
from $0.35 / 1,000 reel scrapeds
Rating
0.0
(0)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Instagram Reel Scraper
Creators can pin reels to the top of their own reels tab, so the first rows the tab returns are not the newest ones. Name a public Instagram profile and you get that tab as rows, in Instagram's own order, with the pinned reels flagged rather than quietly re-sorted. Each row carries the play count, likes, comments, the caption with hashtags and mentions split out, the duration, a direct video file, the cover image, and the audio track behind the reel.
There is no API key to get and no login for you to supply. It reads the same public feed the reels tab itself reads, with no browser anywhere in it.
- Many handles in one run. Every reel is one row, deduplicated inside the run.
- Newest first, exactly as Instagram orders the tab, with pinned reels flagged rather than silently reordered.
- Stop at a date with
onlyPostsNewerThan, or cap each profile withresultsLimit. You only pay for rows you receive. - Collab reels are included, with every co-author handle listed.
- Empty input returns a labelled sample row, free, so you can see the shape before spending anything.
Price
$0.35 per 1,000 reels, plus a $0.0005 start fee per run (billed per gigabyte of run memory, so exactly that at the default 1 GB).
The rate is the same on a free account as on a paid one, at any volume.
| Reels | Total cost |
|---|---|
| 100 | $0.0355 |
| 1,000 | $0.3505 |
| 10,000 | $3.5005 |
| 100,000 | $35.0005 |
What is actually charged
- One
reel-scrapedevent per reel row written to the dataset. Nothing else is metered per row. - Free: the sample row an empty run returns, and every diagnostic row — a private account, a dead handle, a profile with no reels. Those all carry
"charged": false. - Reels already returned earlier in the same run are dropped before they are charged.
- Reels filtered out by
onlyPostsNewerThanorskipPinnedPostsare never charged, because they never become rows. - A run that finds nothing costs the start fee and nothing more.
- Rows never leave the dataset without a charge, and are never charged without a row. The billed event is a named one — nothing is attached to
apify-default-dataset-item, so error rows can't quietly bill you.
Input
{"usernames": ["nasa","natgeo","https://www.instagram.com/nike/"],"resultsLimit": 30,"onlyPostsNewerThan": "3 months","skipPinnedPosts": false}
| Field | What it does |
|---|---|
usernames | Instagram handles. nasa, @nasa, https://www.instagram.com/nasa/ and a numeric profile id all work, mixed in one list. Up to 100 per run. |
resultsLimit | How many reels to take from each profile, newest first. Default 30, maximum 1,000. This is per profile, so three handles at 30 is up to 90 rows. |
maxItems | Optional ceiling for the whole run across every handle. Useful as a spend guard on a long list. |
onlyPostsNewerThan | Stop once the reels get older than this. Takes a date (2026-05-01) or a phrase (3 months, 2 weeks, 10 days). Leave empty to ignore dates. |
skipPinnedPosts | Drop the reels the account pinned to the top of its tab, so you only get the chronological feed. |
sessionCookies | Optional, and normally leave it empty. Runs are logged out and that works for public reels tabs. A handful of otherwise-public accounts are closed to logged-out visitors entirely, and those only open with an account — paste yours here as sessionid=...; csrftoken=... if you need them. Chrome: F12 → Application → Cookies → instagram.com. Used for this run's requests and nothing else: never stored, never logged, never written to the dataset. |
proxyUrls | Leave empty. The run brings its own addresses and their cost is already inside the price. Fill this in only if you want traffic to leave through proxy servers you already pay for, as http://user:pass@host:port. |
Run it with empty input and you get one clearly labelled sample row, free.
Output
One row per reel. A real row from a real run:
{"ok": true,"charged": true,"recordType": "reel","inputUsername": "nasa","shortCode": "DcBx_I1iHZW","url": "https://www.instagram.com/reel/DcBx_I1iHZW/","caption": "Check out the eclipse from 50,000 feet in the air!\n\nThese views, including footage from GoPro cameras, were captured by NASA's WB-57 aircraft during the 2026 total solar eclipse.\n\nCredit: NASA\n\n#NASA #Eclipse2026 #Aircraft","videoPlayCount": 6843356,"likesCount": 266714,"commentsCount": 1329,"videoDuration": 92.9489974975586,"videoUrl": "https://scontent-yyz1-1.cdninstagram.com/o1/v/t2/f2/m86/AQPIqo5-Uqofr5Hb3KFa-LDO1CW2ZC_FITrcfxz-9Cvg.mp4?...","displayUrl": "https://scontent-yyz1-1.cdninstagram.com/v/t51.82787-15/774149093_18637093324049152_5106811212992624343_n.jpg?...","audioTitle": "Original audio","ownerUsername": "nasa","isPinned": false,"id": "3963668990156895830","type": "Video","productType": "clips","hashtags": ["NASA", "Eclipse2026", "Aircraft"],"mentions": [],"videoViewCount": 6843356,"dimensionsWidth": 1080,"dimensionsHeight": 1920,"takenAtTimestamp": 1786726237,"timestamp": "2026-08-14T16:50:37.000Z","ownerFullName": "NASA","ownerId": "528817151","ownerIsVerified": true,"coauthorUsernames": ["gopro"],"isPaidPartnership": false,"hasAudio": true,"audioId": "27860248933613853","audioArtist": "nasa","audioIsOriginal": true,"audioDurationMs": 92933,"inputUrl": "https://www.instagram.com/nasa/reels/","scrapedAt": "2026-08-15T22:22:06.980Z"}
Field notes
videoPlayCount— plays as Instagram reports them at read time.videoViewCountcarries the same number, kept so a pipeline written against another reels feed keeps parsing.videoUrl— a direct, playable MP4 link. It's signed and expires after a few hours, so download it in the same job rather than storing the link. Shortened above for legibility; the real row carries the whole URL.displayUrl— the cover frame as a JPEG, also a signed link, also shortened above.isPinned— true when the account pinned this reel to the top of its tab. Pinned reels are hoisted out of date order, which is why they're flagged rather than quietly moved.coauthorUsernames— the other accounts on a collab reel. A collab reel appears on every co-author's tab, so you may see it under more than one handle if you scrape both.ownerUsername— who actually posted the reel. On a collab this can differ from the handle you asked for, which is whyinputUsernameis a separate field.audioTitle— "Original audio" for a sound the account made itself, or the track title for a licensed song.audioIsOriginaltells you which.videoDuration— length in seconds, as a decimal.timestamp— when the reel was posted, ISO 8601 UTC.takenAtTimestampis the same moment as a Unix number.id— the numeric media id. Stable, and safe as a primary key across re-runs.shortCodeis equally stable and is what appears in the public URL.
Every real row carries "charged": true. Sample rows carry "_sample": true, diagnostic rows carry "_diagnostic": true with an errorCode you can filter on, and neither is ever billed.
How it works
It calls the same public feed the reels tab on a profile page calls and reads the JSON that comes back. No page rendering, no headless browser.
Each handle is turned into its numeric profile id once, and the id is only accepted if Instagram states it belongs to the handle you asked for — a collab post sitting on the profile is owned by the other account, and taking that id would quietly return the wrong profile's reels. The feed is then paged with a cursor until your limit or your date cut-off is reached.
Several profiles are read at the same time, each on its own address, so a long list finishes in a fraction of the time and costs a fraction of the compute. Traffic leaves through a large pool of rotating addresses. Instagram meters these feeds per address, so a refusal is answered by moving rather than by waiting, and nothing is billed per gigabyte. Reel ids already seen in the run are skipped, so a collab reel that turns up under two handles is only charged once.
What it gets used for
- Tracking a set of creators or brands week by week. Schedule the same handles daily and diff on
idto see what's new and how the play counts moved. - Working out what actually lands on an account before you brief a campaign — sort by
videoPlayCountand read the top ten captions. - Building a swipe file of reference reels with the cover image and a playable video link already attached.
- Audio research.
audioId,audioTitleandaudioIsOriginalshow which sounds an account leans on and which of those are its own. - Category benchmarking in one run: thirty handles, ten reels each, and you have a snapshot.
- Feeding a dashboard or a warehouse with a flat, typed table instead of scraping a rendered page that changes shape every month.
Reading the output
A dataset holds three kinds of row:
- Real rows carry
"charged": trueand"recordType": "reel". One billed event each. - The sample row carries
"_sample": trueand"charged": false. There's exactly one, and only when the input had no handles. - Diagnostic rows carry
"_diagnostic": true,"charged": falseand anerrorCode:NOT_FOUNDfor a handle Instagram says doesn't exist,NOT_AVAILABLEwhen Instagram returns an empty answer for the handle from every address tried and from a second endpoint — either a typo or an account closed to logged-out visitors,NO_RESULTSfor a public account with no reels,BAD_INPUTfor an entry that isn't a handle,BLOCKEDwhen Instagram stopped answering for that profile,TIME_BUDGETwhen the run ran out of time before reaching a handle, andDEAD_LISTonce when six handles in a row have returned nothing and the run stops early rather than spend your time on the rest. Each names theinputUsernameit belongs to and explains itself in plain English.
Filter on charged == true if you only want data. The number of those rows always equals the number of events you were billed for, so you can check the bill by counting rows.
Handling the video and cover links
videoUrl and displayUrl are direct, signed links to Instagram's media servers. They play and download without any header or cookie, but the signature expires within a few hours, so treat them as something to fetch now rather than to store. If you need the files kept, download them in the same job that reads the dataset. The reel's own page URL (url) never expires — keep that as the permanent reference.
Limitations
- Public accounts only. A private account returns a free diagnostic row saying so. There's no way to read a private profile without following it, and this Actor won't pretend otherwise.
- A small number of otherwise-public accounts are closed to logged-out visitors entirely: Instagram answers for them with an empty result no matter where the request comes from, verified from a clean home connection as well as from the run's own addresses. Runs are logged out, so those come back as a free
NOT_AVAILABLErow. Supplying your own cookie insessionCookiesis the only thing that opens them. - Play counts, likes and comment counts are a snapshot at read time. They keep moving, so two runs an hour apart will legitimately disagree.
- The reels tab only. Photo posts, carousels, stories, highlights and IGTV-only uploads are out of scope — use a posts or stories Actor for those.
- Instagram hoists pinned reels above the chronological feed, so the date filter ignores pinned reels when deciding where to stop.
isPinnedtells you which rows were hoisted. - A collab reel sits on every co-author's tab, so scraping two co-authors returns it under both handles. Deduplication is within a single run, not across runs.
- Video and cover links are signed and expire after a few hours. The reel page URL does not.
- Some accounts hide their reels tab or have never posted a reel. Those return a free
NO_RESULTSrow rather than an error. - Instagram meters these feeds per address. A very deep pull on many handles at once can hit a refusal, and the run reports an honest
BLOCKEDrow for that handle with the reels it did get already delivered and charged. - The ceiling is 100 handles per run and 1,000 reels per handle. Split larger jobs across runs.
- Comment text, liker lists, view-through rates, follower counts and anything else behind a login are not returned.
Questions
Do I need an Instagram account, a cookie or an API key?
No. Runs work with nothing filled in. Handles are read the way an anonymous visitor reads a reels tab. Private accounts stay out of reach either way.
Then what is sessionCookies for?
The handful of accounts Instagram refuses to show a logged-out visitor at all. Those come back as NOT_AVAILABLE on a normal run, and an account cookie is the only thing that opens them. It also gives you a rate limit that is entirely yours if you're pulling hard. It's used for the run's requests and nothing else — never stored, never logged, never written into the dataset. Most runs never need it.
What does a run actually cost me?
The start fee once, plus one charge per reel row you receive. Three handles at 30 reels each is 90 charged rows. Sample and diagnostic rows are free, and a handle that returns nothing costs nothing beyond the start fee.
Will the run fail if a handle is wrong or an account is private?
No. It writes an uncharged diagnostic row for that handle, carries on with the rest, and finishes as succeeded. A failed run would still bill the start fee, and being charged for a typo is not reasonable.
How do I get only the newest reels?
Set onlyPostsNewerThan to a date or a phrase like 2 weeks. The run stops walking a profile as soon as it passes the cut-off, so a daily schedule reads only what's new and charges only for that.
Can I pass profile URLs or numeric ids instead of handles?
Yes. nasa, @nasa, https://www.instagram.com/nasa/ and the numeric profile id are all accepted in the same list. A single reel URL is not a profile, so it comes back as a free BAD_INPUT row telling you so.
Why did I get a reel from a different account?
Collab reels. When two accounts publish a reel together it sits on both tabs, and the row keeps ownerUsername as whoever posted it while inputUsername stays the handle you asked for.
Is it safe to run on a schedule?
Yes. No state carries over between runs, so the same input can run hourly or daily. Use id or shortCode to work out which rows are new since last time.
