Instagram Scraper
Pricing
$1.75 / 1,000 results
Instagram Scraper
Scrape Instagram profiles, posts, reels and comments from any mix of profile and post URLs, or from a search term. $2.025 per 1,000 results: 25% below the Free plan price of apify/instagram-scraper, and the same flat rate on every plan.
Pricing
$1.75 / 1,000 results
Rating
0.0
(0)
Developer
itnlab
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
21 hours ago
Last modified
Categories
Share
Scrape Instagram profiles, posts, reels and comments from any mix of profile and post URLs — followers, engagement averages, captions, like and comment counts, media URLs — at a flat $1.75 per 1,000 results, whatever your Apify plan.
What you get
- Profiles — followers, follows, post count, verification, business category, engagement averages computed over the posts in the snapshot, and since 14 September 2026
biography,profilePicUrl,externalUrl/externalUrlsandhighlightReelCount. - Posts and reels — caption, timestamp, likes, comments, video views, and media URLs on request. Since 3 September 2026 also
hashtagsandmentionscut out of the caption,taggedUsersfor the accounts tagged inside the picture, anddimensionsWidth/dimensionsHeightfor the frame it was published in; since 14 September 2026alt,productType,locationName/locationId,childPostsfor a carousel's slides,originalWidth/originalHeight, andfirstComment/latestComments, which arrive in the same response as the post and cost nothing extra. - Comments — one row each, in
apify/instagram-comment-scraper's shape. - Posts for a keyword — since 15 September 2026, put a word in Search keyword (
search) and the run collects what Instagram ranks for it, alongside whatever URLs you pasted. Those rows carrysearchTermso a mixed run stays attributable. - Suggested accounts —
relatedProfiles, the accounts Instagram suggests beside a profile. Off by default (includeRelatedProfiles): it costs an extra request per profile and the accounts in it are third parties you did not name. - A row for every input, including the ones that failed — a refused or unreachable target writes a row carrying
inputUrl,erroranderrorDescriptioninstead of vanishing. Fifty URLs in means fifty rows out.
How to use the Instagram scraper
- Paste profile URLs, handles or single post URLs into Instagram profiles or posts (
directUrls).https://www.instagram.com/nasa/,nasaandhttps://www.instagram.com/p/DCMUrLltVlM/all work, and one list may mix them. - Choose What to return (
resultsType):detailsfor the profile alone,postsorreelsfor one row per post,commentsfor one row per comment. A post URL answersposts,reelsandcommentswith that one post;detailsasks for an account's own figures, so it is refused for a post URL rather than answered with blanks. - Set Results limit (
resultsLimit) — up to 1,000 posts, or up to 5,000 comments per post. - Or leave the URLs empty and type a word into Search keyword (
search) with a Search limit (searchLimit). The two can be combined: a run may do both in one go. - Press Start. Results land in the dataset as they are collected; export as JSON, CSV, Excel or via the API.
Optional: Only posts newer than takes a date (2026-07-01) or a span (3 days). Include media URLs adds image and video links — they are signed by Instagram and expire within hours, so download promptly. Repeat the profile on every row carries the owning account onto each post and comment so a flat export can be grouped without keeping state. Include suggested accounts (includeRelatedProfiles) adds relatedProfiles to each profile row.
How it compares
One flat price for every buyer, 35% below what apify/instagram-scraper charges its Free-plan buyers, and a post row that says both how many comments Instagram declares (commentsCount) and how many bodies were actually read (commentsCollected), so a figure computed over the sample is never mistaken for one over the whole.
Prices below marked measured come from runs made on 2 September 2026 against an identical list of public accounts; those marked published are the vendor's own figure and were not re-tested here. Rows left as — are outside that Actor's scope rather than a shortcoming.
| Feature | This Actor | apify/instagram-scraper | apify/instagram-profile-scraper | apify/instagram-comment-scraper | apidojo/instagram-scraper |
|---|---|---|---|---|---|
| Pricing plan | pay per result | pay per result | pay per result | pay per result | pay per result |
| Price per 1,000 profiles or posts | $1.75 (flat) | $2.70 (Free plan, tiered) | $2.60 (Free plan, tiered) | — | $0.50 (published) |
| Price per 1,000 comments | $1.75 (flat) | $2.70 (Free plan - it charges per result, whatever the row) | — | $2.60 (Free plan, tiered) | — |
| Usable on the Apify free plan | ✔️ | ✔️ | ✔️ | ✔️ | ❌ (demo only, 10 items) |
| Profile URLs | ✔️ | ✔️ | ✔️ | ❌ | ✔️ |
| Single post URLs | ✔️ | ✔️ | ❌ | ✔️ | reels URLs (published) |
| Keyword search | ✔️ (search, ranked-posts feed) | ✔️ (published) | ❌ | ❌ | ✔️ |
| Hashtag, place, audio URLs | ❌ (the keyword feed is the reachable approximation) | hashtag + place (published) | ❌ | ❌ | ✔️ |
| An error row for every failed input | ✔️ | ✔️ (published) | ✔️ (published) | ✔️ (published) | ❓ |
relatedProfiles on profile rows | ✔️ (opt-in) | ❌ | ✔️ (published, always on) | — | ❓ |
hashtags, mentions, taggedUsers, dimensions* on post rows | ✔️ (tagged accounts minimised) | ✔️ (published) | ✔️ (published) | — | ✔️ (published) |
How many results you get
resultsLimit is a promise, not a hint: ask for 1,000 posts and a run returns 1,000, or every post the account has when that is fewer. There is no page size to work around and no limit error hiding behind the number you typed. Measured 7 September 2026: 1,000 posts requested from @nasa, 1,000 distinct posts returned over 84 pages.
The same holds for comments: ask for 1,000 and a run returns 1,000 where the post has them. Measured 7 September 2026 on p/Dc6zTucHTv8 (@kimkardashian): three consecutive runs asking for 1,000, three runs returning exactly 1,000.
Where fewer come back it is Instagram's doing rather than this Actor's, and the number to compare against is not commentsCount. That is Instagram's own total and it counts replies and filtered comments a logged-out reader is never served — the same post declares ~4,350 and its logged-out connection carries ~3,350. This Actor does not take Instagram's first "there are no more" for an answer: the page that announced the end is fetched again before a run reports one, because an unconfirmed end was measured arriving hundreds of comments early. When a walk still ends short, the run log names the post, what was asked for, what came back and what Instagram declares, so a short answer never has to be guessed at.
Pricing
Pay per result, no platform usage charged on top.
| Result | Price each | Per 1,000 | apify/instagram-scraper, Free plan |
|---|---|---|---|
| Profile | $0.00175 | $1.75 | $2.70 |
| Post or reel | $0.00175 | $1.75 | $2.70 |
| Comment | $0.00175 | $1.75 | $2.70 |
One result is one row whatever its shape, which is the unit that Actor charges on too — and all three rows cost the same here, so a run's bill is its row count and nothing else.
One flat rate, for everybody. $1.75 per 1,000 — no plans, no tiers, no volume brackets, and no Apify platform usage charged on top. That is 35% below the $2.70 that apify/instagram-scraper charges its Free-plan buyers (read from Apify's public API, 17 September 2026); their rate moves with the subscription a buyer is on, and ours does not move at all.
An answer carrying nothing is free — you pay for rows, not for asking.
Output example
One post row, as it lands in the dataset:
One row, with the field names and types a real run writes (values are from a sample run, not live figures):
{"dataType": "post","id": "3498261938871556428","shortCode": "CmOcK0001aa","type": "Image","url": "https://www.instagram.com/p/CmOcK0001aa/","caption": "New single-origin lot landed this morning: Guji, Ethiopia. Jasmine and stone fruit, roasted light for filter.","timestamp": "2026-08-16T18:01:36.000Z","likesCount": 4310,"likesHidden": false,"commentsCount": 96,"commentsCollected": 10,"isVideo": false,"hashtags": ["coffee", "singleorigin"],"mentions": ["guji.mill"],"taggedUsers": [{"username": "guji.mill", "id": "2421050", "isVerified": true, "imageIndex": 0, "position": [0.5439, 0.4595]}],"dimensionsWidth": 1440,"dimensionsHeight": 1800,"ownerUsername": "nasa","ownerId": "17841400000000001","displayUrl": "https://scontent.cdninstagram.com/v/t51.29350-15/a1_n.jpg","images": ["https://scontent.cdninstagram.com/v/t51.29350-15/a1_n.jpg"],"mediaAssets": [{"url": "https://scontent.cdninstagram.com/v/t51.29350-15/a1_n.jpg", "width": 1440, "height": 1800, "isVideo": false}],"collectedAt": "2026-08-19T18:01:36.000Z","inputUrl": "nasa"}
Field names and value shapes follow the conventions the other Instagram scrapers on Apify Store use — shortCode, likesCount, ownerUsername, ids as strings, type as Image/Video/Sidecar, timestamps as 2026-08-16T18:01:36.000Z, and a hidden like count as -1 rather than 0, because zero likes is a real answer and "Instagram will not say" is not. An input object written for one of those can usually be pasted straight in; addParentData defaults to false here for the same reason.
images is a list of URL strings, which is what apify/instagram-scraper writes into a column of that name — checked against its own dataset schema on 4 September 2026. The width, height and video flag of each rendition sit beside it in mediaAssets, in the same order, rather than inside images where they would change the type of a column somebody else's code already reads. mediaAssets entries omit width and height on posts read from an account's grid: Instagram does not send dimensions there, and a 0 would be a measurement nobody made.
A profile row spells the two account flags the way the reference profile row does: private and verified, and the business category is businessCategoryName. They were isPrivate and categoryName until 4 September 2026.
Input example
{"directUrls": ["https://www.instagram.com/nasa/"],"resultsType": "posts","resultsLimit": 30,"onlyPostsNewerThan": "3 days","includeMediaUrls": false,"addParentData": true,"enrichPosts": true}
apify/instagram-scraper's own example input runs here unchanged — every key on its published form is on this one, and tests/test_actor_variants.py keeps a copy of that object so the day either side moves, the suite says so. To collect for a word instead:
{"resultsType": "posts","search": "specialty coffee","searchType": "hashtag","searchLimit": 48}
What this Actor does not collect
Public profiles, public posts, and the posts Instagram ranks for a keyword. A hashtag page, a place page, a story, a /tagged/ URL and the reels home feed (instagram.com/reels/ with no post after it) are refused by name with an explanation rather than quietly collected as something else — a green run over an empty dataset is the most expensive failure to diagnose, so it does not happen here. Those surfaces are not visible to a logged-out visitor at all, which is not a limit this Actor can lift.
search reaches a ranked feed, not a hashtag page. searchType: hashtag is the only value that answers, and what it answers with is Instagram's own popular-search feed: ask for coffee and you get what Instagram ranks for coffee, not every post carrying #coffee. profile and user would mean searching for accounts by name, which is behind the login curtain; place answers but its cursor returns the first page forever (measured 3 September 2026), so asking for two pages of a place would hand you the same posts twice. All three are refused with that measurement attached rather than answered with something else. Keyword posts arrive complete — each is read from its own page — because the feed node carries no timestamp, no like count and no comment count, and a column that is null for an invisible reason is worse than a slower run.
What the reference scrapers put on a post row and this one does not. Seven columns, down from twelve before the parity pass of 14-15 September 2026: isPinned, isCommentsDisabled, videoDuration, videoPlayCount, musicInfo, coauthorProducers and paidPartnership. Each is absent for the same reason — the logged-out node this Actor reads does not carry it — and a column nobody measured is left out rather than written as null or 0. musicInfo and coauthorProducers need a reel with attached audio and a co-authored post to probe properly, and neither will be declared until one has been.
alt, productType, locationName, locationId, childPosts, originalWidth/originalHeight, firstComment and latestComments left that list on 14 September 2026 and are on post rows now.
On a profile row, biography, profilePicUrl, externalUrl, externalUrls and highlightReelCount are collected as of 14 September 2026, under a data-controller decision recorded in this project's compliance file that set the ceiling at output parity with apify/instagram-profile-scraper and no further. public_email and public_phone_number arrive in the same payload and stay closed: the reference does not publish them either, and "it was in the response" has never been a purpose. latestPosts is still absent from this listing's rows because posts are rows of their own here — use Instagram Profile Scraper for the nested shape.
The tagged accounts are collected minimised. taggedUsers carries username, id, isVerified, imageIndex and position for each account tagged inside a picture — and deliberately not their display name or profile picture, which the other scrapers include. A tag says which account an image references, and the handle plus the id say that completely; a name and a face are personal data about somebody who is not even the profile you asked for. mentions, by contrast, is read out of the caption text, so the two columns answer different questions and neither is a substitute for the other.
enrichPosts is what makes a listed post a complete post, and it is on by default. Instagram's logged-out profile grid renders thumbnails, so its query carries a caption, a shortcode and one image and nothing else: no timestamp, no counts, no frame size and no tags. Each listed post is therefore read from its own page, which has all of it. That costs one request per post, against roughly one per twelve to list them — 100 posts is about 9 requests to list and about 109 to complete — so set "enrichPosts": false when you want shortcodes and captions quickly and can live with timestamp, likesCount, commentsCount, videoViewCount, dimensions* and taggedUsers being absent. When they are absent they are absent, not empty: [] and 0 would read as "this picture tags nobody" and "nobody liked it", which was never measured. A post collected by its own URL always carries them, whatever this flag says.
Single post URLs were on that list until 3 September 2026 and are now targets: paste https://www.instagram.com/p/DCMUrLltVlM/ (or its /reel/ and /tv/ spellings) and you get exactly that post, with its comments if you ask for them. One unreachable profile or post in a list of fifty does not cost the other forty-nine.
FAQ
How fresh is the data? Every profile and post row carries collectedAt. Answers come from a snapshot, not from a live call to Instagram at the moment you read them. A comment row does not carry it — it is the reference scraper's row and nothing else — so a comments run logs the snapshot's age instead.
Why is commentsCount bigger than the comments I got? commentsCount is the total Instagram declares; commentsCollected is how many bodies were actually walked, bounded by your limit.
Why are comment runs slow? The collector paces itself to look like a person rather than a script — roughly one comment per second per post. That pacing is why the data arrives at all.
Is this legal? It collects public data only. You are responsible for how you use it, including under GDPR where the data concerns people in the EU.
Related
- Instagram Comment Scraper with Sentiment — the same engine, shaped for comment work, with a sentiment label and score on every comment, at the same $1.75 per 1,000.
- Instagram Profile Scraper — handles in, one row per account with its posts nested, at the same $1.75 per 1,000.