# Instagram Posts Scraper: Profiles, Reels, No Login (`automation_craft/instagram-posts-scraper`) Actor

Instagram posts scraper for public profiles, no login: each profile's newest posts and reels back to its first post, or single posts by URL. Rows carry caption, hashtags, likes, comments, reel plays, carousels, location, tagged users and collab flags. Excel, CSV or API, scheduled; pay per post.

- **URL**: https://apify.com/automation_craft/instagram-posts-scraper.md
- **Developed by:** [Automation Craft](https://apify.com/automation_craft) (community)
- **Categories:** Social media, Marketing, Automation
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.29 / 1,000 posts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### Instagram Posts Scraper: Profiles, Reels, No Login

**Instagram posts scraper** for public profiles, with no login, no cookies and no Instagram account: give usernames or profile URLs and get each profile's newest posts and reels in the order of its grid, as many as you ask for, back to the profile's first post, with reel play counts. Give post or reel URLs and get each post with its first comments. Pay per post, from $0.29 per 1,000.

Built for brand and competitor tracking, influencer and creator research, content and hashtag analysis, social media reporting and AI data sets. Name a memory and schedule the run: each run then delivers and bills only the posts it has not delivered before. Rows carry what Instagram shows a visitor who is not logged in; use them under the rules that apply to you.

#### At a glance

- **Deep walks.** Measured on 2026-10-11: one profile walked to its first post (442 posts, back to 2010-10-28) in 54 seconds, and 1,000 posts of a bakery's profile in one run in 140 seconds.
- **Both date ends.** Keep posts from a start date, up to an end date, or both, to the minute (UTC), or "7 days", "6 hours". An old window is reached in one or two calls: the walk jumps to the end date instead of paging through every newer post.
- **Reel play counts** joined from the profile's reels tab at no extra charge, with a status that says why a reel has none.
- **Collab and pinned posts flagged.** `isCollab` and `ownerUsername` name a post another account owns that the profile accepted onto its grid; `isPinned` marks pinned posts. A switch leaves out either kind.
- **Each post billed once** per run, and once per memory name across runs. A username or post that does not exist is a free status row; filtered, repeated and already known posts are free.
- **A coverage row per profile** says how many posts were read, delivered, charged and filtered, and why the walk stopped.

### Quick start

1. Open the Actor and paste usernames (`natgeo`, `@nasa`) or profile URLs (`https://www.instagram.com/nike/`) into **Instagram usernames or profile URLs**, up to 500 a run. For single posts, paste post or reel URLs (`/p/`, `/reel/`, `/reels/`, `/tv/`) or the post code alone into **Post or reel URLs**, up to 500 a run.
2. Set **Newest posts per profile**: 12 by default, up to 10,000, or 0 for profile rows only. Add a date range, post types or minimum likes, comments or plays if you like.
3. Run, then download the rows from the Output tab (views **Posts** and **Profiles**) as JSON, CSV or Excel, or read them through the API.
4. For a monitor: give a **Memory name** and schedule the run (see "Monitor profiles for new posts" below).

An empty input (`{}`) runs a sample: @natgeo's 12 newest posts. It is a paid run like any other (USD 0.01538 on the Free plan).

### What you get

One `post` row per Instagram post or reel (`type: "post"`). Fill rates measured on 1,792 distinct grid posts from platform runs of 2026-10-11 over five public profiles (natgeo, nasa, nike, zuck, tartinebakery): 705 photos, 588 reels, 450 carousels and 49 other videos.

| Field | What it is | Filled |
|---|---|---|
| `code`, `id`, `url` | the post code (the part after `/p/` or `/reel/`), the media id (as text) and the post link | 100% |
| `mediaType`, `productType` | `photo`, `video`, `reel` or `carousel`; Instagram's own product type (`feed`, `clips`, `carousel_container`, `igtv`) | 100% |
| `takenAt`, `takenAtUnix` | the time of posting, UTC, as ISO text and as Unix seconds | 100% |
| `caption`, `captionEdited` | the caption as written (empty text when there is none) and whether it was edited | caption 99.7% |
| `hashtags`, `mentions` | the words after # and @ in the caption, in order, without repeats | 48.7%, 51.8% |
| `likeCount`, `likeCountHidden` | likes; `null` (never 0) when the owner hides them, and `likeCountHidden` is then true | 99.9% (2 hidden) |
| `commentCount`, `commentsDisabled` | the comment count; `commentsDisabled` is true when comments are off, else null | 100% |
| `playCount`, `playCountStatus` | reel plays, joined by code from the profile's reels tab; the status is `ok`, or says why not: `not_in_newest_reels`, `lookup_failed`, `hidden_by_owner`, `not_available` (a video that is not a reel), `not_requested` | 99.3% of reels (584 of 588); 481 of 485 on the final build |
| `viewCount` | a video's view count, where the grid gives one | when given |
| `videoUrl`, `videoDuration`, `hasAudio` | the video file link (largest size), its length in seconds and whether it has sound | `videoUrl` 100% of reels; `videoDuration` 482 of 485 reels (the final build's matrix runs of 2026-10-11) |
| `displayUrl`, `width`, `height` | the image (the cover of a video) in its largest size, and the size | 100% |
| `carouselCount`, `carousel` | a carousel's items: `id`, `type`, `displayUrl`, `videoUrl`, `width`, `height`, `taggedUsers`, `accessibilityCaption` | 450 of 450 carousels |
| `location` | `id`, `name`, `lat`, `lng` and the location page `url` | 54.7% |
| `taggedUsers` | the usernames tagged in the media | 56.6% |
| `coauthors` | the coauthors of a collab post | 18.1% |
| `audio` | `type` (`music` or `original`), `title`, `artist`, `audioId` | 71.3% of reels |
| `accessibilityCaption` | Instagram's image description | 2.0% |
| `isPaidPartnership`, `sponsors` | the paid partnership label and the sponsor usernames | 3 of 1,792 marked paid |
| `isPinned` | pinned to the top of the profile's grid | 7 of 1,792 |
| `isCollab`, `ownerUsername`, `ownerId`, `ownerFullName`, `ownerIsVerified` | a post another account owns on this profile's grid, and its owner | `ownerUsername` 100%; 87 of 1,792 collab |
| `profileUsername`, `source`, `subjectKey`, `scrapedAt` | the profile whose grid the post came from, `profile` or `url`, the input it answers, the time of reading | always |

A post read by URL (`source: "url"`) adds `firstComments` (up to 15, each with `id`, `text`, `createdAt`, `username`, `userId`, `isVerified`, `likeCount`, `replyCount`; 3 of 3 posts in the platform run) and `moreCommentsAvailable`. Its `isPinned`, `isCollab` and `profileUsername` are null: they describe a profile's grid.

#### The profile row

One `profile` row per username found: `username`, `userId`, `fullName`, `biography`, `bioLinks` (each `url` and `title`), `externalUrl`, `followerCount`, `followingCount`, `isVerified`, `isPrivate`, `isMemorialized`, `hasReels`, `pronouns`, `highlightTitles`, `threadsUsername` (the linked Threads handle), `profilePicUrl`, `profileUrl`. On a run of 19 large public profiles on 2026-10-11: followers, following and bio links 19 of 19, bio 17 of 19, highlight titles 12 of 19, Threads handle 11 of 19.

#### Coverage, status and summary rows (free)

- A `coverage` row per profile: `stopReason` (`end_of_grid`, `max_posts_per_profile`, `posted_after_reached`, `max_posts`, `charge_limit`, `work_allowance`, `private`, `not_found` and more), `complete`, `reachedEndOfGrid`, `jumpedToEndDate`, `pagesRead`, `postsRead`, `postsDelivered`, `postsCharged`, `repeated`, `known`, `filtered`, `filteredBy` (per reason), `postsSkippedNewer`, `pinnedSkipped`, `reelsCalls`, `playCountsJoined`, `playCountsNotFound`. A post URL gets one too (`delivered`, `charged`, `known`, `repeated`, `commentsDelivered`).
- A `status` row for each value that could not be used: `username_not_found`, `profile_private`, `post_not_found`, `lookup_failed`, `invalid`, `skipped` (inputs a stopped run did not start), `memory_free`, `proxy_mapped`.
- One `summary` row last, with every counter, the reason the run stopped and what the platform confirmed as charged per event.

#### Example post row

A reel of nasa's grid from a run of 2026-10-11 on this Actor's current code (test actor run of the matrix, build 0.1.4), trimmed: media links shortened.

```json
{
  "type": "post",
  "code": "DeTUk4sDSp-",
  "id": "4004634989362489982",
  "url": "https://www.instagram.com/reel/DeTUk4sDSp-/",
  "mediaType": "reel",
  "productType": "clips",
  "takenAt": "2026-10-10T15:15:02.000Z",
  "caption": "POV: you're watching the Earth set\n\nWe just released hundreds of new images and hours of new audio from our Artemis II mission around the Moon this spring. Check out our link in bio for the Artemis II Preliminary Lunar Science Report.\n\n#NASA #ArtemisII #Artemis",
  "hashtags": [
    "NASA",
    "ArtemisII",
    "Artemis"
  ],
  "mentions": [],
  "likeCount": 524004,
  "likeCountHidden": false,
  "commentCount": 4921,
  "playCount": 7411139,
  "playCountStatus": "ok",
  "videoDuration": 43.767,
  "displayUrl": "https://scontent-lga3-3.cdninstagram.com/v/t51.82787-15/84219...",
  "videoUrl": "https://scontent-lga3-2.cdninstagram.com/o1/v/t2/f2/m86/AQNUv...",
  "hasAudio": true,
  "width": 1074,
  "height": 1908,
  "location": null,
  "taggedUsers": [
    "nasaartemis"
  ],
  "coauthors": [],
  "audio": {
    "type": "original",
    "title": "Original audio",
    "artist": "nasa",
    "audioId": "28589819277311219"
  },
  "isPaidPartnership": false,
  "isPinned": false,
  "isCollab": false,
  "ownerUsername": "nasa",
  "ownerFullName": "NASA",
  "ownerIsVerified": true,
  "profileUsername": "nasa",
  "source": "profile",
  "scrapedAt": "2026-10-11T15:51:19.011Z"
}
```

#### How deep it goes

The grid is read up to 33 posts a call, pinned posts first, then newest to oldest, until the profile's first post, your number, your start date or your run cap. Measured on 2026-10-11: zuck's grid to its end, 442 posts back to 2010-10-28 (Instagram counted 444 on 2026-10-08; two or three are not served to a visitor who is not logged in); 1,000 posts of tartinebakery back to 2021-05-20 in 140 seconds; 300 posts of nike in 67 seconds. Pinned posts are often old (natgeo's pinned reels were from July and September), so `isPinned` and **Skip pinned posts** matter for dated work. Collab posts are common on brand grids: 15 of nasa's newest 24 grid posts were owned by other accounts (2026-10-11).

### How much does it cost to scrape Instagram posts?

Pay per event. Prices per 1,000 events (USD):

| Event | Charged for | Free plan | Bronze | Silver | Gold |
|---|---|---|---|---|---|
| Post (`post`) | one post or reel row | 0.49 | 0.45 | 0.39 | 0.29 |
| Profile (`profile`) | once per profile per run, with the profile's first charged post; or per profile row when **Newest posts per profile** is 0 | 2.00 | 1.90 | 1.70 | 1.50 |
| Post details (`post-details`) | a post read by URL (full details and first comments), on top of its Post event | 0.60 | 0.55 | 0.50 | 0.45 |
| Actor start (`apify-actor-start`) | once per run, per GB of memory (runs use 256 MB, so one) | 0.0075 per run | 0.0075 per run | 0.0075 per run | 0.0075 per run |

Platinum and Diamond plans pay the Gold price. There is no discount on the Actor start event. Reel play counts are included in the Post price.

The Store pricing card shows prices per 1,000 events: "$0.29 / 1,000" on the Post row means one post costs USD 0.00029 at Gold. In the same way "$1.50 / 1,000" on the Profile row means one profile costs USD 0.0015, and "$0.45 / 1,000" on the Post details row means USD 0.00045 per post read by URL, both at Gold.

Worked examples:

- A monitor run of one profile that finds 12 new posts: on the Free plan 0.0075 + 12 x 0.00049 + 0.002 = USD 0.01538; at Gold 0.0075 + 0.00348 + 0.0015 = USD 0.01248.
- A monitor run that finds nothing new: USD 0.0075 (the Actor start only).
- 1,000 posts of one profile: USD 0.4995 on the Free plan, 0.0075 + 0.29 + 0.0015 = USD 0.299 at Gold.
- 10 posts by URL on the Free plan: 0.0075 + 10 x (0.00049 + 0.0006) = USD 0.0184.
- 100 profile rows only (**Newest posts per profile** 0) on the Free plan: 0.0075 + 100 x 0.002 = USD 0.2075.

Free: a username or post that does not exist, a private profile in a posts run (its profile row and status row are free; none of its posts can be read), posts left out by a filter, posts repeated in the run, posts the named memory knows, the profile row of a posts run, and every coverage, status and summary row. Your run's charge limit is respected: the run stops when the next event would pass it, and the inputs it did not start get a free `skipped` row.

#### The work allowance

Work that may end without a charge (usernames that do not exist, private profiles, posts all filtered out or all known to the memory, newer posts skipped before an end date) is bounded per run: 45 seconds of unpaid work, plus 2 seconds for each charged post, 6 for each charged profile and 4 for each charged post details event. When it is spent the run stops, says so (`stoppedBy: "work_allowance"`) and lists the inputs it did not start in a free status row. Calls that deliver charged rows never use it up.

### Input

| Field | Default | What it does |
|---|---|---|
| `usernames` | none (the Console fills in `natgeo`) | Usernames (`natgeo`, `@natgeo`) or profile URLs, up to 500 a run. Capitals do not matter. |
| `postUrls` | none | Post or reel URLs (`/p/`, `/reel/`, `/reels/`, `/tv/`) or post codes, up to 500 a run. Filters and dates never apply to these: you named the post. |
| `maxPostsPerProfile` | 12 | How many of each profile's newest posts are read, in grid order (pinned posts first, then newest to oldest), up to 10,000. Every post read counts, whether delivered, filtered or already known to the memory. Posts newer than the end date do not count. 0 = profile rows only. |
| `maxPosts` | no cap | A cap of post rows for the whole run (profile rows are not counted). |
| `postedAfter` | none | Keep posts from this moment on (UTC): `YYYY-MM-DD`, `YYYY-MM-DDTHH:MM`, `today`, `yesterday`, `7 days`, `2 weeks`, `3 months`, `6 hours`, counted back from the run. A walk stops at the first older post (pinned posts aside), so a start date makes a run shorter. |
| `postedBefore` | none | Keep posts up to this moment, in the same forms (`YYYY-MM-DD` means the end of that day). Newer posts are skipped free and do not count toward `maxPostsPerProfile`. After the profile's first page (which holds its pinned posts) the walk jumps straight to the end date, so an old window is reached in one or two calls (`jumpedToEndDate` in the coverage row). |
| `mediaTypes` | all | Keep only `photo`, `video` (not reels), `reel` or `carousel`. Posts of other kinds still count toward `maxPostsPerProfile` and are not charged. |
| `minLikes`, `minComments` | 0 | Keep posts with at least this many likes or comments (a post with hidden likes does not pass `minLikes`). |
| `minViews` | 0 | Keep reels and videos with at least this many plays (`playCount`, or `viewCount` where the grid gives one). Rows without a play count do not pass. |
| `includeCollabPosts` | true | Off: collab posts (owned by another account) are left out and not charged. |
| `skipPinnedPosts` | false | On: pinned posts are left out and do not count toward `maxPostsPerProfile`. |
| `includeReelPlayCounts` | true | Join each reel's play count from the profile's reels tab (about one extra call per 12 reels, no extra charge). |
| `memoryName`, `resetMemory` | none, false | A named memory: later runs with the same name deliver and charge only posts not delivered before. `resetMemory` forgets every post it holds first. |
| `proxyConfiguration` | Apify datacenter proxy | A residential or other Apify proxy group is replaced by the datacenter pool and a free `proxy_mapped` row says so; your own proxy URLs are used as given. |

Through the API, `directUrls` (also `urls` or `startUrls`) takes a mixed list of profile and post URLs, and these key names are read too: `resultsLimit` for `maxPostsPerProfile`, `maxItems` for `maxPosts`, `onlyPostsNewerThan` for `postedAfter`, `onlyPostsOlderThan` for `postedBefore`. Two keys of one meaning sent together refuse the input with a free `invalid` row.

### Monitor profiles for new posts

Give a **Memory name** and schedule the run (daily, hourly). Each run reads the newest posts of every profile again, skips the ones the memory already holds for free, and delivers and charges only the posts it has not delivered before under that name. A scheduled run of the 12 newest posts therefore bills only what is new, and a run that finds nothing new costs the Actor start alone. The profile row comes with every run, so `followerCount` is fresh each time (free in a posts run).

- Known posts count toward **Newest posts per profile**: set it above the number of posts a profile can publish between two runs.
- The memory is a named storage this Actor creates in your account; it belongs to this Actor. **Reset the memory** forgets it.
- Two runs on one memory name at the same time: the second runs free and remembers nothing. A memory that cannot be opened, read or saved makes the run free too, and a `memory_free` row says so.

### What this Actor does NOT do

- It does not log in, use cookies or use Instagram accounts. It reads only what Instagram shows a visitor who is not logged in.
- It does not read comments beyond the first 15 of a post read by URL, and gives no comments on grid posts (the grid answer carries none): read the post by its URL for them.
- It does not read follower or following lists.
- It does not read stories or the contents of highlights (the profile row lists the highlight titles).
- It does not search by hashtag, keyword or place, and does not read the tagged tab of a profile.
- It does not give shares or saves counts.
- It does not transcribe videos or download video files: the row carries the video URL.
- It does not give the exact post count of a profile.
- It does not read the posts of a private profile: the profile row only.
- It cannot read posts Instagram does not show to logged out visitors (age or region restricted): those get a free `post_not_found` row that says so.
- It gives play counts for reels only (from the reels tab), not for other videos.
- It does not tell trial reels (reels shown first to people who do not follow the account) from other reels: the answers it reads carry no such mark.

### API examples

```bash
curl -X POST "https://api.apify.com/v2/acts/automation_craft~instagram-posts-scraper/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"usernames": ["natgeo", "nasa"], "maxPostsPerProfile": 50, "postedAfter": "30 days"}'
```

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('automation_craft/instagram-posts-scraper').call({
    usernames: ['nike'],
    maxPostsPerProfile: 100,
    mediaTypes: ['reel'],
    minViews: 100000,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const p of items.filter((x) => x.type === 'post')) console.log(p.url, p.playCount, p.likeCount);
```

```python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("automation_craft/instagram-posts-scraper").call(run_input={
    "usernames": ["tartinebakery"],
    "maxPostsPerProfile": 12,
    "memoryName": "bakery watch",
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["type"] == "post":
        print(item["takenAt"], item["url"], item["likeCount"], item["commentCount"])
```

### FAQ

#### Can I scrape more than 12 posts from a public Instagram account?

Yes. Set **Newest posts per profile** (`maxPostsPerProfile`) up to 10,000: the grid is read up to 33 posts a call, newest first, until the profile's first post. On 2026-10-11 a run read 442 posts of one profile back to 2010, and another read 1,000 posts of one profile in 140 seconds.

#### How do I track a competitor's Instagram posts?

Put the competitor's username in `usernames`, give a `memoryName` and schedule the run. Each run delivers and charges only the posts not delivered before under that name, and a run with nothing new costs USD 0.0075.

#### Can I run an Instagram profile scraper on a schedule without it breaking?

Schedule the Actor with a memory name: every run returns the profile row (followers, following, bio, links, verified) and only the new posts. It needs no login, cookies or Instagram account, and each profile's `coverage` row says why its walk stopped. It gives no exact post count of a profile.

#### Can I filter Instagram reels by minimum views?

Yes: set `minViews` (with `mediaTypes: ["reel"]` for reels only). Play counts come from the profile's reels tab at no extra charge, a row without a play count does not pass, and posts a filter leaves out are free.

#### Can I scrape Instagram stories or follower names without a login?

No. This Actor reads only what Instagram shows a visitor who is not logged in, and that includes neither stories nor follower or following lists. It gives each profile's follower and following counts.

#### Why does this Actor run with limited permissions?

Least privilege: it only needs its own storages and the named memory store it creates itself, so it asks for nothing else in your account.

### More data tools by Automation Craft

- [Threads Scraper: Posts, Search, Tags, Replies](https://apify.com/automation_craft/threads-scraper)
- [Threads Profile Scraper: Followers, Bio, Posts](https://apify.com/automation_craft/threads-profile-scraper)
- [Instagram Ads Library Scraper - Creatives, Video](https://apify.com/automation_craft/instagram-ads-library-scraper)
- [Meta Ads Library Scraper - Ad Library, All Filters](https://apify.com/automation_craft/meta-ads-library-scraper)
- [TikTok Profile Scraper: Exact Stats, No Login](https://apify.com/automation_craft/tiktok-profile-scraper)
- [TikTok Video Scraper: Post Stats, Transcripts](https://apify.com/automation_craft/tiktok-video-scraper)
- [Telegram Channel Scraper: Posts, Views, Dates](https://apify.com/automation_craft/telegram-channel-scraper)

### Changelog

- 0.1 (2026-10-11): first release. Profile rows and the newest posts and reels of public profiles, back to the first post on request, with reel play counts; posts by URL with their first comments; date range, post type, likes, comments and plays filters; collab and pinned flags and switches; a cross run memory; free rows for misses. No login.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation_craft/instagram-posts-scraper/changelog.md

# Actor input Schema

## `usernames` (type: `array`):

Public Instagram profiles to read, one per line: a username (natgeo or @natgeo) or a profile URL (instagram.com/natgeo). Capitals do not matter. Up to 500 per run. Each profile gives one profile row (followers, following, bio, links, verified) and its newest posts and reels. A username with no public profile gets a free status row.

## `postUrls` (type: `array`):

Single posts to read, one per line: a post or reel URL (instagram.com/p/CODE, /reel/CODE, /reels/CODE or /tv/CODE) or the post code alone. Up to 500 per run. Each post found is one row with its full details and its first comments (up to 15), charged as a Post and a Post details event. A post that does not exist or is not shown without a login gets a free status row. Filters and dates do not apply to these: you named the post.

## `maxPostsPerProfile` (type: `integer`):

How many of each profile's newest posts are read, in the order of the profile's grid (pinned posts first, then newest to oldest), back to the first post if you ask for enough. Every post read counts, whether it is delivered, left out by a filter, or already known to your memory, so a filtered run may deliver fewer. Posts newer than the end date do not count. Default 12, at most 10,000. 0 means profile rows only (each charged as a Profile).

## `maxPosts` (type: `integer`):

The cap of post rows for the whole run, across every profile and post URL (profile rows are not counted). No cap when left out: the per profile number and your charge limit bound the run.

## `postedAfter` (type: `string`):

Keep posts from this moment on (UTC): YYYY-MM-DD, YYYY-MM-DDTHH:MM, today, yesterday, "7 days", "2 weeks", "3 months" or "6 hours" (counted back from the run's start). The walk of a profile stops at the first older post (pinned posts aside), so a date also makes a run shorter.

## `postedBefore` (type: `string`):

Keep posts up to this moment (UTC): YYYY-MM-DD (the end of that day), YYYY-MM-DDTHH:MM, today, yesterday, "N days" or "N hours". Newer posts are skipped free and do not count toward the newest posts per profile. After the profile's first page (which holds its pinned posts) the walk jumps straight to the end date, so an old window is reached in one or two calls.

## `mediaTypes` (type: `array`):

Keep only these kinds of posts. All kinds when left empty. Posts of other kinds still count toward the newest posts per profile and are not charged.

## `minLikes` (type: `integer`):

Keep only posts with at least this many likes (a post whose owner hides its likes does not pass). Default 0.

## `minComments` (type: `integer`):

Keep only posts with at least this many comments. Default 0.

## `minViews` (type: `integer`):

Keep only reels and videos with at least this many plays. Posts without a play count (photos, carousels, and reels whose count was not found) do not pass. Default 0.

## `includeCollabPosts` (type: `boolean`):

Collab posts are posts another account owns that the profile accepted onto its grid (on some brand profiles half the grid). On by default: they are delivered with isCollab true and ownerUsername naming the owner. Off: they are left out and not charged.

## `skipPinnedPosts` (type: `boolean`):

Leave out the posts the profile pinned to the top of its grid (often old posts). Skipped pinned posts do not count toward the newest posts per profile. Off by default; pinned posts carry isPinned true.

## `includeReelPlayCounts` (type: `boolean`):

Add each reel's play count, read from the profile's reels tab (about one small extra call per 12 reels, no extra charge). On by default; playCountStatus says ok, or why a reel has none (an old pinned reel can be beyond the newest reels read).

## `memoryName` (type: `string`):

Give a name and later runs with the same name deliver and charge only posts not delivered before under this name: a scheduled run of the 12 newest posts then bills only what is new. Known posts are read and skipped free. A memory belongs to this Actor; two runs on one name at the same time: the second runs free. Letters, digits, spaces and . \_ , : # ( ) - only.

## `resetMemory` (type: `boolean`):

Forget every post the named memory holds before this run: every post counts as new again (and is charged again).

## `proxyConfiguration` (type: `object`):

Apify datacenter proxy, one exit address at a time; when Instagram asks an exit to wait, the run moves to a fresh one, and after 12 it reads on from its own address. A residential or other Apify proxy group is not used (the datacenter pool answers, and the run says so); your own proxy URLs are used as given.

## Actor input object example

```json
{
  "usernames": [
    "natgeo"
  ],
  "maxPostsPerProfile": 12,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "usernames": [
        "natgeo"
    ],
    "maxPostsPerProfile": 12,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation_craft/instagram-posts-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "usernames": ["natgeo"],
    "maxPostsPerProfile": 12,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("automation_craft/instagram-posts-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "usernames": [
    "natgeo"
  ],
  "maxPostsPerProfile": 12,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call automation_craft/instagram-posts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation_craft/instagram-posts-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/rgpNq2t5uZHVuOfsN/builds/3E8vEmtydtroeqvt0/openapi.json
