Instagram Profile Post Scraper With Engagement Analytics
Pricing
from $2.99 / 1,000 results
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
Maintained by CommunityActor stats
1
Bookmarked
40
Total users
11
Monthly active users
17 hours ago
Last modified
Categories
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
targetsfield; 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 math —
engagementRatePct,likeViewRatio,commentViewRatio,likeCommentRatio, andengagementVelocityPerHour, 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 filtering —
postsSinceaccepts an absolute date or a relative window (3 days,2 weeks,1 month). - 📌 Pin-aware filtering —
pinFilterModeincludes, 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
| Field | Example value | Use 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 null | Reels/clips vs standard post (null on the fast profile-page path) |
is_video | true | Video vs image flag |
is_carousel | false | Multi-image/video post flag |
is_pinned | true, false, or null | Pin status — null unless pinFilterMode is skip or only (see caveat below) |
has_audio | true | Video audio flag; null for non-video posts |
posted | "01 Jan 2025, 12:00:00 pm" | Human-readable UTC timestamp |
taken_at_timestamp | 1735732800 | Unix 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 null | Tagged accounts and their position in the image |
likes_count / like_count | 15234 | Like count (both key aliases carry the same value) |
comments_count / comment_count | 312 | Comment count (both key aliases carry the same value) |
views_count / view_count | 98211 or null | Video view count; null for image posts |
video_duration | 14.5 or null | Video 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_count | 4 or null | Number of items in a carousel post |
carousel_media | [{"id":"...","is_video":false,"display_url":"...","video_url":null}] or null | Per-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_metadata | null 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.
| Field | Example value | Use case |
|---|---|---|
engagementRatePct | 4.21 or null | (likes + comments) ÷ followers × 100; null if follower count is unavailable |
likeViewRatio | 0.156 or null | Likes ÷ views; null for non-video posts |
commentViewRatio | 0.0032 or null | Comments ÷ views; null for non-video posts |
likeCommentRatio | 48.83 or null | Likes ÷ comments; null if comments is 0 |
engagementVelocityPerHour | 12.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).
| Feature | Instagram Graph API | Instagram Post Scraper With Engagement Analytics |
|---|---|---|
| Accounts you can query | Only Business/Creator accounts that authorized your app | Any public profile, username, or post shortcode |
| Setup | Facebook Login, app review, Facebook Page link | Paste usernames/URLs into targets — no account linking |
| Login required to run | Yes, by the account owner | No — post collection works fully logged out |
| Engagement-rate math | Not provided — raw counts via Insights only | engagementRatePct and 4 other ratios computed per post |
| Posting-cadence analytics | Not provided | Computed per profile — avg days between posts, posts/week, common day/hour |
| Result ordering | "Ordering results is not supported" per docs | Posts returned in Instagram's own feed order; filterable by postsSince and pinFilterMode |
| Cost model | Meta developer platform, subject to app review | Apify 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
| Parameter | Required | Type | Description | Example value |
|---|---|---|---|---|
targets | No | array | Profile URLs, usernames, or post shortcodes. Normalized to usernames automatically. | ["https://www.instagram.com/cristiano/", "mrbeast"] |
postsPerProfile | No | integer (min 0, max 10000) | Posts to collect per profile. 0 = unlimited. Default 10. | 25 |
pinFilterMode | No | string, enum: include, skip, only | How pinned posts are handled. Default include. | "skip" |
postsSince | No | string (date picker) | Absolute date (YYYY-MM-DD) or relative window (3 days, 2 weeks, 1 month). Leave empty for all available posts. | "2 weeks" |
computeEngagementAnalytics | No | boolean | Adds per-post engagement ratios and the profile roll-up row. Default true. | true |
computeCadenceAnalytics | No | boolean | Adds posting-cadence fields to the profile row. Default true. | true |
startUrls | No | array | Legacy name for targets. Kept for backward compatibility; targets wins if both are set. | [] |
maxPosts | No | integer (min 0, max 10000) | Legacy name for postsPerProfile. | 10 |
pinnedMode | No | string, enum: include, skip, only | Legacy name for pinFilterMode. | "include" |
recent | No | string (date picker) | Legacy name for postsSince. | "3 days" |
requestDelaySeconds | No | integer (min 1, max 30) | Base delay between requests/pages. Default 2. | 2 |
maxRetryAttempts | No | integer (min 1, max 10) | Retries per proxy tier before escalating. Default 3. | 3 |
igSessionId | No | string (secret text) | Optional Instagram sessionid cookie. Used only for the follower-count lookup behind engagementRatePct; never for post collection. | "58012345%3AaBcDeFgH..." |
proxyConfiguration | No | object (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 name | What it extracts |
|---|---|
| Instagram Posts Scraper With Tagged Places & Coordinates | Instagram posts with tagged-location and coordinate data |
| Instagram Story Details Scraper With Media Details | Instagram Stories and their media details |
| TikTok User Profile Scraper: Business Leads | TikTok user profile and business-lead data |
| Snapchat User Stories Scraper With Creator Profile Insights | Snapchat 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 ApifyClientclient = 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":continueprint(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.
⚖️ Is it legal to scrape Instagram?
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.