Instagram Posts Scraper & Instagram Reels Scraper, No Login avatar

Instagram Posts Scraper & Instagram Reels Scraper, No Login

Pricing

from $1.35 / 1,000 post or reels

Go to Apify Store
Instagram Posts Scraper & Instagram Reels Scraper, No Login

Instagram Posts Scraper & Instagram Reels Scraper, No Login

Scrape Instagram profile posts and reels, no login: an Instagram post scraper and reels scraper for usernames, profile links or post URLs. Caption, hashtags, exact time, likes, comments, reel plays, owner, coauthors, the real pinned flag, paid flag, location, alt text, carousel images, video URLs.

Pricing

from $1.35 / 1,000 post or reels

Rating

0.0

(0)

Developer

Hyperbach

Hyperbach

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

Instagram posts and reels as data, logged out: usernames, profile links or profile ids in — or post and reel links — and one row per post out. Each post has its whole caption with its hashtags and mentions, the exact publish time, the likes (null when the owner hides them, never a fake 0), the comment count, a reel's plays, duration and music, the owner and their followers, the coauthors of a collab post, the paid-partnership flag, the owner's own pinned flag (a coauthor's pin is not counted), the tagged accounts, the location, Instagram's alt text and topic tags, every carousel item, and the image, video and audio URLs with the time they expire. Pasted links carry the post's latest comments; profile rows can too. Every profile gets a free summary row with what was read and a verdict — COMPLETE (the profile's list ended), CAPPED, DATE_LIMIT or PARTIAL — and a private, missing or age-gated profile, a deleted post or a value that names nothing is a free row that says which. No login, no Instagram account, no cookie, proxies handled for you.

Why this Instagram posts scraper

  • The pinned flag, right. A pinned post is the owner's own pin, read from the order of the owner's list and Instagram's grid; a coauthor's pin is not the owner's. On National Geographic's three pinned posts the three biggest Instagram scrapers all say isPinned: false; this Actor flags the three, and leaves out the post a coauthor pinned.
  • The newest posts, not the oldest pins. A pinned post counts at its own date: three old pins never take the places of three new posts in your maxPostsPerProfile, and they never stop a date cut-off early. Every row has its position in the profile, newest first.
  • Deep, with a proof. The walk reads the profile's posts in date order to Instagram's end flag, 50 posts a call (our source study walked a 1,066-post profile to its last post that way), with the owner's own list beside it for the pins, the type, the tags and the paid flag; a post the owner's list skips at one of its page boundaries is still delivered. Each profile's summary row says how many posts were looked at and delivered and whether the list ended (COMPLETE) or your cap came first (CAPPED): a short dataset is never silent.
  • Counts that do not lie. Likes are exact; a post whose owner hides the likes gets likes null and likes_hidden true, never Instagram's placeholder 0. A reel's plays come from the reels list, the counter the app shows, not a smaller web counter; a row without plays says why (plays_status).
  • Collab posts as they are. A post another account owns but lists in this profile's grid keeps its real owner, with the profile you gave in profile and in the coauthors. The coauthors come with their usernames.
  • One row per post, free rows for the rest. A post given twice, or both as a link and in a profile's posts, is one row and one charge. A post left out by a filter, an old post onlyNewPosts skips, a private or missing profile: free.
  • Plain HTTP, no browser. Instagram's own logged-out answers: no login, no cookie, no account of yours, nothing to configure. On our test a profile's 200 newest posts and reels, every reel with its plays and the three pins flagged, came out in 45 to 70 seconds (21 requests; the time depends on the connection the run gets); the first row arrives with the profile's newest reel's plays.

Who it's for

  • Marketers and brand teams — a competitor's or a creator's newest posts and reels with likes, comments, plays and the paid-partnership flag, pinned posts told apart, on a schedule with only the new posts.
  • Influencer and social analysts — engagement per post against the owner's followers, collab posts and their coauthors, the music of reels, a date window per profile across many profiles in one run.
  • Researchers — a profile's whole list of posts to its end with captions, hashtags, mentions, exact times, locations and Instagram's own topic tags, with a verdict per profile.
  • Developers and data teams — typed rows keyed by the media id, one free summary row per profile, a run summary to check a pipeline against; post links in, the same columns out.

Quick start

A profile's 12 newest posts and reels

{
"profiles": [
"natgeo"
],
"maxPostsPerProfile": 12
}

Several profiles' reels with their plays, the last 30 days

{
"profiles": [
"natgeo",
"nasa"
],
"contentType": "reels",
"maxPostsPerProfile": 50,
"newerThan": "30 days"
}

Post and reel links, with their latest comments

{
"postUrls": [
"https://www.instagram.com/p/Des_X6JFjbE/",
"https://www.instagram.com/reel/Dewd1c3iRmI/"
]
}

Every post of a profile, to the end of its list

{
"profiles": [
"https://www.instagram.com/jefferson_fisher/"
],
"maxPostsPerProfile": 0
}

Only the new posts since the last run (for a schedule)

{
"profiles": [
"natgeo"
],
"maxPostsPerProfile": 20,
"onlyNewPosts": true,
"stateStoreName": "natgeo-daily"
}

Output

One row per post or reel (and one free summary row per profile, one free row per input that gave no post):

fieldmeaning
record_typepost (a post or a reel: charged when status is ok) or profile (a free summary row per profile: what was read, the verdict, or why the profile gave no post). A post link that gave no post has a free post row with its status.
statusA post: ok. A profile's summary row: ok (see verdict), not_found (no profile with that username: two exits agree), restricted (Instagram serves no record logged out: an age-gated brand or an otherwise restricted profile; two exits agree), private, no_posts, duplicate (the same profile as another input of the run, e.g. a username and its id: walked once), failed, not_read (maxItems, the charge limit or the time limit came first). A post link with no post: not_found, restricted, duplicate (delivered under another input of the run: the note says which), filtered (left out by a min filter), failed, not_read. A value that names nothing: invalid_input.
inputThe value you gave that led to this row.
input_typeprofile, post or invalid.
profileThe username of the profile input the row came from; on a post link's row, the post's owner.
position1-based place in the profile's list of posts, newest first by date, a pinned post at its own date; every post the walk looked at keeps its place (one left out by a filter, a date limit or onlyNewPosts too), so the delivered rows can skip numbers. Null on a post link's row.
idInstagram's media id (the number behind the shortcode): one row and one charge per post a run, whatever inputs overlap.
shortcodeThe code in the post's link (DeuOR-CjKE9).
urlThe post on Instagram: /reel/<code>/ for a reel, /p/<code>/ for the rest.
typephoto, carousel (several photos or videos), reel, or video (a long video that is not a reel), from Instagram's own product type, never from the media type alone (a reel and a long video share it).
product_typeInstagram's own name for the kind of post: feed, carousel_container, clips (a reel) or longform.
timestampThe exact publish time, UTC (ISO 8601).
taken_atThe same time as a Unix timestamp (seconds).
captionThe whole caption, emoji and line breaks kept.
hashtagsEvery #hashtag of the caption, without the #, in order, once each.
mentionsEvery @username the caption mentions, without the @, once each.
caption_is_editedTrue when Instagram marks the caption as edited; read with the post itself (a post link's row, or includeComments on a profile's rows), else null.
altInstagram's accessibility caption of the post ("Photo by National Geographic on October 20, 2026. May be an image of …"); not on reels-only rows (the reels list carries none).
likesThe exact like count; null when the owner hides the likes (Instagram then serves 0, which is not the count: likes_hidden is true).
likes_hiddenTrue when the owner hid the post's like count.
comments_countThe comment count Instagram shows.
comments_disabledTrue when the owner turned the comments off; null when the route that read the post does not say.
playsA reel's plays, from the profile's reels list (the counter the app shows); null with the reason in plays_status elsewhere.
plays_statusserved; not_a_reel (a photo, a carousel, a long video); outside_walk (an old pasted reel past the 5 pages of its owner's reels list the Actor reads for it); not_listed (the reels list passed its date without it); refused (the reels list refused two exits); not_asked.
video_durationA video's or reel's length in seconds (the reels list's figure, else the video manifest's).
has_audioFalse on a video Instagram marks as silent; null on a photo.
engagement_rate(likes + comments) ÷ the owner's followers, four significant digits; null when the likes are hidden or the owner is not the profile read (a collab post another account owns).
owner_usernameThe post's owner. On a collab post another account owns, that account (the profile you gave is then in coauthors and in profile).
owner_idThe owner's numeric profile id.
owner_full_nameThe owner's display name.
owner_is_verifiedTrue when the owner has the verified badge.
owner_profile_pic_urlThe owner's profile picture URL (signed; see media_urls_expire_at).
owner_followersThe owner's follower count, read once per profile at the start of the run; null on a collab post owned by another account.
coauthorsThe collab post's other accounts: {username, is_verified}; the owner left out.
coauthor_idsThe coauthors' numeric profile ids, as the owner's list serves them.
is_paid_partnershipTrue on a post marked "Paid partnership"; read from the profile's own list (on a pasted photo link whose owner is not walked in the run: null; a pasted reel gets it from its owner's reels list).
is_pinnedTrue when the profile's owner pinned the post to the top of the profile (a coauthor's pin is not the owner's); on a reels-only row, pinned on the reels tab. Null on a pasted post whose owner is not walked in the run.
tagged_usersThe accounts tagged in the post or any carousel item: {username, full_name, id}.
location_nameThe place tagged on the post.
location_idInstagram's id of the place.
location_latThe place's latitude: on a post link's row, or with includeComments (the post itself carries the coordinates).
location_lngThe place's longitude, as location_lat.
display_urlThe post's image at its largest size (a video's cover; a carousel's first item).
imagesEvery image of the post at its largest size: one for a photo, one per item for a carousel.
carouselA carousel's items in order: {type (photo or video), shortcode, display_url, video_url, width, height, alt}; empty for a single photo or video.
carousel_countHow many items the carousel holds.
video_urlThe video file at its largest size (a reel, a long video, a carousel's first video).
audio_urlThe video's audio-only file from its manifest.
media_urls_expire_atWhen the signed media URLs of the row stop working (Instagram's own expiry in each URL: 1 to 4 days after the read). Download what you need before.
widthThe post's width in pixels.
heightThe post's height in pixels.
musicA reel's sound: {title, artist, audio_id, uses_original_audio} (a licensed track, or the creator's original audio).
shared_to_facebookTrue when the reel was also posted to Facebook.
ai_labelInstagram's own AI label of the post ("Likely made with AI"); null when it shows none.
topicsInstagram's own topic tags of the post (the related-topic pills of its page).
latest_commentsThe post's latest comments: {id, text, timestamp, likes, owner_username, owner_id, owner_is_verified, gif_url}. On a post link's row up to 15 (with includeComments up to maxComments); on a profile's rows only with includeComments.
verdictOn a profile's row: COMPLETE (the profile's list ended: Instagram's own end flag), CAPPED (maxPostsPerProfile), DATE_LIMIT (the walk reached posts older than newerThan), NO_NEW_POSTS (onlyNewPosts: the walk reached the newest post an earlier run of the memory delivered), RUN_CAP (maxItems or the charge limit), PARTIAL (an error or the time limit stopped it; the note says which).
posts_listedOn a profile's row: the posts of its list the walk looked at.
posts_deliveredOn a profile's row: the posts delivered from it.
pinned_postsOn a profile's row: the pinned posts among them.
reels_with_playsOn a profile's row: the delivered reels that carry their plays.
noteWhy, in words: the reason of a free row, what a walk did not read and why.
scraped_atWhen the row was read (UTC).

Example record:

{
"record_type": "post",
"status": "ok",
"input": "natgeo",
"input_type": "profile",
"profile": "natgeo",
"position": 24,
"id": "4008597565992995700",
"shortcode": "DehZj9wggt0",
"url": "https://www.instagram.com/reel/DehZj9wggt0/",
"type": "reel",
"product_type": "clips",
"timestamp": "2026-10-15T19:00:02Z",
"taken_at": 1792090802,
"caption": "Keeping up with a pack of hunting wolves is just another day at the office for @bertiegregory and his team.\n\n#WolvesUpClose with Bertie Gregory premieres Friday, November 6 at 8/7c on National Geographic. Streaming on @DisneyPlus and @hulu.",
"hashtags": [
"WolvesUpClose"
],
"mentions": [
"bertiegregory",
"DisneyPlus",
"hulu"
],
"caption_is_edited": false,
"alt": "Video by National Geographic on October 15, 2026. May be an image of wolf, screen, poster and text.",
"likes": 52551,
"likes_hidden": false,
"comments_count": 394,
"comments_disabled": false,
"plays": 1365388,
"plays_status": "served",
"video_duration": 30.037,
"has_audio": true,
"engagement_rate": 0.0001973,
"owner_username": "natgeo",
"owner_id": "787132",
"owner_full_name": "National Geographic",
"owner_is_verified": true,
"owner_profile_pic_url": "https://scontent-lax3-2.cdninstagram.com/v/t51.82787-19/683576066_18653628823019133_9051036240972105113_n.jpg?stp=dst-jpg_s150x150_tt6&efg=eyJ2ZW5jb2RlX3RhZyI6InByb2ZpbGVfcGljLmRqYW5nby40MDAuYzIifQ&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_cat=1&_nc_oc=Q6cZ2gGIu2AAIfvqb_qru1YNeVSltqzzlfg3q7mRYg0BpTh-Hford4St2YUa751WTgvOuec&_nc_ohc=3hWKJm3WbW0Q7kNvwHlU5hP&_nc_gid=D2Uom52ccYS92qkQv1Uuvg&edm=APs17CUBAAAA&ccb=7-5&oh=00_AQOVgoqmr4j4loX7a4unOepSshwHhvMpoY9141_eUbpEuA&oe=6ADE8DAB&_nc_sid=10d13b",
"owner_followers": 268325315,
"coauthors": [
{
"username": "bertiegregory",
"is_verified": true
},
{
"username": "natgeoanimals",
"is_verified": true
}
],
"coauthor_ids": [
"1284886006",
"1029649300"
],
"is_paid_partnership": false,
"is_pinned": true,
"tagged_users": [
{
"username": "natgeoanimals",
"full_name": "National Geographic Animals",
"id": "1029649300"
},
{
"username": "bertiegregory",
"full_name": "Bertie Gregory",
"id": "1284886006"
}
],
"location_name": null,
"location_id": null,
"location_lat": null,
"location_lng": null,
"display_url": "https://scontent-lax3-2.cdninstagram.com/v/t51.82787-15/849098148_18708313780019133_730867734770692515_n.jpg?stp=dst-jpg_e15_tt6&_nc_cat=111&ig_cache_key=NDAwODU5NzU2NTk5Mjk5NTcwMDE4NzA4MzEzNzc3MDE5MTMz.3-ccb7-5&ccb=7-5&_nc_sid=58cdad&efg=eyJ2ZW5jb2RlX3RhZyI6IkNMSVBTLnhwaWRzLjEwODAuc2RyLnZpZGVvX2RlZmF1bHRfY292ZXJfZnJhbWUuQzMifQ%3D%3D&_nc_ohc=rG4S4sXViaUQ7kNvwE5V2uj&_nc_oc=AdrKEiKYpn0XcPIN4gYWhlTeHevXStfRhQns_6itwLUAAwTWqFNVB6b5aHNJe1vtTCo&_nc_ad=z-m&_nc_cid=0&_nc_zt=23&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_gid=D2Uom52ccYS92qkQv1Uuvg&_nc_ss=7a20f&oh=00_AQNalScRgJgRgiZU6Ce2XepdSpcRdBJmYnRI2ABAU4JbDQ&oe=6ADEB64C",
"images": [
"https://scontent-lax3-2.cdninstagram.com/v/t51.82787-15/849098148_18708313780019133_730867734770692515_n.jpg?stp=dst-jpg_e15_tt6&_nc_cat=111&ig_cache_key=NDAwODU5NzU2NTk5Mjk5NTcwMDE4NzA4MzEzNzc3MDE5MTMz.3-ccb7-5&ccb=7-5&_nc_sid=58cdad&efg=eyJ2ZW5jb2RlX3RhZyI6IkNMSVBTLnhwaWRzLjEwODAuc2RyLnZpZGVvX2RlZmF1bHRfY292ZXJfZnJhbWUuQzMifQ%3D%3D&_nc_ohc=rG4S4sXViaUQ7kNvwE5V2uj&_nc_oc=AdrKEiKYpn0XcPIN4gYWhlTeHevXStfRhQns_6itwLUAAwTWqFNVB6b5aHNJe1vtTCo&_nc_ad=z-m&_nc_cid=0&_nc_zt=23&_nc_ht=scontent-lax3-2.cdninstagram.com&_nc_gid=D2Uom52ccYS92qkQv1Uuvg&_nc_ss=7a20f&oh=00_AQNalScRgJgRgiZU6Ce2XepdSpcRdBJmYnRI2ABAU4JbDQ&oe=6ADEB64C"
],
"carousel": [],
"carousel_count": null,
"video_url": "https://scontent-lax7-1.cdninstagram.com/o1/v/t2/f2/m86/AQPmzWFyFbdQEUSTj3bdWcEdgJrx_FjybCJC5cI7WvE3uOZ2rh-VGb63KeYgGbUc5ovqd29n4IW7ZzvvAKPIhr9epNJVMFjcrAsy4GI.mp4?_nc_cat=101&_nc_sid=5e9851&_nc_ht=scontent-lax7-1.cdninstagram.com&_nc_ohc=QpkVpzHAn_oQ7kNvwEJowvT&efg=eyJ2ZW5jb2RlX3RhZyI6Inhwdl9wcm9ncmVzc2l2ZS5JTlNUQUdSQU0uQ0xJUFMuQzMuNzIwLmRhc2hfYmFzZWxpbmVfMV92MSIsInhwdl9hc3NldF9pZCI6MTg3MDgyNzIzMzIwMTkxMzMsImFzc2V0X2FnZV9kYXlzIjo1LCJ2aV91c2VjYXNlX2lkIjoxMDA5OSwiZHVyYXRpb25fcyI6MzAsInVybGdlbl9zb3VyY2UiOiJ3d3cifQ%3D%3D&ccb=17-1&vs=a8143723d0c4400&_nc_vs=HBksFQIYUmlnX3hwdl9yZWVsc19wZXJtYW5lbnRfc3JfcHJvZC83MzFCMTY4MzlGODE0MTEyOTMzQjkwMDQ2ODYwQUFEOV92aWRlb19kYXNoaW5pdC5tcDQVAALIARIAFQIYUWlnX3hwdl9wbGFjZW1lbnRfcGVybWFuZW50X3YyLzAzNDgzMzFGNTA0NTRFNkQ5RUM2NjREMTM4REUwOEYzX2F1ZGlvX2Rhc2hpbml0Lm1wNBUCAsgBEgAoABgAGwKIB3VzZV9vaWwBMRJwcm9ncmVzc2l2ZV9yZWNpcGUBMRUAACb61rDr4sS7QhUCKAJDMywXQD4HrhR64UgYEmRhc2hfYmFzZWxpbmVfMV92MREAdf4HZeadAQA&_nc_gid=D2Uom52ccYS92qkQv1Uuvg&_nc_zt=28&_nc_ss=7a20f&oh=00_AQMc0GQeLtuzAXYuR--d3DhkC81H2nuoWdcOq0u4IEQkMg&oe=6ADAA7E2",
"audio_url": "https://scontent-lax3-1.cdninstagram.com/o1/v/t2/f2/m78/AQMigN4zZ5qE3ZMxWEAeFmpCJnjzPNAFgzv_oD9D8ewf9o51S1kqsqG2ArP2PChzrVzhbrs5BgnUl1fAlc7v3YLnlcMx92At3yGJPGk.mp4?_nc_cat=110&_nc_sid=9ca052&_nc_ht=scontent-lax3-1.cdninstagram.com&_nc_ohc=bnEHLAqnLKYQ7kNvwGa1xxS&efg=eyJ2ZW5jb2RlX3RhZyI6ImlnLXhwdmRzLmNsaXBzLmMyLUMzLmRhc2hfbG5faGVhYWNfdmJyM19hdWRpbyIsInZpZGVvX2lkIjpudWxsLCJvaWxfdXJsZ2VuX2FwcF9pZCI6OTM2NjE5NzQzMzkyNDU5LCJjbGllbnRfbmFtZSI6ImlnIiwieHB2X2Fzc2V0X2lkIjoxODcwODI3MjMzMjAxOTEzMywiYXNzZXRfYWdlX2RheXMiOjUsInZpX3VzZWNhc2VfaWQiOjEwMDk5LCJkdXJhdGlvbl9zIjozMCwiYml0cmF0ZSI6NzU5NDgsInVybGdlbl9zb3VyY2UiOiJ3d3cifQ%3D%3D&ccb=17-1&_nc_gid=D2Uom52ccYS92qkQv1Uuvg&_nc_ss=7a20f&_nc_zt=28&oh=00_AQNKCQKFwt4GBMwA67slQL0GTq92Fhx5a4Iu5N7xRU0tbA&oe=6ADA9D0F",
"media_urls_expire_at": "2026-10-23T00:18:42Z",
"width": 1080,
"height": 1920,
"music": {
"title": "Original audio",
"artist": "natgeo",
"audio_id": "28762088093432459",
"uses_original_audio": true
},
"shared_to_facebook": false,
"ai_label": null,
"topics": [
"wolves up close",
"bertie gregory",
"national geographic",
"hudson bay",
"wolf hunting moose",
"arctic nature"
],
"latest_comments": null,
"verdict": null,
"posts_listed": null,
"posts_delivered": null,
"pinned_posts": null,
"reels_with_plays": null,
"note": null,
"scraped_at": "2026-10-21T14:59:56Z"
}

Pricing

Pay for the posts and reels a run delivers, plus one run start; no platform usage is billed to you. The post price falls with your Apify plan.

eventunitFreeStarterScaleBusiness
Post or reel (post)per 1,000$2.70$2.295$1.89$1.35
Run start (start)per run$0.0013$0.0013$0.0013$0.0013

A post or a reel is $2.70 per 1,000 on the Free plan ($0.0027 a row) and $2.295 on Starter, whatever input gave it (a profile or a link) and whatever it carries: the latest comments of a link's row, and the comments includeComments adds (up to maxComments), are inside the row's price. Each run that delivers a post also pays the run start, $0.0013 once; a run that delivers none pays nothing at all. So a profile's 12 newest posts cost $0.0337 on the Free plan and $0.02884 on Starter; 25 reels $0.0688 and $0.058675; 1,000 posts $2.7013 and $2.2963; several profiles and links in one run share one start. Free: every profile's summary row, a private, missing or age-gated profile, a profile with no post, a deleted post, a value that names nothing, a post given twice (one row, one charge), a post a filter or onlyNewPosts leaves out, and a profile left unread at a cap or the time limit. A run that stops early (a timeout, an abort) is charged for the rows it delivered. maxItems counts the charged rows. Before the first request the log names the price of a row at your plan, the run start and, when a cap bounds the run (maxPostsPerProfile, the post links, maxItems), the most it can cost. With maxPostsPerProfile 0 (every post) and no maxItems a run has no bound but the profiles' own lists, and Instagram shows no post count logged out: a profile holds from none to many thousands (on our runs: a profile with no post, one with 4, a restaurant's whole list of 2,045 in 99 seconds). Set maxPostsPerProfile, maxItems or a charge limit for a lower maximum. Enterprise plans have their own rates — the Actor's Pricing tab shows the price for your plan.

Usage patterns

  • What to give — profiles: usernames (natgeo, @natgeo), profile links (instagram.com/natgeo/, parameters such as ?igsh= dropped), a reels tab (instagram.com/natgeo/reels/: reels only) or numeric profile ids. postUrls: post and reel links in any form (/p/, /reel/, /reels/, /tv/, with the owner's username in the path, ?img_index=) or bare shortcodes. A value repeated is read once; a value that names nothing (a story, a tagged tab, a hashtag or location page, another site) is a free invalid_input row that says why. A share link (instagram.com/share/…) is not read in this version: open it and paste the link it lands on.
  • How many posts — maxPostsPerProfile (12 by default; 0 = all) takes each profile's newest posts by date; a pinned post counts at its own date, and a post a filter leaves out does not count. contentType picks posts and reels, posts only, or reels only. newerThan and olderThan (a date, a date and time, or 7 days) set a window; the walk stops at the first unpinned post older than newerThan. maxItems caps the charged rows of the whole run.
  • Filters — minLikes, minComments and minPlays keep a post only above a count (a hidden like count and a post without plays do not pass a minimum above 0); skipPinned leaves the pinned posts out. A post left out is free and frees its place in maxPostsPerProfile.
  • Comments — A post link's row carries the post's latest comments (up to 15) at no extra call. Switch on includeComments to add them to a profile's rows too, up to maxComments a post (200 at most; past 15 they are read 14 a call), with the location's coordinates and the edited flag: it reads each post once more, so the run takes longer. Replies to comments are not shown to logged-out viewers.
  • Scheduled runs: only the new posts — Save a task with onlyNewPosts (and a stateStoreName to name the memory) and schedule it: the walk stops at the newest post an earlier run of that memory delivered (the profile's verdict NO_NEW_POSTS), so each run delivers the posts that appeared since the last one and nothing older; a pinned post the memory knows is skipped free and does not stop the walk. The memory keeps the newest 20,000 post ids.

Input configuration

fieldtypedefaultwhat it does
profilesarray[]Instagram profiles, one per line: a username (natgeo, @natgeo), a profile link (https://www.instagram.com/natgeo/, any ?igsh= or other parameter is dropped), a profile's reels tab (instagram.com/natgeo/reels/: that profile's reels only) or a numeric profile id (787132). Each gives its newest maxPostsPerProfile posts and reels. A post link pasted here is read as a post. A private, missing or age-gated profile, or a value that names nothing, is a free row that says why.
postUrlsarray[]Posts and reels, one per line, in any form: https://www.instagram.com/p/DeuOR-CjKE9/, a /reel/, /reels/ or /tv/ link, a link with the owner's username in it, a link with ?img_index=, or a bare shortcode (DeuOR-CjKE9). Each gives one row with the post's latest comments. The same post given twice, or also in a profile's posts, is one row. A share link (instagram.com/share/…) is not read in this version: open it and paste the link it lands on.
maxPostsPerProfileinteger12How many of each profile's newest posts and reels, by date; 0 = every post to the end of the profile's list. A pinned post counts at its own date: an old pin never takes the place of a newer post. A post left out by a filter does not count. The profile's summary row says whether the list ended (COMPLETE) or this cap came first (CAPPED).
contentTypeall / posts / reels"all"All posts and reels as the profile's grid lists them, posts only (photos, carousels and long videos, no reels), or reels only (the profile's reels tab: every reel with its plays, the tab's own pinned reels at their dates). A profile link that ends in /reels/ reads reels only whatever this says.
newerThanstring""Only posts published on or after this date: 2026-09-01, a date and time (2026-09-01T12:00:00Z), or a span back from now in lower case (7 days, 12 hours, 2 weeks, 3 months). The walk stops at the first unpinned post older than this; a pinned post older than it is skipped.
olderThanstring""Only posts published on or before this date (a date includes its whole day), in the same forms as newerThan. Newer posts are skipped and do not count toward maxPostsPerProfile.
skipPinnedbooleanfalseLeave out the posts the owner pinned to the top of the profile. Off = a pinned post is delivered at its own date, flagged is_pinned.
minLikesinteger0Keep a post only with at least this many likes (a post whose owner hides the likes is left out when this is above 0). A post left out is free and does not count toward maxPostsPerProfile. 0 = every post.
minCommentsinteger0Keep a post only with at least this many comments. Free when it leaves a post out. 0 = every post.
minPlaysinteger0Keep a reel only with at least this many plays; a photo, a carousel or a reel whose plays are not read is left out when this is above 0. Free when it leaves a post out. 0 = every post.
includeCommentsbooleanfalseAdd each post's latest comments (latest_comments: text, time, likes, author) to the rows of a profile, up to maxComments a post, with the location's coordinates and the edited flag. It reads each post once more, so a run is slower. A post link's row always carries the post's latest comments (up to 15); with this on, up to maxComments.
maxCommentsinteger15The latest comments read a post when includeComments is on: the first 15 come with the post, the rest 14 a call. Replies to comments are not shown to logged-out viewers.
onlyNewPostsbooleanfalseDeliver only the posts that appeared since an earlier run with the same memory: the walk stops at the newest post that run delivered (the profile's verdict NO_NEW_POSTS; a pinned post it knows is skipped free). For a scheduled task.
stateStoreNamestring""The name of the memory onlyNewPosts keeps (letters, digits, '.', '_', '-'); empty = a name derived from the inputs.
maxItemsinteger0The most charged rows (posts and reels) of the whole run, over every profile and link; 0 = no cap beyond maxPostsPerProfile and your charge limit.
proxyUrlstringYour own proxy (scheme://user:pass@host:port) for every call instead of ours. Leave empty: the Actor brings its own connections at no cost to you.

Errors

A run does not fail because of your input. When an input cannot be used — a date that is not a date, an id in the wrong form, two settings that contradict each other — the run ends Succeeded, charges nothing, and says what to change:

  • the status message starts with INPUT REJECTED;
  • the dataset holds one row, {"error": true, "code": "…", "message": "…"}, and no results;
  • the key-value store record ERROR holds the same object.

From code, check error on the first row before reading results.

A private, missing or age-gated profile, a deleted post or a link that names nothing is not an error: it gets one free row with its status, and the run goes on with the others. A run that ends Failed is a fault on our side, never your input; it charges nothing, and we are alerted.

FAQ

Does it get every post of a profile?

Every post the profile lists to logged-out viewers, newest first, to Instagram's own end of the list (maxPostsPerProfile 0), 50 posts a call: our source study walked a 1,066-post profile to its last post that way. The profile's summary row says how many posts were looked at and delivered and whether the list ended (COMPLETE) or a limit came first. A few posts a profile hides from its grid may not be listed (2 of 1,068 on that profile).

Why are some likes null?

The owner hid the like count on that post. Instagram then serves 0, which is not the count, so the row gives likes null and likes_hidden true. The comment count is still there.

Where do the plays come from?

From the profile's reels list, the play counter the app shows on each reel. A profile's reels get them as the walk reaches their date. A pasted reel link gets them from its owner's reels list, read up to 5 pages back (the newest 60 reels); an older pasted reel gets plays null with plays_status outside_walk. Instagram's web page shows a second, smaller view counter: it is not used as plays.

What does pinned mean here?

is_pinned is true on a post the profile's owner pinned to the top of the profile. Instagram's grid also marks a post that a coauthor pinned on their own profile; that is not counted as the owner's pin. A pinned post is delivered at its own date and counts toward maxPostsPerProfile only there. On a reels-only row, is_pinned is the reels tab's own pin.

What are the free rows?

One summary row per profile (record_type profile): ok with the verdict and the counts, or why it gave no post — not_found (two reads agree), restricted (age-gated or otherwise restricted logged out), private, no_posts, duplicate (the same profile as another input: walked once), failed, not_read. One row per post link that gave no post (not_found, restricted, duplicate when another input of the run delivered it, filtered) and per value that names nothing (invalid_input). Only posts are charged. The free rows share the dataset with the posts: for posts alone keep the rows whose record_type is post and status is ok.

Does it need an Instagram account, cookies or a login?

No. Everything is read from Instagram's public logged-out data with a plain HTTP client; nothing of yours is sent to Instagram. A private account's posts are not served logged out and come back as one free private row; a public collab post of another account that the private account's list may serve is not delivered (read it from that account's profile).

Does it give shares, saves or reposts?

No. Instagram does not serve the share, save and repost counts to logged-out viewers: the scrapers that sell them read them with a logged-in account. Each row has the likes, the comment count and, on a reel, the plays.

How long do the image and video URLs work?

Instagram signs every media URL with an expiry, 1 to 4 days after the read; each row gives that time in media_urls_expire_at. Download what you need before it.

I move from another Instagram scraper: which column is which?

The keys most Instagram post scrapers use map one to one: shortCode → shortcode, type → type, productType → product_type, caption, hashtags, mentions → the same, timestamp → timestamp, likesCount → likes (+ likes_hidden), commentsCount → comments_count, videoPlayCount → plays, videoDuration → video_duration, ownerUsername / ownerId / ownerFullName → owner_username / owner_id / owner_full_name, coauthorProducers → coauthors, paidPartnership → is_paid_partnership, isPinned → is_pinned, taggedUsers → tagged_users, displayUrl → display_url, childPosts / images → carousel / images, videoUrl → video_url, audioUrl → audio_url, musicInfo → music, alt → alt, isCommentsDisabled → comments_disabled, latestComments → latest_comments, inputUrl → input.

Can it read hashtags, locations, stories or tagged posts?

Not in this version. Stories and a profile's tagged posts are not served logged out; hashtag, keyword and location pages are not read yet. A link to one of them is a free invalid_input row that says so.

Integration

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('hyperbach/instagram-posts-scraper').call({"profiles": ["natgeo"], "maxPostsPerProfile": 12});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Python

from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('hyperbach/instagram-posts-scraper').call(run_input={'profiles': ['natgeo'], 'maxPostsPerProfile': 12})
items = client.dataset(run['defaultDatasetId']).list_items().items

CLI

$apify call hyperbach/instagram-posts-scraper --input '{"profiles": ["natgeo"], "maxPostsPerProfile": 12}'

REST

curl -X POST "https://api.apify.com/v2/acts/hyperbach~instagram-posts-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
-H 'Content-Type: application/json' -d '{"profiles": ["natgeo"], "maxPostsPerProfile": 12}'

Support

apify@hyperbach.com

This page is generated from the Actor's schemas and a live sample — it cannot describe a field the Actor does not have.