# Instagram Post Scraper

> Get the posts of any public Instagram profile, with caption, likes, comment count, date, photos, videos, tags and music.

Instagram Post Scraper gets the posts of any public Instagram profile, newest first, or single posts from their links. Each post is one row with caption, likes, comment count, date, photos, videos, carousel slides, tags, mentions and music. The newest comments of each post are an optional add-on, charged per comment.

- Web page: https://keklik.io/scrapers/instagram-post-scraper
- Apify Actor: `keklik/instagram-post-scraper` (https://apify.com/keklik/instagram-post-scraper)
- You enter: Usernames, profile links or post links
- You get: Caption, likes, comment count, date, photos, videos, tags and music
- Status: working, last tested 12 Nov 2026
- Needs: an Apify account (free is enough to try). No login to the site itself.

## Price per 1,000 posts

US dollars, on each Apify plan. Apify's platform costs are included. Add-ons are charged only when switched on in the input.

| Charge | Free | Starter | Scale | Business |
|---|---:|---:|---:|---:|
| Post: Each post the scraper saves. | $1.00 | $0.90 | $0.80 | $0.70 |
| Comment (add-on): Each comment added to a post when Comments per post is above 0. | $1.00 | $0.90 | $0.80 | $0.70 |

Apify's plans and a calculator: https://keklik.io/pricing.md

## Input

The run input is a JSON object with these keys:

- `urls` (array, required): 👤 Profiles or posts. One per line. A profile link or username returns that profile's posts, newest first. A post or reel link returns that one post.
- `maxPostsPerProfile` (integer, optional): 🔢 Max posts per profile. Stops each profile after this many posts. Leave it empty for all of them. Single post links are not affected.
- `maxResults` (integer, optional): 🛑 Max posts in total. Stops the whole run after this many posts. Leave it empty for no limit.
- `newerThan` (string, optional): 📅 Posted after. Posted after: a date such as 2026-01-31, or a period back from now such as 7 days or 3 months (24h, 7d and 2w work too). Each profile stops at its first older post.
- `postedBefore` (string, optional): 📅 Posted before. Pick a date, or a period back from now such as "1 year", to get only posts published before it. With Posted after, you get the posts between the two dates. Older dates take longer to reach on profiles with many posts; the posts passed over on the way are not charged.
- `skipPinnedPosts` (boolean, optional, default false): 📌 Skip pinned posts. Drops the posts pinned to the top of a profile.
- `commentsPerPost` (integer, optional, default 0): 💬 Comments per post ($). How many of each post's newest comments to add. 0 adds none. Each comment is charged and makes the run slower.

Example input:

```json
{
  "urls": [
    "starbucks"
  ],
  "maxPostsPerProfile": 10,
  "skipPinnedPosts": true,
  "commentsPerPost": 3
}
```

## Output: one row per post

48 fields in 9 groups.

### Post

- `url`: Post URL. Link to the post (https://www.instagram.com/p/<shortcode>/).
- `timestamp_datetime`: Posted. Publication time, ISO 8601 in UTC.
- `media_type`: Media type. "image", "video" or "carousel".
- `caption`: Caption. Caption text.
- `alt`: Alt text. Alt text of the image, when Instagram or the author set one.
- `product_type`: Product type. Instagram product type: "feed" (photo), "carousel_container" or "clips" (reel).
- `is_video`: Is video. True for videos and reels.
- `is_pinned`: Pinned. Pinned to the top of the scraped profile. Always false for single post URLs.
- `is_paid_partnership`: Paid partnership. Labelled as a paid partnership.
- `paid_partners`: Paid partner. The brands a paid partnership names ("Paid partnership with ..."), by username. Empty when it isn't one, and on some paid partnerships that name no brand.
- `dimensions`: Dimensions. Width x height of the main media, in pixels.
- `timestamp`: Posted (Unix). Publication time, Unix seconds.
- `post_id`: Post ID. Numeric Instagram media ID, as a string.
- `shortcode`: Shortcode. The code in the post URL.

### Author

- `owner_username`: Author username. Username of the author.
- `owner_full_name`: Author name. Display name of the author.
- `owner_id`: Author ID. Numeric Instagram user ID of the author, as a string.
- `owner_profile_pic_url`: Author profile picture. Profile picture URL of the author. Instagram image links expire.

### Likes and comments

- `likes_count`: Likes. Number of likes. Empty when the author hides like counts (like_and_view_counts_disabled is then true).
- `comments_count`: Comments. Number of comments Instagram shows on the post.
- `like_and_view_counts_disabled`: Likes hidden. True when the author hides this post's like and view counts. likes_count is then empty, because Instagram shows visitors no real number.

### Photos and videos

- `carousel_media_count`: Carousel slides. Number of slides, for carousels.
- `display_url`: Image URL. Main image or video cover URL. Instagram media links expire.
- `video_url`: Video URL. Video file URL, for videos and reels. Instagram media links expire.
- `reel_url`: Reel URL. Link to the reel (https://www.instagram.com/reel/<shortcode>/), for videos only.
- `image_1`: Slide 1 image. Image URL of carousel slide 1.
- `image_2`: Slide 2 image. Image URL of carousel slide 2.
- `image_3`: Slide 3 image. Image URL of carousel slide 3.
- `image_4`: Slide 4 image. Image URL of carousel slide 4.
- `image_5`: Slide 5 image. Image URL of carousel slide 5.

### Carousel slides

- `child_post_1`: Carousel slide 1. Carousel slide 1: id, alt, width, height, display_url, timestamp, media_type, video_url.
- `child_post_2`: Carousel slide 2. Carousel slide 2: id, alt, width, height, display_url, timestamp, media_type, video_url.
- `child_post_3`: Carousel slide 3. Carousel slide 3: id, alt, width, height, display_url, timestamp, media_type, video_url.
- `child_post_4`: Carousel slide 4. Carousel slide 4: id, alt, width, height, display_url, timestamp, media_type, video_url.
- `child_post_5`: Carousel slide 5. Carousel slide 5: id, alt, width, height, display_url, timestamp, media_type, video_url.
- `child_post_6`: Carousel slide 6. Carousel slide 6: id, alt, width, height, display_url, timestamp, media_type, video_url.
- `child_post_7`: Carousel slide 7. Carousel slide 7: id, alt, width, height, display_url, timestamp, media_type, video_url.
- `child_post_8`: Carousel slide 8. Carousel slide 8: id, alt, width, height, display_url, timestamp, media_type, video_url.
- `child_post_9`: Carousel slide 9. Carousel slide 9: id, alt, width, height, display_url, timestamp, media_type, video_url.
- `child_post_10`: Carousel slide 10. Carousel slide 10: id, alt, width, height, display_url, timestamp, media_type, video_url.

### Tags, mentions and hashtags

- `hashtags`: Hashtags. Hashtags in the caption, with the #.
- `mentions`: Mentions. Usernames mentioned in the caption, without the @.
- `coauthors`: Co-authors. Collab co-authors: id, username, full_name, is_verified, profile_pic_url.
- `user_tags`: Tagged accounts. Accounts tagged in the media: id, username, full_name, is_verified, profile_pic_url.

### Music

- `music`: Music. Audio of a reel or video: audio_id, song_name, artist_name, uses_original_audio, should_mute_audio, should_mute_audio_reason, is_trending_in_clips, is_explicit.

### Latest comments (add-on)

- `latest_comments`: Latest comments. Only with Comments per post set, for posts that have comments: the post's newest comments, newest first, each with comment_id, text, created_at (Unix seconds), likes_count, is_edited, is_covered (hidden by Instagram), giphy_id, giphy_url, owner_id, owner_username, owner_is_verified, owner_profile_pic_url.

### Scrape details

- `source_url`: Source input. The input this post came from, as you entered it.
- `scraping_time`: Scraped at. When the row was scraped, ISO 8601 in UTC.

## How it works

1. Type the profiles. A username or profile link gets its posts, newest first. A post link gets that one post.
2. Keep only the posts you want. Set a start date, how many posts to take from each profile, or leave out pinned posts.
3. Add comments if you need them. Choose how many of each post's newest comments to add. You pay per comment, and none are added unless you ask.
4. Download your list. As a spreadsheet, in Google Sheets, or into your reporting tool.

## Use cases

- Competitor tracking: What rival accounts post, how often and in which format, and which posts draw the most likes and comments.
- Vetting influencers: Engagement on each post, paid partnership labels, collab posts and the brands an influencer tags.
- Content planning: The captions, hashtags, formats and music behind the best posts in your niche.
- Research datasets: Every post from a set of accounts over a chosen period, for trend or sentiment analysis.

## Good to know

Instagram hides view and play counts from visitors who aren't logged in, so posts come without them. Comments are top-level only, and a carousel row keeps its first 10 slides. Image and video links expire, so download any media you need soon after the run.

## Questions

### How do I keep my first run small and cheap?

Start with one or two profiles and keep 🔢 Max posts per profile at 20, or set 🛑 Max posts in total: the run stops there. Leave 💬 Comments per post at 0 until you know you need comments. To follow a profile over time, schedule a run with 📅 Posted after set to 1 day so each run gets only the new posts.

### Is it legal to scrape Instagram?

This scraper collects only data that Instagram shows publicly to visitors who are not logged in. Posts can still contain personal data, such as usernames of tagged accounts or commenters. Personal data is protected by laws such as the GDPR in Europe, so make sure you have a lawful reason to collect and use it, and check with a lawyer if you are unsure.

### Do I need an Instagram account?

No. You don't need an Instagram account, a login or cookies.

### Can I scrape private Instagram profiles?

No. Instagram shows a private profile's posts only to approved followers, so private profiles are skipped.

### Can I get view or play counts?

Not on posts: Instagram does not show view counts to logged-out visitors. For reels, Instagram Reels Scraper gives the play count of each reel.

### How do comments work?

Set Comments per post to add each post's newest comments to its row. Comments come newest first and are top-level comments only: replies and the "most relevant" order are not available without logging in. A post with no comments has no latest_comments. If Instagram refuses a post's comments, the post is still saved without them, and the run's status message says how many posts missed their comments. You pay only for the comments you get. For many comments per post, or one row per comment, use the Instagram Comments Scraper.

### Why do some media links stop working?

Instagram image and video links expire after a while. Download the media soon after the run if you need to keep it.

### What happens when a run fails?

The run's status message says why, and the log names each input that failed. If a mistake in the input stops the run before anything is scraped, the run ends without results and its status message says what to change. Either way the Actor sends us a short error report, so we can fix the cause even if you never open an issue: the Actor's build, the reason, the counts from the end-of-run summary, whether your Apify plan is free or paid and, for an unexpected error, where in our code it happened. Web addresses and @usernames are blanked out first. The report never includes your input, your account, the pages you scraped or your results. Every run, failed or not, also sends one usage line as it ends, so we can see which options people use, what runs cost us and how well the data is filled: how many of each charged event it used (for example 120 full details) and the price your plan paid for them; the run's platform cost by item and its resources (time, memory, proxy traffic); whether it stopped at your maximum cost or maximum items; how it was started (Console, API, schedule, saved task, and for API runs the kind of tool, such as Make, Zapier or the Apify client, never the tool's full identity); our scraper's own retry and timing counts; and for each output field, how many results had it filled (counts only, never the values). That never includes your input, your account, your IP address, the pages you scraped or your results either. If you need help with a run, open an issue on the Issues tab and attach the run, or email support@kekliktech.com with the run's link; the run's log ends with both.

## Run it

- In Apify Console: https://apify.com/keklik/instagram-post-scraper
- With the Apify API: POST https://api.apify.com/v2/acts/keklik~instagram-post-scraper/run-sync-get-dataset-items?token=APIFY_TOKEN with the input JSON as the body. It returns the rows.
- From an AI agent: connect the Apify MCP server (https://mcp.apify.com), add the Actor `keklik/instagram-post-scraper` and call it with the input.
