Instagram Profile Post Scraper With Engagement Analytics avatar

Instagram Profile Post Scraper With Engagement Analytics

Pricing

from $2.99 / 1,000 results

Go to Apify Store
Instagram Profile Post Scraper With Engagement Analytics

Instagram Profile Post Scraper With Engagement Analytics

Instagram Profile Post Scraper extracts posts from any public Instagram profile, including captions, image/video URLs, hashtags, timestamps, and engagement metrics. Ideal for content analysis, influencer research, trend tracking, and automating structured Instagram post data collection

Pricing

from $2.99 / 1,000 results

Rating

0.0

(0)

Developer

SimpleAPI

SimpleAPI

Maintained by Community

Actor stats

1

Bookmarked

40

Total users

11

Monthly active users

17 hours ago

Last modified

Share

Instagram Post Scraper — Posts, Engagement Rate and Cadence Data

Instagram Post Scraper With Engagement Analytics pulls every public post from one or more Instagram profiles — captions, likes, comments, media links, hashtags, and timestamps — and layers on per-post engagement-rate math plus a profile-level posting-cadence summary (most common posting day and hour, posts per week). It targets social media managers, marketing agencies, and researchers who need structured post data and the engagement math Instagram itself doesn't surface anywhere in one place. Post collection works fully logged out — no Instagram account or developer credentials needed to start.

❓ What is Instagram Post Scraper With Engagement Analytics?

Instagram Post Scraper With Engagement Analytics is an Apify Actor that scrapes public Instagram posts for one or more profiles and returns structured JSON rows — one row per post, plus one optional profile-level analytics row per profile. No Instagram login is required to collect posts; an optional session cookie only improves the follower-based analytics fields for accounts that otherwise return them as null.

🔑 Key features:

  • 🔗 Flexible profile input — accepts full profile URLs, plain usernames, or post shortcodes in the same targets field; shortcodes are resolved to a username automatically.
  • 📝 Full post content — captions, hashtags, mentions, tagged users, image/video/carousel links, dimensions, and up to 10 recent comments per post.
  • 📈 Per-post engagement mathengagementRatePct, likeViewRatio, commentViewRatio, likeCommentRatio, and engagementVelocityPerHour, computed from likes, comments, views, and follower count.
  • 🗓️ Posting-cadence analytics — a profile-level row with average days between posts, posts/week, and the most common posting day and hour (UTC) — computed locally from the posts already collected, no extra request.
  • ⏱️ Date filteringpostsSince accepts an absolute date or a relative window (3 days, 2 weeks, 1 month).
  • 📌 Pin-aware filteringpinFilterMode includes, skips, or isolates pinned posts.
  • 🛡️ Residential proxy by default — prefilled and strongly recommended for reliable access.

Output rows land in the run's default Apify dataset, which the platform lets you export as JSON, CSV, Excel, or XML from the Console or via the API.

📦 What data can I extract with Instagram Post Scraper With Engagement Analytics?

The Actor returns two row types in the same dataset: a post row for every collected post, and one optional profile analytics row per profile summarizing engagement and cadence.

Post fields

FieldExample valueUse case
id"3241...845_71..."Unique post identifier
pk"3241...845"Instagram's internal media PK
shortcode"CzAbCdEfGh1"Short post code used in URLs
url / post_url"https://www.instagram.com/p/CzAbCdEfGh1/"Direct link to the post
type"Image", "Video", "Carousel"Media type for filtering
product_type"clips" or nullReels/clips vs standard post (null on the fast profile-page path)
is_videotrueVideo vs image flag
is_carouselfalseMulti-image/video post flag
is_pinnedtrue, false, or nullPin status — null unless pinFilterMode is skip or only (see caveat below)
has_audiotrueVideo audio flag; null for non-video posts
posted"01 Jan 2025, 12:00:00 pm"Human-readable UTC timestamp
taken_at_timestamp1735732800Unix timestamp, UTC
date"2025-01-01T12:00:00.000Z"ISO-8601 UTC timestamp
crawled_at / scraped_at"2026-07-30T10:15:22.123Z"When this row was fetched
caption"New drop 🔥 #ootd"Full caption text
accessibility_caption"Photo by ..."Instagram's auto-generated alt text
hashtags["ootd"]Hashtags parsed from the caption
mentions["brandname"]@mentions parsed from the caption
usertags{"in": [{"user": {"username": "..."}, "position": [0.5, 0.5]}]} or nullTagged accounts and their position in the image
likes_count / like_count15234Like count (both key aliases carry the same value)
comments_count / comment_count312Comment count (both key aliases carry the same value)
views_count / view_count98211 or nullVideo view count; null for image posts
video_duration14.5 or nullVideo length in seconds
video_links["https://.../video.mp4"]Direct video file URLs
video_versions[{"width":720,"height":1280,"url":"...","type":0}]Available video renditions
display_url / thumbnail / image"https://.../photo.jpg"Primary display image URL (same value, three keys)
image_links["https://.../1.jpg"]All image URLs (carousel children included)
images[{"url":"...","width":1080,"height":1350}]Image URL with dimensions
dimensions{"width":1080,"height":1350}Media dimensions
carousel_media_count4 or nullNumber of items in a carousel post
carousel_media[{"id":"...","is_video":false,"display_url":"...","video_url":null}] or nullPer-item carousel media
coauthor_producers[]Collaborator accounts, if any
owner{"id":"...","pk":"...","username":"...","profile_pic_url":"...","is_verified":false,"is_private":false}Poster's account summary
user{"pk":"...","id":"...","username":"...","full_name":"...","profile_pic_url":"...","hd_profile_pic_url_info":{"url":"..."},"is_verified":false,"is_private":false}Extended poster profile
account_username / account_verified / account_profile_picture"cristiano" / true / "https://..."Flattened poster identity fields
from_url"https://www.instagram.com/cristiano/"The profile URL this post was collected from
clips_metadatanull or {"audio_type":"original_sounds","music_info":{...},"original_sound_info":{...}}Reels/audio metadata; populated only for product_type: "clips" posts
comments / latest_comments[{"id":"...","text":"🔥🔥","created_at":1735732900,"user":{"username":"..."},"comment_like_count":4,"has_liked_comment":false}]Up to 10 most visible comments per post (fixed internally, not input-controlled)

📈 Engagement and analytics fields

Added to every post row when computeEngagementAnalytics is true (default). When it is false, these five keys are absent from the row entirely — not null, simply not written.

FieldExample valueUse case
engagementRatePct4.21 or null(likes + comments) ÷ followers × 100; null if follower count is unavailable
likeViewRatio0.156 or nullLikes ÷ views; null for non-video posts
commentViewRatio0.0032 or nullComments ÷ views; null for non-video posts
likeCommentRatio48.83 or nullLikes ÷ comments; null if comments is 0
engagementVelocityPerHour12.4(likes + comments) ÷ hours since posting

The profile-level roll-up (avgLikes, avgEngagementRatePct, topPostShortcode, contentTypeMixPct, …) and posting-cadence fields (postingCadenceDaysAvg, mostCommonPostDayOfWeekUtc, …) are covered in the profile row table in the 🔽 Output sample section below — they answer "which posts perform best" and "when does this account post," the two questions raw per-post counts can't answer on their own.

🌐 How does Instagram Post Scraper With Engagement Analytics differ from the official Instagram API?

Meta's Instagram Graph API cannot fetch posts or engagement data for an arbitrary public profile at all — it "cannot access Instagram consumer accounts (i.e., non-Business or non-Creator Instagram accounts)" and only returns data for Professional accounts that have explicitly authorized your app via Facebook Login (Instagram Graph API docs, developers.facebook.com, checked 2026-07-30).

FeatureInstagram Graph APIInstagram Post Scraper With Engagement Analytics
Accounts you can queryOnly Business/Creator accounts that authorized your appAny public profile, username, or post shortcode
SetupFacebook Login, app review, Facebook Page linkPaste usernames/URLs into targets — no account linking
Login required to runYes, by the account ownerNo — post collection works fully logged out
Engagement-rate mathNot provided — raw counts via Insights onlyengagementRatePct and 4 other ratios computed per post
Posting-cadence analyticsNot providedComputed per profile — avg days between posts, posts/week, common day/hour
Result ordering"Ordering results is not supported" per docsPosts returned in Instagram's own feed order; filterable by postsSince and pinFilterMode
Cost modelMeta developer platform, subject to app reviewApify pay-per-event, $0.001 per dataset row

Can the Instagram Graph API return posts from any public profile?

No. The Instagram Graph API only exposes data for Professional (Business or Creator) accounts that have explicitly granted your app access through Facebook Login — it cannot reach consumer accounts or any account you don't manage. In practice this rules it out for competitor tracking, influencer vetting, or any workflow that needs data from accounts outside your own portfolio. Instagram Post Scraper With Engagement Analytics covers that gap by reading the same public HTML and endpoints a logged-out browser sees: paste any public profile's URL or username into targets and it returns posts without Facebook Login, app review, or the target account's cooperation.

Use the Graph API when you manage the account yourself and need first-party Insights metrics tied to your Meta Business account. Use this Actor when you need post and engagement data from profiles you don't control.

📊 How to use data extracted from Instagram

Social media managers and marketers

Set targets to your own or a client's profile and postsSince to 1 month to pull the latest posts plus the profile analytics row. The avgEngagementRatePct, topPostShortcode, and contentTypeMixPct fields tell you which format (photo, video, carousel) is currently outperforming, without exporting Instagram's own insights manually.

Agencies auditing or benchmarking client and competitor accounts

Run one job with several targets — a client account plus its competitors — and compare each profile's postingCadenceDaysAvg, postsPerWeekAvg, and avgEngagementRatePct rows side by side. Because cadence analytics need no extra request, this scales to dozens of profiles per run without added cost per profile.

Market research and intelligence

Use hashtags, mentions, and type across a batch of profiles to map which content formats and collaborations a niche or category leans on, and pair engagementRatePct with mostCommonPostDayOfWeekUtc to identify posting patterns worth benchmarking against.

AI agents and automated pipelines

Call the Actor from a script or agent framework, filter the returned dataset rows for type != "profile_summary" to isolate post-level records, and feed caption, hashtags, and engagementRatePct into a summarization or trend-detection pipeline as structured input rather than scraped HTML.

🔼 Input sample

ParameterRequiredTypeDescriptionExample value
targetsNoarrayProfile URLs, usernames, or post shortcodes. Normalized to usernames automatically.["https://www.instagram.com/cristiano/", "mrbeast"]
postsPerProfileNointeger (min 0, max 10000)Posts to collect per profile. 0 = unlimited. Default 10.25
pinFilterModeNostring, enum: include, skip, onlyHow pinned posts are handled. Default include."skip"
postsSinceNostring (date picker)Absolute date (YYYY-MM-DD) or relative window (3 days, 2 weeks, 1 month). Leave empty for all available posts."2 weeks"
computeEngagementAnalyticsNobooleanAdds per-post engagement ratios and the profile roll-up row. Default true.true
computeCadenceAnalyticsNobooleanAdds posting-cadence fields to the profile row. Default true.true
startUrlsNoarrayLegacy name for targets. Kept for backward compatibility; targets wins if both are set.[]
maxPostsNointeger (min 0, max 10000)Legacy name for postsPerProfile.10
pinnedModeNostring, enum: include, skip, onlyLegacy name for pinFilterMode."include"
recentNostring (date picker)Legacy name for postsSince."3 days"
requestDelaySecondsNointeger (min 1, max 30)Base delay between requests/pages. Default 2.2
maxRetryAttemptsNointeger (min 1, max 10)Retries per proxy tier before escalating. Default 3.3
igSessionIdNostring (secret text)Optional Instagram sessionid cookie. Used only for the follower-count lookup behind engagementRatePct; never for post collection."58012345%3AaBcDeFgH..."
proxyConfigurationNoobject (proxy editor)Apify Proxy configuration. Prefilled to residential.{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}

No parameter is required — an empty input run falls back to the built-in defaults (@cristiano and mrbeast, 10 posts each).

{
"targets": ["https://www.instagram.com/cristiano/", "mrbeast"],
"postsPerProfile": 25,
"pinFilterMode": "include",
"postsSince": "2 weeks",
"computeEngagementAnalytics": true,
"computeCadenceAnalytics": true,
"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}

⚠️ Real constraint: setting proxyConfiguration.useApifyProxy to false has no effect — the Actor forces Apify Proxy back on internally before every run, since Instagram reliably blocks unproxied requests. Residential proxies are prefilled by default and are the only tier the Actor actually uses for these requests.

Common pitfall: requesting a large postsPerProfile (e.g. 500) with the default pinFilterMode: "include" does not guarantee 500 posts. The Actor first tries a fast path that reads whatever Instagram embeds on the profile's own page load; if that page returns any posts at all, the Actor uses exactly those and stops — it does not paginate further in include mode. Set pinFilterMode to skip or only to force the full paginated path (up to 100 pages of 12 posts each, i.e. up to 1,200 posts per profile before your postsPerProfile cap is applied), which is also what makes is_pinned a reliable true/false instead of null.

🔽 Output sample

Every row is typed, normalized JSON written to the run's default dataset — no HTML, no nested scraping artifacts. Export it as JSON, CSV, Excel, or XML from the Apify Console or via the API. A complete post row:

{
"id": "3241876543210987654_71234567",
"pk": "3241876543210987654",
"shortcode": "CzAbCdEfGh1",
"url": "https://www.instagram.com/p/CzAbCdEfGh1/",
"post_url": "https://www.instagram.com/p/CzAbCdEfGh1/",
"type": "Image",
"product_type": null,
"is_video": false,
"is_carousel": false,
"is_pinned": null,
"has_audio": null,
"posted": "01 Jan 2025, 12:00:00 pm",
"taken_at_timestamp": 1735732800,
"date": "2025-01-01T12:00:00.000Z",
"crawled_at": "2026-07-30T10:15:22.123Z",
"caption": "New drop out now 🔥 #ootd",
"accessibility_caption": "Photo by cristiano on January 01, 2025.",
"hashtags": ["ootd"],
"mentions": [],
"usertags": null,
"likes_count": 4521233,
"like_count": 4521233,
"comments_count": 18422,
"comment_count": 18422,
"views_count": null,
"view_count": null,
"video_duration": null,
"video_links": [],
"video_versions": [],
"display_url": "https://scontent.cdninstagram.com/photo.jpg",
"thumbnail": "https://scontent.cdninstagram.com/photo.jpg",
"image": "https://scontent.cdninstagram.com/photo.jpg",
"image_links": ["https://scontent.cdninstagram.com/photo.jpg"],
"images": [{"url": "https://scontent.cdninstagram.com/photo.jpg", "width": 1080, "height": 1350}],
"dimensions": {"height": 1350, "width": 1080},
"carousel_media_count": null,
"carousel_media": null,
"coauthor_producers": [],
"owner": {"id": "173560420", "pk": "173560420", "username": "cristiano", "profile_pic_url": "https://...", "is_verified": true, "is_private": false},
"user": {"pk": "173560420", "id": "173560420", "username": "cristiano", "full_name": "Cristiano Ronaldo", "profile_pic_url": "https://...", "hd_profile_pic_url_info": {"url": "https://..."}, "is_verified": true, "is_private": false},
"account_username": "cristiano",
"account_verified": true,
"account_profile_picture": "https://...",
"from_url": "https://www.instagram.com/cristiano/",
"clips_metadata": null,
"comments": [{"id": "17998877", "text": "🔥🔥🔥", "created_at": 1735732900, "created_at_utc": 1735732900, "user": {"pk": "998877", "username": "fan_account", "full_name": "", "profile_pic_url": "https://...", "is_verified": false, "is_private": false}, "comment_like_count": 4, "has_liked_comment": false}],
"latest_comments": [{"id": "17998877", "text": "🔥🔥🔥", "created_at": 1735732900, "created_at_utc": 1735732900, "user": {"pk": "998877", "username": "fan_account", "full_name": "", "profile_pic_url": "https://...", "is_verified": false, "is_private": false}, "comment_like_count": 4, "has_liked_comment": false}],
"scraped_at": "2026-07-30T10:15:22.123Z",
"engagementRatePct": 4.21,
"likeViewRatio": null,
"commentViewRatio": null,
"likeCommentRatio": 245.4,
"engagementVelocityPerHour": 12.4
}

One profile-level analytics row is pushed per profile when computeEngagementAnalytics and/or computeCadenceAnalytics is enabled and at least one post was collected. Its fields depend on which toggles are on and whether the follower-count lookup succeeded — keys are omitted (not nulled) when their source data isn't available:

{
"type": "profile_summary",
"isChild": false,
"username": "cristiano",
"profileUrl": "https://www.instagram.com/cristiano/",
"scrapedAt": "2026-07-30T10:15:22.500Z",
"fullName": "Cristiano Ronaldo",
"biography": "...",
"externalUrl": "https://...",
"followersCount": 630000000,
"followingCount": 580,
"postsCountBio": 3800,
"isVerified": true,
"isBusinessAccount": true,
"businessCategoryName": "Athlete",
"profilePicUrlHD": "https://...",
"postsAnalyzed": 25,
"avgLikes": 3812440.2,
"avgComments": 15230.6,
"avgEngagementRatePct": 3.98,
"topPostShortcode": "CzAbCdEfGh1",
"topPostEngagementRatePct": 4.21,
"contentTypeMixPct": {"photo": 60.0, "video": 32.0, "carousel": 8.0},
"postingCadenceDaysAvg": 1.8,
"postsPerWeekAvg": 3.889,
"mostCommonPostDayOfWeekUtc": "Friday",
"mostCommonPostHourUtc": 18,
"postDayDistributionUtc": {"Friday": 8, "Monday": 4, "Sunday": 3},
"postHourDistributionUtc": {"14": 5, "18": 9, "21": 3}
}

To keep post rows and profile rows separate downstream, filter on item.type === "profile_summary" — every post row's own type value is "Image", "Video", or "Carousel" instead.

🎯 How do you filter and target specific posts?

targets accepts any mix of full profile URLs, bare usernames, or post shortcodes — use shortcodes when you only have a link to one post and want the actor to resolve the poster automatically. postsSince narrows results by recency using either an absolute date or a relative window, which matters most when you're rerunning the same profiles on a schedule and only want what's new since the last run.

pinFilterMode trades speed for accuracy: include (default) uses the faster profile-page read but leaves is_pinned unknown (null); skip and only force the slower, fully paginated path, where pin status is detected correctly. Use skip to exclude an account's evergreen pinned content from performance analysis, or only to audit exactly what's pinned.

postsPerProfile is the volume control, but its ceiling depends on pinFilterMode: in include mode it's capped by whatever Instagram embeds on the profile's own page load regardless of the number you set; in skip/only mode the Actor paginates properly, up to 1,200 posts per profile (100 pages × 12 posts) before your postsPerProfile value trims the result.

Three real examples:

{ "targets": ["nike"], "postsSince": "1 month", "postsPerProfile": 0 }

All of Nike's posts from the last month, unlimited count.

{ "targets": ["nike", "adidas", "puma"], "pinFilterMode": "skip", "postsPerProfile": 50, "computeCadenceAnalytics": true }

50 non-pinned posts per competitor with posting-cadence analytics, for a benchmarking run across three profiles.

{ "targets": ["ABC123DEF45"], "pinFilterMode": "only", "computeEngagementAnalytics": false }

Resolve a single post shortcode to its owner and return only that profile's pinned posts, skipping engagement math to save the extra follower-count request.

▶️ Want to try other Instagram scrapers?

Scraper nameWhat it extracts
Instagram Posts Scraper With Tagged Places & CoordinatesInstagram posts with tagged-location and coordinate data
Instagram Story Details Scraper With Media DetailsInstagram Stories and their media details
TikTok User Profile Scraper: Business LeadsTikTok user profile and business-lead data
Snapchat User Stories Scraper With Creator Profile InsightsSnapchat user stories and creator profile insights

💻 How to extract Instagram data programmatically

This Actor runs as a standard Apify Actor: one API call starts the run, and the dataset it writes to is available as structured JSON through the same client.

Python example

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_API_TOKEN>")
run_input = {
"targets": ["cristiano", "mrbeast"],
"postsPerProfile": 20,
"postsSince": "2 weeks",
"computeEngagementAnalytics": True,
}
run = client.actor("<YOUR_USERNAME>/instagram-profile-post-scraper-with-engagement-analytics").call(
run_input=run_input
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
if item.get("type") == "profile_summary":
continue
print(item["shortcode"], item["likes_count"], item.get("engagementRatePct"))

Export to spreadsheets or CRM

Export the dataset as CSV directly from the Apify Console (or via the API's dataset export endpoint) and map columns straight to your destination: account_username → account/company name, caption → note/description, likes_count and engagementRatePct → performance columns, url → the record's source link.

Scraping publicly visible Instagram posts and profile pages is generally lawful — these are pages Instagram serves to any logged-out visitor, not private or access-controlled content. That said, posts, captions, comments, and profile bios frequently contain personal data (usernames, full names, profile photos, comment authors), so GDPR and CCPA govern how you store and use it, not just whether you can read it. You need a lawful basis for any storage or downstream use of that personal data, and Instagram's own Terms of Use impose separate contractual restrictions on automated access regardless of copyright or privacy law. Consult legal counsel for commercial applications involving bulk storage of personal data.

❓ FAQ

How current is the post and engagement data?

Every field is fetched live at request time — crawled_at and scraped_at on post rows, and scrapedAt on the profile row, record exactly when the Actor fetched it. There is no caching layer; rerunning the Actor re-fetches current likes, comments, and follower counts.

Can I get comments along with the posts?

Yes — comments and latest_comments return up to 10 of the most visible comments per post (this cap is fixed internally and is not an input parameter), each with the commenter's username, text, timestamp, and like count.

How accurate is the engagement-rate and follower-count data?

The figures reflect Instagram's own public counts and follower totals at the moment of the request. engagementRatePct and the other follower-based fields return null when the follower-count lookup fails or is rate-limited — supplying igSessionId raises the retry budget for that lookup (14 attempts vs. 6 when logged out) and improves completeness, but post collection itself is unaffected either way.

How many posts can I get per run?

postsPerProfile accepts 0–10,000 per profile (0 = unlimited), but the real ceiling depends on pinFilterMode. In the default include mode, the Actor only returns what Instagram embeds on the profile's own page load and does not paginate further. In skip or only mode it paginates fully, up to 1,200 posts per profile (100 pages × 12 posts) before your postsPerProfile value trims the result.

How do I get only pinned posts, or only recent posts?

Set pinFilterMode to only for pinned posts alone, or skip to exclude them — both force the Actor's accurate pin-detection path. Combine with postsSince (an absolute date or a relative window like "2 weeks") to also bound results by recency; the two filters apply independently.

Does Instagram Post Scraper With Engagement Analytics work with Claude, ChatGPT, and AI agent frameworks?

It's callable as a standard Apify Actor run via apify-client (Python or JS) or the REST API, so any agent framework that can make an HTTP call or use an Apify tool integration can trigger a run and read back the resulting dataset. It is not currently listed as an MCP server.

Can I use this without an Instagram API key or developer account?

Yes. Post collection needs no Instagram account, API key, or developer credentials at all — the only optional credential is igSessionId, an Instagram session cookie that improves completeness of the follower-based analytics fields, never a requirement for scraping posts.

Does the profile need to be public?

Post collection is designed for public profiles; the Actor logs an error and returns no posts for a username when nothing is reachable, which is the behavior a private or unreachable account produces.

Why is engagementRatePct sometimes null even when computeEngagementAnalytics is true?

It's null whenever the follower-count lookup that feeds it didn't succeed for that profile in that run — the field still appears with a null value in that case, it just can't be computed without a follower count. Retrying, or supplying igSessionId, both raise the odds of a successful lookup.

Conclusion

Instagram Post Scraper With Engagement Analytics turns raw public post data into the engagement-rate and posting-cadence numbers that competitor tracking, influencer vetting, and content-performance reviews actually need, without an Instagram login or Meta app review. It fits social media managers, agencies benchmarking multiple accounts, and researchers who need structured, schema-consistent JSON rather than scraped HTML. Start a run from the Apify Console or trigger it with apify-client using the input fields documented above.