Instagram Post Scraper - $0.18/1k, No Login
Pricing
Pay per event
Instagram Post Scraper - $0.18/1k, No Login
Instagram post and reel links in, clean rows out: caption, likes, comments, publish time, every carousel image and video URL, owner, hashtags, mentions and tagged users. Runs logged out, no browser. $0.18 per 1,000 posts, flat on every plan.
Pricing
Pay per event
Rating
0.0
(0)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
13
Total users
8
Monthly active users
2 days ago
Last modified
Categories
Share
Instagram Post Scraper
Paste Instagram post or reel links and get one 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.
Nothing to set up, and no browser is started.
- 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's dead, private or deleted produces a free diagnostic row, never a charge.
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).
$0.18 on the free plan, $0.18 on a paid one. No volume tiers, no minimum spend, nothing charged monthly.
| 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. - Never charged: the sample row, and any diagnostic row. A dead URL, a deleted post and a link
the target refused all come back at no cost, marked
"charged": false. - Duplicate links in your list are collapsed before the run starts, so the same post is never charged twice.
- Links that aren't Instagram post or reel links are reported free of charge and never fetched.
- If every link in your list is dead, you pay the start fee and not a cent more.
- Every row has a charge behind it and every charge has a row, so the two reconcile. The billed
event is named
post-scraped, not Apify's generic per-item event.
Input
{"postUrls": ["https://www.instagram.com/p/DbtErSrlB2J/","https://www.instagram.com/reel/DcCH2ZygIiP/","DbwWBxpDm_0"],"maxItems": 10}
| Field | What it does |
|---|---|
postUrls | The post, reel or IGTV links you want. Bare shortcodes such as DbtErSrlB2J 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. |
sessionCookies | Optional. Leave it empty for public posts. See About logins below. |
proxyUrls | Leave empty. Fill it in only if you want the traffic to leave through proxy servers you already pay for. |
Hand it no links at all and it writes one labelled sample row, free of charge, so you can see the shape first.
Output
One row per post. Real row from a real run, with the media URLs and the carousel frames trimmed:
{"ok": true,"charged": true,"recordType": "post","inputUrl": "https://www.instagram.com/p/DbtErSrlB2J/","id": "3957840214710558089","shortCode": "DbtErSrlB2J","url": "https://www.instagram.com/p/DbtErSrlB2J/","type": "Sidecar","productType": "carousel_container","isReel": false,"isSponsored": false,"caption": "Where in the (Martian) world is Curiosity?\n\nOur Curiosity Mars rover has started its climb up a Martian valley called Valle Grande…\n\n#NASA #Mars #CuriosityRover","hashtags": ["NASA", "Mars", "CuriosityRover"],"mentions": [],"likesCount": 273272,"commentsCount": 1692,"videoViewCount": null,"likesAndViewCountsDisabled": false,"isCommentsDisabled": false,"timestamp": "2026-08-06T15:49:01.000Z","displayUrl": "https://scontent-fra3-1.cdninstagram.com/v/t51.82787-15/766074877_...jpg","videoUrl": null,"videoDuration": null,"images": ["…jpg", "…jpg", "…jpg", "… 5 more …"],"videos": [],"childPosts": ["… one object per frame, trimmed …"],"childPostsCount": 8,"dimensionsWidth": 495,"dimensionsHeight": 292,"alt": null,"ownerId": "528817151","ownerUsername": "nasa","ownerFullName": "NASA","ownerIsVerified": true,"ownerIsPrivate": false,"ownerProfilePicUrl": "https://scontent-fra3-1.cdninstagram.com/v/t51.2885-19/…jpg","ownerProfileUrl": "https://www.instagram.com/nasa/","coauthorProducers": [],"taggedUsers": [],"locationName": null,"locationId": null,"musicArtist": null,"musicTitle": null,"source": "graphql","scrapedAt": "2026-08-20T19:51:52.669Z"}
A reel row fills in videoUrl, videoDuration and videos, and carries type: "Video" with
productType: "clips".
Field notes
type—Image,VideoorSidecar.Sidecaris Instagram's name for a carousel.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's 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 sharing 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.
About logins
Runs are logged out. A public post normally answers a logged-out reader on the first attempt, and that's how the vast majority of links are read.
Instagram does sometimes refuse a logged-out reader on a specific post or during a busy stretch.
When that happens you get a free RATE_LIMITED or BLOCKED diagnostic row for that one link and
the run carries on. If you're hitting that a lot, sessionCookies lets you paste your own Instagram
cookie (sessionid=…; csrftoken=…, from Chrome's F12 → Application → Cookies → instagram.com) and
the run reads through your account's own rate limit instead. Treat those values like a password.
They're used for that run's requests and nothing else, never stored, never logged, never written to
the dataset.
Nothing is ever posted, liked, followed or changed on any account.
How it works
It asks Instagram directly for the post record the public post page is built from, and reads the answer as structured data. No page is rendered and no browser is started.
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 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.
Diagnostic rows
A link that doesn't work out comes back as a row rather than failing the run. Those rows carry
"_diagnostic": true, "charged": false and an errorCode: NOT_FOUND when a post is deleted,
private or the link is wrong; BAD_INPUT when an entry isn't an Instagram post link at all;
RATE_LIMITED or BLOCKED when the target pushed back on that one link; TIME_BUDGET when the run
ran out of time before reaching a link. Each carries the inputUrl it belongs to, so you can retry
exactly those.
The free sample row is tagged "_sample": true and only appears when the run was given no links.
Filter on charged == true for the data alone, and counting those rows checks the bill.
Limitations
- It reads posts, not profiles. Give it post, reel or IGTV links. It doesn't take a username and walk somebody's grid.
- Public posts only. A post on a private account isn't visible to anyone who isn't logged in and
following, so it returns a free
NOT_FOUNDrow rather than data. - View and play counts aren't exposed to logged-out callers, so
videoViewCountis almost alwaysnull. Likes and comments are exposed and are returned. - Comments themselves aren't 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. Don't 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.
- 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's 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.
Questions
Do I need an Instagram account, a session cookie or a login?
Not for public posts. Runs work with nothing filled in and read post links the way a logged-out
visitor does. The sessionCookies field is there for the case where Instagram starts refusing your
links and you'd rather read through your own account's rate limit.
What happens to a link that's dead or private?
One uncharged diagnostic row for that link with errorCode: "NOT_FOUND", and the run carries on
through the rest of your list. You're 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 finishes as succeeded. Failing it would still bill the start fee, so you'd be paying to hear that one link was dead.
Do I need a proxy?
No. Egress is included in the rate above and there's nothing to set up. proxyUrls is there only
for people who already pay for their own servers and want the traffic to leave through those.
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.