Instagram Scraper API - Profiles, Posts and Reels
Pricing
from $0.44 / 1,000 results
Instagram Scraper API - Profiles, Posts and Reels
Instagram scraper for profiles, posts and reels. A username gives the profile row plus the posts on its grid; a post or reel link gives that one item. Rows have the caption, likes, comments, views, media links, tagged users and timestamp. $0.45 per 1,000 rows.
Pricing
from $0.44 / 1,000 results
Rating
5.0
(2)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
115
Total users
27
Monthly active users
23 hours
Issues response
4 hours ago
Last modified
Categories
Security
Limited permissionsShare
Instagram scraper for a username or a post link, flat rows back: the account's profile row plus the posts and reels on its public grid, or the one post or reel you linked, with caption, likes, comments, views, media links and tagged people. One row per thing, ready for a sheet.
The part worth knowing up front: a run reads the public grid. On most accounts that is the whole grid. On a few it is smaller than what you see while logged in. On the free Apify plan a profile stops at 12 posts; a paid plan gets the rest, up to about 720.
| Input | Usernames, profile, post and reel links, accounts whose tagged posts or live stories you want, place links |
| Output | One row per profile, post, reel or place: caption, likes, comments, views, media links, post type, length, sound, place, owner, tagged users, timestamp |
| Ceiling | 5,000 rows per run, and 12 posts a profile on the free plan |
| Account needed | None |
| Price | $0.45 per 1,000 rows on the free plan, down to $0.435 on Business. The free plan's $5 a month covers about 11,000 |
🔍 What Instagram Scraper does
Public accounts are supported, including brands and creators with a business category set. On some
business accounts postsCount and isBusinessAccount come back null; the rest of the row is
filled in.
A username returns that account's profile row, then its grid in the grid's own order: pinned tiles
first, then newest first. Every media row carries isPinned, and skipPinnedPosts drops the pinned
ones if you want plain publication order. Reels an account keeps off its grid are a separate set,
and includeReelsTabOnly adds them on top of the grid.
A post or reel link returns that one item, whether or not it is still on the grid: one row, billed
once. That holds for links with the account name in front (instagram.com/nasa/p/...) and for the
instagram.com/share/ links the app's Share button copies. You can mix usernames and links in one run.
Hashtags and mentions are parsed out of the caption for you. Tagged users, collaborators and sponsors come back as their own arrays with ids, names and links.
📋 What data you get from each Instagram profile and post
| What you get | Field |
|---|---|
| What the row is: a profile, a photo, a video or a carousel | type |
| Profile: handle, display name, numeric id, link | username, fullName, id, url |
| Profile: bio, bio link and picture | biography, externalUrl, profilePicUrl |
| Profile: follower, following and post counts | followersCount, followsCount, postsCount |
| Profile: verified, business and private flags | verified, isBusinessAccount, private |
| Post: link and shortcode | url, shortCode |
| Post: caption, and the hashtags and handles in it | caption, hashtags, mentions |
| Post: likes, comments and video views | likesCount, commentsCount, videoViewCount, videoPlayCount |
| Post: cover image, video file and every carousel frame | displayUrl, videoUrl, images, dimensions, isVideo |
| Post: who posted it and when | ownerUsername, ownerId, timestamp |
| Post: tagged people, co-authors and sponsors | taggedUsers, coauthorProfiles, coauthorUsernames, sponsorProfiles |
| Post: paid partnership, hidden likes, comments off, pinned | isPaidPartnership, isSponsored, likesAndViewCountsDisabled, isCommentsDisabled, isPinned |
| Post: Instagram's alt text for the image | accessibilityCaption |
| Post: numeric id, type (reel, photo, video, carousel) and length | postId, postType, videoDuration |
| Post: the poster's name, badge, privacy, picture and counts | ownerFullName, ownerIsVerified, ownerIsPrivate, ownerProfilePicUrl, ownerFollowersCount, ownerFollowsCount, ownerPostsCount |
| Post: the place it is tagged at | locationName, locationId, locationLat, locationLng |
| Post: its sound | musicTitle, musicArtist, audioId, isOriginalAudio |
| Post: every carousel slide with its own image and video link | carouselSlides |
| Profile: category, highlights, new-account flag, every bio link | businessCategory, highlightCount, joinedRecently, bioLinks |
| Profile: Facebook page, Threads, live story, adults-only flag | facebookPageUrl, threadsUrl, hasPublicStory, isAgeRestricted |
| Profile: business address and related accounts | businessAddress, relatedProfiles |
| Tagged posts: the account tagged and how | mentionedUsername, mentionTypes, timestampIsApproximate |
| Place: name, category, address, coordinates, post count, phone, opening status | locationId, locationName, category, address, city, zip, latitude, longitude, postCount, phone, openingStatus, priceRange |
| Story (paid plan): one row per story up now | storyId, ownerUsername, ownerFullName, mediaType, timestamp, expiresAt, displayUrl, videoUrl, videoDuration, url |
| Comment under a post link (paid plan) | id, text, ownerUsername, ownerId, likesCount, repliesCount, timestamp, isReply, parentCommentId, postShortCode, postUrl |
| Every row: the input it came from | inputUrl |
| Optional add-ons, from 9 November 2026 | facebookPlayCount, facebookLikesCount, instagramPlayCount, facebookCountsFound, audioUrl, topComments |
▶️ How to scrape Instagram posts
- Open Instagram Scraper and click Try for free.
- Type a handle into Profile usernames, or paste links into Start URLs (profiles or posts).
- Set Max results. Start at 50 while you look at the output shape.
- Click Start.
- Download the dataset as JSON, CSV or Excel, or pull it from the Apify API.
Start it with both lists empty and you get one labelled sample row, which is not charged.
💰 How much does it cost to scrape Instagram posts?
$0.45 per 1,000 rows. That is the free plan's price; paid plans pay a little less: $0.445 on Starter, $0.44 on Scale and $0.435 on Business. On the free plan, the $5 Apify gives you each month covers about 11,000 rows.
Profile, post, place, story and comment rows cost the same. You pay for rows that carry data, so the sample row,
diagnostic rows, posts your date or type filter leaves out and duplicates inside a run are not charged.
resultsLimit is the ceiling on both the rows and the spend.
The three add-ons start on 9 November 2026 and need a paid Apify plan. Before then a free row says so.
| Add-on, from 9 November 2026 | Starter | Scale | Business |
|---|---|---|---|
| Facebook counts, per 1,000 posts checked | $4.94 | $4.89 | $4.83 |
| Audio link, per 1,000 posts checked | $4.94 | $4.89 | $4.83 |
| Comments, per 1,000 comments | $0.396 | $0.391 | $0.387 |
📥 What you give it
{"usernames": ["nasa"],"onlyPostsNewerThan": "2 weeks","resultsLimit": 50}
| Field | Default | What it is |
|---|---|---|
usernames | none | Handles with or without @. Each returns its profile row plus its grid. |
startUrls | none | Paste links instead. A profile link reads that grid; a /p/, /reel/ or /tv/ link reads that one item, with or without the account name in front, and so does a /share/ link from the app. A stories, hashtag or place page gets a free row naming the field that reads it. |
mentionsOf | none | Accounts whose Tagged tab you want: public posts by other people that tag or mention them. |
places | none | Place links (instagram.com/explore/locations/...) or place ids. One place row each, then recent posts tagged there on a paid Apify plan. |
storiesOf | none | Accounts whose stories are up now, one row per story, on a paid Apify plan. An account with none gets a free row. |
commentsAsRows | none | Up to this many comments under each post link, one row each, on a paid Apify plan. |
resultsLimit | 50 | Rows across the whole run, profiles and posts together. 1 to 5,000. This is also your spend cap. |
resultsPerProfile | none | Posts per profile (and per tagged account). 0 returns the profile or place row only and reads no posts. |
postTypes | all | reels, photos or carousels to keep only that kind. A post link you paste always comes back. |
onlyPostsNewerThan | none | A day (2026-05-01) or a window (7 days, 2 weeks, 1 month); the form gives you a calendar. Days count in UTC, and pinned tiles have to meet the date too. A value that is not a date stops the run with a BAD_INPUT row before anything is read. |
skipPinnedPosts | off | Drop the tiles pinned to the top of the grid, up to three of them. |
includeReelsTabOnly | off | Also return reels the account keeps off its grid, such as trial reels. It adds them; it does not limit the run to reels. |
posterCountsOnLinks | off | Fill the poster's follower, following and post counts on post links too, at one more profile read per poster. Profile runs fill them anyway. |
includeFacebookCounts | off | Add-on from 9 November 2026: Instagram plays, Facebook plays and Facebook likes. Needs a paid Apify plan. |
includeAudioLink | off | Add-on from 9 November 2026: the link to a reel's or video's sound. A link only, nothing is downloaded. Needs a paid Apify plan. |
commentsPerPost | none | Add-on from 9 November 2026: up to 15 of each post's comments on the post row. Needs a paid Apify plan. |
sessionCookies | none | Optional. Your own Instagram cookie, used from the first request so the run sees what your account sees. Treat the value like a password. |
proxyConfiguration | none | Leave it alone unless you have your own servers to use. |
notionConnector, notionParentId | none | Optional. Write each row into a Notion database as well as the dataset. |
📤 What you get back
A real post row from a run on 30 September 2026, with the image links cut short:
{"ok": true,"type": "image","shortCode": "Dd4OWyWjFsh","url": "https://www.instagram.com/p/Dd4OWyWjFsh/","caption": "I spy with my X-ray eyes 👀\n\nUsing @NASAChandraXray, astronomers discovered a new class of \"hypersoft\" objects that give off very low-energy, difficult to detect X-rays. These objects exist in multiple galaxies, including this one, the Pinwheel Galaxy.\n\n#NASA #Space #Galaxy","hashtags": ["NASA", "Space", "Galaxy"],"mentions": ["NASAChandraXray"],"likesCount": 240993,"commentsCount": 664,"videoPlayCount": null,"videoViewCount": null,"displayUrl": "https://scontent-iad6-1.cdninstagram.com/v/t51.82787-15/...","videoUrl": null,"images": ["https://scontent-iad6-1.cdninstagram.com/v/t51.82787-15/..."],"isVideo": false,"dimensions": {"height": 1441, "width": 1439},"ownerUsername": "nasa","ownerId": "528817151","timestamp": "2026-09-29T16:48:08.000Z","accessibilityCaption": null,"taggedUsers": [],"sponsorProfiles": [],"coauthorProfiles": [],"coauthorUsernames": [],"isPaidPartnership": false,"isSponsored": false,"likesAndViewCountsDisabled": false,"isCommentsDisabled": false,"isPinned": false}
| Field | How to read it |
|---|---|
shortCode | Unique per post. Use it as your dedupe key across runs. |
ownerUsername | On a collaboration post this can be the other account, with the one you asked for in coauthorUsernames. |
videoViewCount, videoPlayCount | null on photos. On a reel the two usually hold the same number. |
isSponsored | The same flag as isPaidPartnership, kept under both names. |
displayUrl, videoUrl, images | Signed and time limited. Download anything you want to keep. |
postsCount, isBusinessAccount | On the profile row, null on some business accounts. The rest of the profile row is still there. |
postId | Text, not a number, so it is never rounded. |
locationLat, locationLng | The tagged place's page, not where the photo was taken. |
timestamp on tagged posts | Worked out from the post id, so it can be a few minutes off; timestampIsApproximate says so. |
businessCategory, highlightCount, joinedRecently | null on some business accounts. |
🧾 Reading the output
Three kinds of row can land in your dataset.
| Row | How to spot it | Billed |
|---|---|---|
| A profile, post or reel | ok: true and a type | yes |
| The sample row | _sample: true, charged: false | no |
| A diagnostic | ok: false and an errorCode | no |
Filter on ok == true and you have your data. Diagnostic rows carry one of these codes:
| Code | What it means |
|---|---|
NOT_FOUND | Instagram showed the item or account as gone, private or suspended. |
NOT_SERVED | This request was not served this run. Re-run it later. Runs before 13 October 2026 wrote BLOCKED here. |
RATE_LIMITED, SERVER_ERROR, NETWORK | Instagram was busy or unreachable for that item. Try again in a few minutes. |
PARTIAL | A profile's posts stopped coming part way, or a free-plan run reached its 12 posts a profile. postsDelivered says how many arrived, and only those were charged. A new run starts from the top. |
BAD_INPUT | An option had a value it cannot use, so nothing was read. Or a /share/ link was cut short, or a link needs another field. |
OPTION_STARTS_SOON, OPTION_OFF | An add-on, stories, comment rows or posts at a place did not run: it needs a paid Apify plan, or it was an add-on asked for before they started on 9 November 2026. The rest of the run went ahead. |
NO_RESULTS | The run finished with nothing to return for your input. |
TIME_LIMIT | The run got close to its time limit and stopped on its own. The results delivered before that are complete and charged. Give the run more time, or ask for fewer results, to get the rest. |
UNEXPECTED_ERROR | The run stopped early on an unexpected error. The results before this row are kept. |
None of these rows is billed. The run carries on after each one except TIME_LIMIT, which ends it, and even then it still ends as succeeded, with an amber status message.
One quirk of the Overview table: it shows post columns, so a profile row reads as an almost empty line there. Open the row itself or download the JSON and it is all present.
💡 What people use it for
- Pulling a competitor's recent posts every Monday and diffing the like and comment counts.
- Checking one post or reel at a time from links a team drops into a channel.
- Measuring posting cadence:
timestampacross a grid tells you how often an account actually posts.
From a grid to what the comments say, in three steps:
- Run this on a username with
onlyPostsNewerThanset to30 days. - Sort the rows on
commentsCountand copy theurlof the posts at the top. - Paste those links into Instagram Comments Scraper and read what people actually said.
🚧 What it does not do
- No hashtag feeds or keyword search, and only the top comments under a link. Separate actors do those, linked below.
- No website on place rows, and the phone is empty when the place lists none.
- A private account returns the profile shell only. Bio, counts and
private: truecome through. There is nothing public behind it to read. - Recent posts, not the full archive. About 720 posts per profile at most, so on a very large
account use
onlyPostsNewerThanto make the walk purposeful. - A signed-out run can see a smaller grid. If a profile shows more in your browser than the run returned, that is the difference.
- Media links expire. They are signed, short-lived links. Fetch anything you want to keep.
- Counts are a snapshot at read time, not at publish time.
- It is not an official Instagram tool. Dami's Studio is independent and is not affiliated with or endorsed by Instagram or Meta, or by any other company named on this page.
🧭 Which Instagram scraper do you need?
| If you want | Use |
|---|---|
| A profile plus the posts on its grid | This one |
| One row per account, with engagement | Instagram Profile Scraper |
| Posts and reels from links you already have | Instagram Post Scraper |
| The reels on a profile's reels tab, with play counts and MP4 links | Instagram Reel Scraper |
| Posts under a hashtag | Instagram Hashtag Scraper |
| Comments under a post or reel | Instagram Comments Scraper |
| Stories a profile has up right now | Instagram Stories Scraper |
| Followers or following lists of a profile | Instagram Followers & Following Scraper |
❓ Questions people ask
Do I need an Instagram account to scrape posts?
No. Profiles with their first 12 posts, post links, tagged posts and places are read the way a signed-out visitor sees them. Later posts, stories, comment rows and posts at a place need a paid Apify plan. The cookie field is optional.
Is resultsLimit per username or per run?
Per run, and the profile row counts towards it. Ask for 100 across three handles and you get 100 rows in total, not 300.
Why did I get fewer posts than I asked for?
The account has fewer, your date filter dropped them, the free plan stopped at 12, or the walk hit
its 720-post ceiling. If the posts stopped for any other reason, a free PARTIAL row says so.
Why do the numbers differ from what I see in the app?
Counts keep moving, and the row holds them as they were when it was read. If posts are missing, open the profile in a private browser window: what a logged-out visitor sees there is what this actor can read.
Can I call it from code or connect it to an AI assistant?
The API tab has code for Python, JavaScript and the CLI.
MCP clients like Claude and ChatGPT use
https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/instagram-scraper; coding assistants can load our
skill file. All three
bill your Apify account at the same price.
Is scraping Instagram legal?
This reads public profiles, posts and place pages only, never private accounts or messages. Results can still contain personal data, which GDPR and similar laws cover, so have a reason for collecting it. Apify's write-up on the legality of web scraping is a good starting point, and we are not lawyers.
🆘 If something breaks
Open the Issues tab on the actor page. Include the username or link you used and the run ID, and the log is enough for us to see what happened.