Instagram Reel Scraper - $0.35 per 1,000 Reels avatar

Instagram Reel Scraper - $0.35 per 1,000 Reels

Pricing

from $0.35 / 1,000 reel scrapeds

Go to Apify Store
Instagram Reel Scraper - $0.35 per 1,000 Reels

Instagram Reel Scraper - $0.35 per 1,000 Reels

Give it Instagram handles and get each profile's reels tab newest-first: play counts, likes, comments, caption, duration, the direct video file, the cover image and the audio track. No login, no cookies, no API key. The cheapest Instagram reel scraper on the market.

Pricing

from $0.35 / 1,000 reel scrapeds

Rating

0.0

(0)

Developer

Dami's Studio

Dami's Studio

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

2 days ago

Last modified

Share

Instagram Reel Scraper

Give it Instagram handles and get each profile's reels tab back, newest first: play count, likes, comments, caption, hashtags, duration, the direct video file, the cover image and the audio track behind the reel. No API key and no browser, and nothing for you to set up — it reads the same public feed the reels tab itself reads.

  • Many handles in one run; every reel is one row, deduplicated inside the run.
  • Newest first, exactly as Instagram orders the reels tab, with pinned reels flagged rather than silently reordered.
  • Stop at a date with onlyPostsNewerThan, or cap each profile with resultsLimit — you only pay for rows you receive.
  • Collab reels are included, with every co-author handle listed.
  • Run it with empty input and you get a labelled sample row, free, so you can see the shape before spending anything.
  • Nothing to configure. Handles are read without an account, and a built-in session covers the handles Instagram refuses to show a logged-out visitor at all.

Price

$0.35 per 1,000 reels, plus a $0.0005 start fee per run (billed per gigabyte of run memory, so exactly that on the default 1 GB).

This is the cheapest Instagram reel scraper on the market, and it is the same rate on every plan, free or paid. There are no volume tiers, no minimum spend, no subscription and no add-on fees. What you read here is what you pay on day one and on day four hundred.

ReelsTotal cost
100$0.0355
1,000$0.3505
10,000$3.5005
100,000$35.0005

What is actually charged

  • One reel-scraped event 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 blocked target, a dead URL, a search that matched nothing. Those rows all carry "charged": false.
  • A profile that turns out to be private, deleted or reel-less costs nothing beyond the start fee — it returns a free diagnostic row instead.
  • Reels already returned earlier in the same run are dropped before they are charged.
  • Reels filtered out by onlyPostsNewerThan or skipPinnedPosts are 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, so there is no price quietly attached to apify-default-dataset-item — the trick that makes some scrapers bill you for their own error messages.

Input

{
"usernames": [
"nasa",
"natgeo",
"https://www.instagram.com/nike/"
],
"resultsLimit": 30,
"onlyPostsNewerThan": "3 months",
"skipPinnedPosts": false
}
FieldWhat it does
usernamesOne or more Instagram handles. nasa, @nasa, https://www.instagram.com/nasa/ and a numeric profile id all work. Up to 100 per run.
resultsLimitHow 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.
maxItemsOptional overall ceiling for the whole run, across every handle. Useful as a spend guard when you pass a long list of handles.
onlyPostsNewerThanStop once the reels get older than this. Takes a date (2026-05-01) or a plain phrase (3 months, 2 weeks, 10 days). Leave it empty to ignore dates.
skipPinnedPostsSet to true to drop the reels the account has pinned to the top of its tab, so you only get the chronological feed.
sessionCookiesLeave empty. Runs work out of the box using a built-in session for the handles that need one. That session is shared, so on a long list under heavy load some handles can come back blocked; putting your own Instagram cookie here gives the run a rate limit nobody else shares. Paste it as sessionid=...; csrftoken=... — Chrome: F12 → Application → Cookies → instagram.com. It is used for this run's requests and nothing else.
proxyUrlsLeave empty. The run brings its own addresses and the cost of them 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, so you can see the output shape before you spend anything.

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. Taking off from Iceland, our pilots studied the Sun's corona as they raced along the path of totality.\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. videoViewCount carries the same number, kept so an existing pipeline written against another reels feed keeps parsing.
  • videoUrl — a direct, playable MP4 link. It is signed and expires after a few hours, so download it in the same session rather than storing the link — shortened above for legibility, the real row carries the full URL.
  • displayUrl — the cover frame as a JPEG, also a signed link — shortened above for the same reason.
  • isPinned — true when the account has pinned this reel to the top of its tab. Pinned reels are hoisted out of date order, which is why they are flagged rather than silently 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 why inputUsername is a separate field.
  • audioTitle — "Original audio" for a sound the account made itself, or the track title for a licensed song. audioIsOriginal tells you which.
  • videoDuration — length in seconds, as a decimal.
  • timestamp — when the reel was posted, ISO 8601 in UTC. takenAtTimestamp is the same moment as a Unix number.
  • id — the numeric media id. Stable, and safe as a primary key across re-runs; shortCode is equally stable and is what appears in the public URL.

Every real row carries "charged": true. Sample rows carry "_sample": true and 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 and no headless browser.
  • Every handle is tried logged-out first, because that is what the reels tab of a public profile answers. Only the handles Instagram will not show that way — measured as a property of the account, not of the connection — fall back to a built-in session, so the fallback is reserved for the handles that actually need it.
  • 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 on the profile is owned by the other account, and taking that id would quietly return the wrong profile's reels.
  • Several profiles are read at the same time, each on its own address, so a long list of handles finishes in a fraction of the time and costs a fraction of the compute.
  • The reels feed is then paged with a cursor until your limit or your date cut-off is reached.
  • Traffic leaves through a large pool of rotating addresses. Instagram meters these feeds per address, so a refusal is answered by moving to a new address rather than by waiting, and nothing is billed per gigabyte.
  • Reel ids already seen in the run are skipped, so a collab reel that appears under two handles is only charged once.

What people use it for

  • Tracking a set of creators or brands week by week: schedule the same handles daily and diff on id to see what is new and how the play counts moved.
  • Finding out what actually works on an account before you brief a campaign — sort the rows by videoPlayCount and 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, audioTitle and audioIsOriginal show which sounds an account leans on and which of those are its own.
  • Competitive benchmarking across a whole category at once — pass thirty handles, cap each at ten reels, and you have a category snapshot in one run.
  • 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

Every run writes three kinds of row and they are trivial to tell apart:

  • Real rows carry "charged": true and "recordType": "reel". One billed event each, and nothing else is metered per row.
  • The sample row carries "_sample": true and "charged": false. There is exactly one, it only appears when the input had no handles in it, and it exists so you can look at the output shape before you spend anything.
  • Diagnostic rows carry "_diagnostic": true, "charged": false and an errorCode you can switch on: NOT_FOUND for a handle Instagram says does not exist, NOT_AVAILABLE when Instagram returns an empty answer for the handle from every address tried and from a second endpoint — either a typo or an account that is closed to logged-out visitors, NO_RESULTS for a public account with no reels, BAD_INPUT for an entry that is not a handle, BLOCKED when Instagram stopped answering for that profile, TIME_BUDGET when the run ran out of time before reaching a handle, and DEAD_LIST once when six handles in a row have returned nothing and the run stops early rather than spend your time on the rest. Each one names the inputUsername it belongs to and explains itself in plain English.

If you only want data, filter on charged == true. The number of those rows always equals the number of events you were billed for, so the dataset is its own invoice.

The videoUrl and displayUrl fields 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, so keep that as the permanent reference.

Limitations

  • Public accounts only. A private account returns a free diagnostic row saying so — there is no way to read a private profile without an account, and this Actor never creates or uses one.
  • 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. Those handles are retried with the built-in session, and if that does not open them either they return a free NOT_AVAILABLE row; no proxy or re-run changes it.
  • The built-in session is shared, and Instagram rate-limits per account rather than per caller, so it is not unlimited throughput. Under heavy concurrent load supply your own cookie in sessionCookies for a rate limit that is yours alone. If the built-in session runs out mid-run, the run finishes with what it collected and says so on its status line rather than failing.
  • 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.
  • Play counts, likes and comment counts are a snapshot at read time. They keep moving, so two runs an hour apart will legitimately disagree.
  • Instagram hoists pinned reels above the chronological feed. The date filter therefore ignores pinned reels when deciding where to stop, and isPinned tells you which rows were hoisted.
  • A collab reel is on every co-author's tab, so scraping two co-authors returns it under both handles — once per handle, deduplicated only within a single run.
  • 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_RESULTS row 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 then reports an honest BLOCKED row 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 available and are not returned.

Questions

Do I need an Instagram account, a cookie or an API key?

No. Runs work out of the box with nothing filled in. Handles are read the way an anonymous visitor reads a reels tab, and the handles Instagram will not show that way fall back to a built-in session. Private accounts are still out of reach either way.

Then what is sessionCookies for?

A private rate limit. The built-in session is shared between runs, and Instagram counts requests per account rather than per caller, so a busy hour is a slower hour for everyone using it. Paste your own Instagram cookie into sessionCookies and this run uses only that — its own quota, nobody else's. It is used for the run's requests and nothing else: never stored, never logged, never written into the dataset. Optional, and most runs never need it.

What does a run actually cost me?

The start fee once, plus one charge per reel row you receive. A run over three handles at 30 reels each is 90 charged rows. Sample rows 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, which would mean paying to be told about a typo.

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 is 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. Nothing is held 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.