Instagram Post Scraper - $0.18/1k, No Login
Pricing
Pay per event
Instagram Post Scraper - $0.18/1k, No Login
Turn Instagram post and reel links into clean data rows at $0.18 per 1,000 posts - the cheapest on the market. Caption, likes, comments, publish time, every carousel image and video URL, owner, hashtags and mentions. No login, no browser.
Pricing
Pay per event
Rating
0.0
(0)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
17 hours ago
Last modified
Categories
Share
Instagram Post Scraper
Paste Instagram post or reel links and get one clean row per post: the full caption, the like and comment counts, the exact publish time, every image and video URL including all the frames of a carousel, the owner, the hashtags, the mentions, the tagged users and the co-authors. No browser, and nothing to set up — runs work out of the box.
- Takes post links, reel links, IGTV links or bare shortcodes — mix them freely in one run.
- Carousels come back whole: every child image and video URL, not just the cover frame.
- Reels come back with the video file URL and its exact duration in seconds.
- Duplicate links are removed before anything is charged, so pasting a messy list is safe.
- A link that is dead, private or deleted produces a free diagnostic row, never a charge.
- Run it with empty input and you get a free labelled sample row so you can see the shape first.
- Nothing to configure. Links are read without an account, and a built-in session covers the occasional post Instagram will not serve a logged-out reader.
Price
$0.18 per 1,000 posts, 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 post 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.
| Posts | Total cost |
|---|---|
| 100 | $0.0185 |
| 1,000 | $0.1805 |
| 10,000 | $1.8005 |
| 100,000 | $18.0005 |
What is actually charged
- One
post-scrapedevent per post 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. - Duplicate links in your list are collapsed before the run starts, so the same post is never charged twice.
- Links that are not Instagram post or reel links are reported free of charge and never fetched.
- 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
{"postUrls": ["https://www.instagram.com/p/DbwWBxpDm_0/","https://www.instagram.com/reel/DcCH2ZygIiP/","DbtErSrlB2J"],"maxItems": 10}
| Field | What it does |
|---|---|
postUrls | The post, reel or IGTV links you want. Bare shortcodes such as DbwWBxpDm_0 work too, and so do instagram.com/username/p/<shortcode>/ links copied from a profile. Up to 5,000 per run; duplicates are removed first. |
maxItems | Stop after this many posts, counting from the top of your list. Leave it empty to take every link you supplied. Keep it low while you are testing — you pay per post. |
sessionCookies | Leave empty. Runs work out of the box using a built-in session for the posts that need one. That session is shared, so under heavy concurrent load you may see rows 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. |
proxyUrls | Leave empty. Fill it in only if you want the 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 post. A real row from a real run:
{"ok": true,"charged": true,"recordType": "post","inputUrl": "https://www.instagram.com/p/DbwWBxpDm_0/","id": "3958760951503548404","shortCode": "DbwWBxpDm_0","url": "https://www.instagram.com/p/DbwWBxpDm_0/","type": "Video","productType": "clips","isReel": true,"isSponsored": false,"caption": "Are you team LEGO Charizard 🔥, or LEGO Jigglypuff 🎀? Watch the most epic LEGO Pokémon Trainer battle between Finn Wolfhard 🏆 and Terry Crews 💪 \n\n#LEGO #Pokemon #LEGOPokemon","hashtags": ["LEGO","Pokemon","LEGOPokemon"],"mentions": [],"likesCount": 259719,"commentsCount": 1302,"videoViewCount": null,"likesAndViewCountsDisabled": false,"isCommentsDisabled": false,"timestamp": "2026-08-08T14:00:05.000Z","displayUrl": "https://scontent.cdninstagram.com/v/t51.82787-15/768860404_18619203115007445_n.jpg","videoUrl": "https://scontent.cdninstagram.com/o1/v/t2/f2/m86/AQMfU9jUuK82HTk2KE6vE8OoaDkCtaMPVX3MEaAQxxWerJREhWEM.mp4","videoDuration": 30.037,"images": ["https://scontent.cdninstagram.com/v/t51.82787-15/768860404_18619203115007445_n.jpg"],"videos": ["https://scontent.cdninstagram.com/o1/v/t2/f2/m86/AQMfU9jUuK82HTk2KE6vE8OoaDkCtaMPVX3MEaAQxxWerJREhWEM.mp4"],"childPosts": [],"childPostsCount": 0,"dimensionsWidth": 1080,"dimensionsHeight": 1920,"alt": null,"ownerId": "196743444","ownerUsername": "lego","ownerFullName": "LEGO","ownerIsVerified": true,"ownerIsPrivate": false,"ownerProfilePicUrl": "https://scontent.cdninstagram.com/v/t51.2885-19/456426104_487557390884013_n.jpg","ownerProfileUrl": "https://www.instagram.com/lego/","coauthorProducers": ["finnwolfhardofficial","pokemonnewsuk","pokemon","terrycrews"],"taggedUsers": [{"username": "terrycrews","fullName": "Terry Crews","id": "196598493"},{"username": "pokemon","fullName": "Pokémon","id": "1711337410"}],"locationName": null,"locationId": null,"musicArtist": null,"musicTitle": "Original audio","source": "graphql","scrapedAt": "2026-08-15T22:26:02.669Z"}
Field notes
type—Image,VideoorSidecar.Sidecaris Instagram's name for a carousel — a post holding several images or clips.productType— the finer-grained kind:feedfor an ordinary post,clipsfor a reel,carousel_containerfor a carousel,igtvfor a long-form video.timestamp— the moment the post was published, normalised to ISO 8601 in UTC. It never moves, so it is the right key for sorting and for date filtering after the fact.likesCount / commentsCount— a snapshot at the moment the row was read, not at publish time. On an account that has hidden its counts these come backnullandlikesAndViewCountsDisabledistrue.images / videos— flat arrays of every media URL in the post, in carousel order. For a single-image postimagesholds one URL; for a six-frame carousel it holds six.childPosts— the same carousel frames as objects, each with its own id, shortcode, type, dimensions and alt text — use this when you need to tell which frame is a video.videoDuration— length of the clip in seconds, to three decimals.nullon a still image.taggedUsers— people tagged in the photo itself, as{username, fullName, id}. People merely @-mentioned in the caption are inmentionsinstead.coauthorProducers— usernames of accounts that share authorship of a collab post; empty for an ordinary post.displayUrl / videoUrl— direct links to the media on Instagram's CDN. They carry a signed query string that expires after a few days, so download what you need in the same session rather than storing the URL for later.source— which reader produced the row.graphqlis the normal one;post-pagemeans the run fell back to the slower reader mid-flight. The fields are identical either way.
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 asks Instagram directly for the post record that the public post page itself is built from, and reads the answer as structured data. No page is rendered and no browser is started.
- Every post is tried without a session first, because a post is public and normally answers a logged-out reader on the first attempt. Only when Instagram refuses twice, on two different addresses, does the run fall back to a built-in session — so the fallback is reserved for the posts that actually need it.
- Requests leave through a large pool of rotating addresses, and the run moves to a fresh one every few requests, so the per-address rate limit is answered by moving rather than by waiting.
- Posts are fetched several at a time, which is why a hundred links take seconds rather than minutes.
- A second, slower reader is kept in reserve. If the fast path ever stops answering, the run switches to it automatically, writes one free note in the dataset saying so, and carries on returning the same fields.
- Nothing is stored between runs, so the same input always produces the same rows plus whatever changed on Instagram since.
What people use it for
- Measuring campaign performance: drop in the links your brand and your agency posted this month and get likes, comments, publish times and captions in one table you can pivot.
- Influencer vetting: pull the last twenty posts of a creator you are about to pay and check the real engagement numbers instead of the ones in the media kit.
- Archiving your own content: every image and video URL of every carousel frame, so a back-up script has something concrete to fetch.
- Feeding a content dashboard or a warehouse — the rows are flat, typed and deduplicated, so they load into a spreadsheet or a database with no cleaning step.
- Enriching a list of links you already collected elsewhere: this Actor is built to take URLs in and give full records out, which is exactly the missing half of a discovery tool.
- Tracking hashtag and mention usage over time by diffing the
hashtagsandmentionsarrays across scheduled runs.
Reading the output
Every run writes three kinds of row, and they are easy to tell apart:
- Real rows carry
"charged": trueand"recordType": "post". These are the rows you paid for, one billed event each. - The sample row carries
"_sample": trueand"charged": false. There is exactly one, it only appears when the input had no links, and it exists so you can look at the output shape before you spend anything. - Diagnostic rows carry
"_diagnostic": true,"charged": falseand anerrorCodeyou can switch on:NOT_FOUNDwhen a post is deleted, private or the link is wrong;BAD_INPUTwhen an entry in your list is not an Instagram post link at all;RATE_LIMITEDorBLOCKEDwhen the target pushed back on that one link;TIME_BUDGETwhen the run ran out of time before reaching a link. Each carries theinputUrlit belongs to, so you can retry exactly those.
If you only want the data, filter on charged == true. The count of those rows always equals the number of events you were billed for, so the dataset is its own invoice.
Limitations
- It reads posts, not profiles. Give it post, reel or IGTV links; it does not take a username and walk somebody's grid.
- Only public posts. A post on a private account is not visible to anyone who is not logged in and following, so it returns a free
NOT_FOUNDrow rather than data. - View and play counts are not exposed to logged-out callers, so
videoViewCountis almost alwaysnull. Likes and comments are exposed and are returned. - Comments themselves are not returned — only how many there are.
- Like and comment counts are a snapshot at read time and will differ from what you see if you open the post a minute later.
- Media URLs are signed and expire after a few days. Fetch the files during the run or shortly after; do not treat the URL as permanent.
- Carousel child dimensions are whatever Instagram reports for that frame, which for some older posts is a scaled size rather than the original.
- Age-restricted posts, and posts removed for a copyright claim, return a free
NOT_FOUNDrow. - Instagram rate-limits per address. The run handles that by moving addresses, but a very large single run will be slower per post than a small one.
- The built-in session that covers gated posts 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
sessionCookiesfor 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. - If the first 40 links in a list all return nothing, the run stops there rather than reading the rest, writes one free note saying so, and charges nothing. That is a guard against a list of dead links, not a limit on real work — a single good post resets it.
- The ceiling is 5,000 links per run. For more than that, split the work across runs — nothing is held between runs, so that is safe.
Questions
Do I need an Instagram account, a session cookie or a login?
No. Runs work out of the box with nothing filled in. Post links are read the way a logged-out visitor reads them, and for the occasional post Instagram will not serve that way there is a built-in session behind the scenes. Nothing is ever posted, liked, followed or changed.
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 happens to a link that is dead or private?
You get one uncharged diagnostic row for that link with errorCode: "NOT_FOUND", and the run carries on through the rest of your list. You are never billed for a link that returned nothing.
Does a carousel count as one post or as several?
One. A carousel is one row, one charge, with every frame's URL inside it — however many frames it has.
Can I hand it a reel link?
Yes. Reel links, post links, IGTV links and bare shortcodes all resolve to the same record, and reels come back with the video URL and its duration.
Will the run fail if Instagram blocks something?
No. A blocked or empty link produces an uncharged diagnostic row explaining what happened and the run still finishes as succeeded. A failed run would still bill you the start fee, which would mean paying to be told something went wrong.
Do I need a proxy?
No. The run brings its own egress and its cost is already inside the price you see. The proxyUrls field exists only for callers who specifically want traffic to leave through servers they already own.
Can I run this on a schedule?
Yes. Nothing is held between runs, so a schedule with the same links is safe and gives you a clean time series of engagement counts. Use shortCode as the key when you diff one run against the next.
How do I get exactly the rows I paid for?
Filter the dataset on "charged": true. Sample and diagnostic rows are always false, and the number of charged rows always equals the number of billed events.