# Instagram Profile Scraper – Posts & Followers (`simple.actor/instagram-profile-posts`) Actor

Scrape any public Instagram profile for its latest posts — caption, likes, comments, images, video URLs and carousels — plus the account record with follower count and bio. No login, no cookies, no API key. Built for polling many accounts often: 12 posts per profile. Unofficial.

- **URL**: https://apify.com/simple.actor/instagram-profile-posts.md
- **Developed by:** [Simple Actor](https://apify.com/simple.actor) (community)
- **Categories:** Social media, Lead generation, Marketing
- **Stats:** 1 total users, 1 monthly users, 99.8% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$8.00 / 1,000 profile reads

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Instagram Profile Scraper – Posts & Followers

Scrape any public Instagram profile for what it has posted lately — caption,
likes, comments, images, video files, carousels — together with the full account
record: followers, following, bio, bio links, category, business contact details
and verification. **12 latest posts per profile** — or **24** with the video tab
switched on — newest first, 57 fields each. Built for social media monitoring,
influencer research and competitor tracking across many accounts at once.

**$8 per 1,000 profiles read** — Apify platform usage, residential proxy and
the clean-IP route this Actor carries on its own side all included, nothing else
to pay. The charge is per profile and does not depend on how many posts come
back, so a full read and a check that finds one new post cost the same. A profile
that cannot be read is never charged, and does not fail the run: it comes back as
a row saying why.
**No login, no cookies, no API key, no account of yours involved.** There is no
session to keep alive and nothing of yours to get restricted.

*Unofficial. Not affiliated with, endorsed by, or sponsored by Instagram or Meta.*

> **Read this before you start: 12 posts per profile** from the main grid, and
> 12 more if you turn on the video tab. That is Instagram's limit for a
> logged-out reader, not a setting. This is built to check *many* accounts
> *often* — if you need one account's back catalogue, this is the wrong tool.
>
> **Pick your row shape up front.** The default is one row per *post*, with the
> account record repeated on each — right for collecting posts, wasteful for
> watching accounts. Set [`outputFormat: "profile"`](#two-shapes-one-row-per-post-or-one-row-per-account)
> and each account is a single row instead, at the same price.

### Features

- **Latest posts per public profile** — up to 12 from the grid, 24 with the
  video tab, newest first, 57 fields each.
- **Full account record** — followers, following, total posts, bio, bio links,
  category, business email and phone, verification, profile pictures.
- **Every media type handled** — single images, videos with a direct file URL
  and play count, and carousels with **every slide** individually.
- **Two output shapes at the same price** — one row per post, or one row per
  account with its posts nested.
- **Caption parsed** — hashtags and mentions extracted, plus tagged accounts,
  collaborators, location, music credits on reels and alt text.
- **Collaborations attributed correctly** — `ownerUsername` comes from the post,
  so a collab posted to two grids is credited to its real owner, with
  `coauthors` listing the rest.
- **A second timeline for free** — the long-form/IGTV tab arrives in the same
  request and shares no posts with the grid.
- **Related accounts** — up to fifty accounts Instagram suggests alongside this
  one, for discovery.
- **Incremental polling** — `onlyPostsNewerThan` takes `20 hours`, `3 days` or
  an ISO date, and refuses rather than misleads if the window reaches too far
  back.
- **Three doors to every account.** Instagram's profile endpoint first; the
  accounts it refuses with its own internal error are read from its post feed;
  and when Instagram turns a logged-out read away altogether — which it now does
  from most addresses — the profile page itself is read, over a clean-IP route
  the Actor carries on its own side. Each row says which door it came through
  in `isProfilePartial` and `isPostPartial`, so a `null` is never mistaken for
  a zero.
- **Runs stay green.** A missing, private or mistyped account is a row, not a
  failed run.

### Input

```json
{
  "startUrls": [{ "url": "https://www.instagram.com/nasa/" }],  // profiles as links
  "usernames": ["natgeo", "@who"],   // or as usernames — both lists are read as one
  "outputFormat": "posts",           // "posts" = row per post, "profile" = row per account
  "maxPosts": 12,                    // latest posts per account (max 12, or 24 with the video tab)
  "includeVideoTab": false,          // add the long-form/IGTV timeline — doubles output, same cost
  "includeRelatedProfiles": false,   // attach the accounts Instagram suggests alongside
  "onlyPostsNewerThan": "3 days",    // optional window: "20 hours", "3 days", "2026-08-01"
  "includeRaw": false,               // attach Instagram's untouched post object under `raw`
  "proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

The shortest useful input is a single profile:

```json
{ "startUrls": [{ "url": "https://www.instagram.com/nasa/" }] }
```

If you have usernames rather than links, put them in `usernames` instead — both
lists are read as one:

```json
{
  "startUrls": [{ "url": "https://www.instagram.com/nasa/" }],
  "usernames": ["natgeo", "@who"]
}
```

| Field | Type | Default | What it does |
|---|---|---|---|
| `startUrls` | array | — | Profiles to read, as links. |
| `usernames` | array | — | Profiles to read, as usernames. Combined with `startUrls`. |
| `outputFormat` | string | `posts` | `posts` gives one row per post; `profile` gives one row per account with its posts under `posts`. |
| `maxPosts` | integer | all | How many latest posts per account. Ceiling is 12, or 24 with the video tab. |
| `includeVideoTab` | boolean | `false` | Also read the long-form/IGTV timeline — roughly doubles the output, same request. |
| `includeRelatedProfiles` | boolean | `false` | Attach the accounts Instagram suggests alongside this one. |
| `onlyPostsNewerThan` | string | — | Keep only posts after a point: `"20 hours"`, `"3 days"`, or `2026-08-01`. |
| `includeRaw` | boolean | `false` | Attach Instagram's untouched post object under `raw`. |
| `proxy` | object | Residential | Leave this alone unless you know why you are changing it. |

### Output

One dataset item per post — or one per account, if you would rather have the
profile stated once with its posts attached ([both shapes](#two-shapes-one-row-per-post-or-one-row-per-account)):

- **The post** — caption, permalink, publish time, type (image, video, carousel)
- **Engagement** — likes, comments, and play counts on video
- **Media** — full-size image URL and every smaller rendition; for video, a
  direct file URL; for a carousel, **every slide** with its own image or video
- **Context** — hashtags and mentions read from the caption, tagged accounts,
  collaborators, location, music credits on reels, alt text
- **The account** — bio, bio links, followers, following, total posts, category,
  business contact details, verification, profile pictures

```json
{
  "type": "Sidecar",
  "shortCode": "DcJ2mQxFabc",
  "url": "https://www.instagram.com/p/DcJ2mQxFabc/",
  "caption": "Hold the dots and scroll to switch between…",
  "likesCount": 216896,
  "commentsCount": 716,
  "timestamp": "2026-08-14T21:48:00.000Z",
  "childPostCount": 20,
  "hashtags": ["NASA", "space", "astronomy"],
  "ownerUsername": "nasa",
  "owner": { "followersCount": 104409230, "isVerified": true }
}
```

#### Two shapes: one row per post, or one row per account

The item above is the default: **one row per post**, with the account record on
each of them. It is the right shape for collecting posts — pipe it into a table
and every row stands alone.

If you are looking at *accounts* rather than collecting posts, set
`outputFormat` to `profile` and each account becomes **a single row**: the
profile at the top level, and its latest posts in short form under `posts`.

```json
{
  "username": "westvillenyc",
  "fullName": "Westville",
  "url": "https://www.instagram.com/westvillenyc/",
  "biography": "Neighborhood restaurants…",
  "profilePicUrlHd": "https://…",
  "followersCount": 48213,
  "postsCount": 3241,
  "categoryName": "Restaurant",
  "isVerified": false,
  "postsReturned": 5,
  "posts": [
    {
      "id": "3972256348138218143",
      "type": "Sidecar",
      "shortCode": "DcgShpLkaKf",
      "url": "https://www.instagram.com/p/DcgShpLkaKf/",
      "caption": "Summer specials are officially here…",
      "hashtags": [],
      "mentions": ["goldencalibear"],
      "timestamp": "2026-08-25T14:19:51.000Z",
      "timestampUnix": 1787667591,
      "likesCount": 1247,
      "commentsCount": 31,
      "isVideo": true,
      "videoUrl": "https://…",
      "videoViewCount": 4925,
      "displayUrl": "https://…",
      "alt": null,
      "locationName": "Westville",
      "isPinned": false,
      "childPostCount": 2,
      "ownerUsername": "westvillenyc",
      "source": "grid",
      "isPostPartial": false
    }
  ]
}
```

That is the **complete** post shape in this format — all 22 fields, not an
excerpt. In particular **`displayUrl` is here**: the post's full-size image is on
every post in this format, so you do not need the row-per-post format to get the
pictures. What the short form drops is the long tail: every smaller rendition of
each image (`images`), the blur-up placeholder, the per-slide `childPosts`,
tagged-account coordinates, music credits, and the streaming-manifest fields. If
you need any of those, use the default format.

Same charge either way — it is one profile read, so this costs no less and no
more. What it saves is size: measured on a real five-post read, **13.9 KB against
101 KB**, because the profile is stated once instead of once per post.

#### Two timelines for one request

Instagram serves a **second timeline** alongside the grid — the account's
long-form and IGTV videos — in the same request, sharing no posts with the grid.
Set `includeVideoTab` to double what a run returns **at no extra cost**. Every
item says which one it came from in `source` (`grid` or `videos`).

It is off by default because that tab reaches years further back, and folding it
in silently would change what "latest" means.

#### Related accounts

Instagram also ships up to fifty suggested accounts with every profile. Set
`includeRelatedProfiles` to get them under `owner.relatedProfiles`, each with a
username, name, ID, verification and link — a ready-made way to widen a watchlist.

### How to use

#### From Apify Console

1. Open the Actor and click **Try for free** / **Start**.
2. Put profile links into **Instagram profiles**, or a column of bare usernames
   into **Usernames** — both lists are read together.
3. Decide the row shape in **What a row is**: one row per post for collecting
   posts, one row per account for watching accounts. Same price either way.
4. Turn on **Also read the video tab** to roughly double what comes back at no
   extra cost.
5. Leave the proxy on **Residential** — Instagram refuses a repeated address.
6. Click **Start**, then open the **Dataset** tab and export as JSON, CSV or
   Excel.

To monitor accounts, schedule the run and set **Only posts newer than** to a
window shorter than the gap between runs.

#### From the API

```bash
curl -s "https://api.apify.com/v2/acts/simple.actor~instagram-profile-posts/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"usernames":["nasa"]}'
```

Watching a list of accounts, with the JavaScript client:

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('simple.actor/instagram-profile-posts').call({
    usernames: ['nasa', 'natgeo', 'who'],
    outputFormat: 'profile',       // one row per account
    includeVideoTab: true,         // 24 posts instead of 12, same charge
    onlyPostsNewerThan: '24 hours',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
// Treat any row with an `error` field as the failure signal — the run stays green.
```

### Use cases

- **Monitor competitor and brand accounts** — poll a list of profiles on a
  schedule with `onlyPostsNewerThan` and see new posts, captions and engagement
  as they appear.
- **Influencer research and vetting** — pull follower counts, verification,
  category, business contact details and recent engagement across a shortlist in
  one run.
- **Engagement benchmarking** — compare likes and comments per post across
  accounts in the same niche, loaded straight into a warehouse.
- **Hashtag and campaign tracking** — hashtags and mentions come parsed out of
  every caption, so you can measure which tags an account actually uses.
- **Media archiving** — collect full-size images, video files and every carousel
  slide for a set of accounts (download promptly — the URLs expire).
- **Account discovery** — switch on related accounts to expand a seed list into
  the neighbours Instagram itself suggests.

### Usage notes

Instagram allows one address about **nineteen requests** before refusing it, and
the refusal lasts far longer than the message claims — measured still in force
**33 minutes** after "please wait a few minutes". So every attempt goes out from
a different IP, which is what residential proxy provides.

The default is already set correctly. If you switch to datacenter, the pool is
too small to rotate through and runs will come back `rate_limited`.

#### A post on a profile is not always *by* that profile

Instagram collaborations appear on both accounts' grids but are owned by one of
them. `ownerUsername` is read from the post, so a collaboration is attributed to
its real owner rather than to the profile you asked for — and `coauthors` lists
the credited accounts.

#### What an empty result means

An empty dataset means the account was read and has posted nothing in the window
you asked for. It never means "we could not look".

An account that cannot be read is reported as its **own row** with `error` and
`errorDescription`, so one bad account never costs you the rest:

| `error` | Meaning | Run status |
|---|---|---|
| `not_found` | No such account | Succeeds |
| `private` | Posts are visible only to approved followers, so a login would be needed | Succeeds |
| `bad_input` | The entry was never a usable username or profile URL | Succeeds |
| `window_too_wide` | `onlyPostsNewerThan` reaches further back than this run can see | Succeeds |
| `instagram_error` | Instagram returned an internal error for this account on both routes — see below | Fails only if *every* account did |
| `rate_limited` | Instagram refused a logged-out read from every address tried, through every door including the clean-IP route — rare; a re-run almost always passes | Fails only if *every* account did |
| `read_failed` | Instagram did not serve a readable response after several attempts | Fails only if *every* account did |

**A missing, private or mistyped account does not fail the run.** Those are
answers about the account you asked for, not a fault, and a run reporting them
finishes as SUCCEEDED with the reason in a row. The run's status message says how
many accounts were read and how many answered with an error.

**So check the rows, not the run status.** If you are scheduling this, treat any
row with an `error` field as the failure signal — a green run can still contain
accounts that were not read. The one case that does fail a run is *every* account
in it coming back unreachable, which means Instagram or the proxy could not be
reached at all rather than anything about your accounts.

An empty dataset still means exactly one thing — read, nothing new — because
every failure leaves a row.

#### Accounts Instagram's own endpoint refuses

Some business accounts make Instagram's profile endpoint return an internal
error about a deleted asset — reproducible per account, a fault on Instagram's
side rather than the account's. **Those accounts are read anyway**, through a
second Instagram endpoint that does serve them.

The posts come back complete. The account record does not: that second endpoint
carries the username, display name, picture and verification, and nothing else.
The rest of the profile fields read `null` rather than being guessed at, and the
row says so in **`isProfilePartial: true`** — so a `null` follower count is
"could not be read", never zero. Check that flag before charting anything from
the profile fields.

`instagram_error` is now reported only when *both* routes fail for an account.

#### When Instagram turns a logged-out read away

Instagram increasingly answers its own endpoints with a login requirement for
readers who are not signed in — from most addresses, for most accounts,
including public ones. When both endpoints refuse an account, **it is still
read**: from the profile page itself, first over your proxy and then over a
clean-IP route the Actor carries on its own side, at no extra charge.

What comes back through that door is honest about itself. The profile carries
followers, following, post count, bio, links, verification and privacy —
**`isProfilePartial: true`**, because it is not the endpoint's full record. The
latest 12 posts carry their id, link, type, caption, alt text, one image, a
carousel's slide count and an exact timestamp, and say **`isPostPartial: true`**,
because **`likesCount`, `commentsCount`, the extra image renditions, `videoUrl`,
tagged users, location and music read `null`** — Instagram does not put those on
the page for a logged-out reader. A `null` there is "not on the page", never
zero. If your use depends on engagement counts, filter on `isPostPartial` and
treat those posts as counts-unknown.

#### What it costs

**$3 per 1,000 profiles read.** One profile is one charge, so asking for five
profiles in one run costs five. There is no per-post fee and no per-run fee.

Two consequences worth knowing:

- **Turn on the video tab.** It doubles what you get — 24 posts instead of 12 —
  for exactly the same charge, because it arrives in the same request.
- **Take all the posts.** Fewer posts cost you no less; the price is for reading
  the profile.

Because the charge is per profile, what you pay per *post* depends on how many
come back:

| Posts returned | Works out at |
|---|---|
| 24 — grid plus video tab | $0.13 per 1,000 posts |
| 12 — grid only | $0.25 per 1,000 posts |
| 1 — a poll that found one new post | $3.00 per 1,000 posts |

If you are polling with `onlyPostsNewerThan` and most checks come back empty,
budget by **profiles checked**, not posts collected — that is what you are billed
on. A profile that cannot be read is never charged, so a list containing dead or
private accounts costs only for the live ones.

#### `onlyPostsNewerThan` will refuse rather than mislead

A run sees an account's latest posts and no further back. If your window reaches
past them, posts inside it were never fetched — and returning what was found
would read as "this is everything since then". So the account is reported with
`window_too_wide` instead, telling you how far back it could actually see. A
pinned post does not count towards that reach, since it can be years old.

#### Limits

**12 posts per profile from the grid, 24 with the video tab** — Instagram's
numbers, not settings.

**Media URLs expire.** Image and video URLs are signed by Instagram and die
within hours. Download them promptly; the post's `url` is the durable one.

**No comment text.** This endpoint carries comment *counts* but no comments.
`firstComment` and `latestComments` are present and empty so existing code does
not break on their absence.

**Private accounts cannot be read.** They need an approved follower's login,
which this deliberately does not have.

### FAQ

**Is scraping Instagram legal?**
This Actor reads only posts that are publicly visible to anyone without logging
in — it does not log in, use cookies, or reach private accounts, stories or
direct messages. Meta's Terms of Service restrict automated collection from its
properties, so check the platform's ToS and your own obligations, particularly
around personal data, before using it.

**Do I need an Instagram login, cookies or an API key?**
No. There is no session to keep alive and nothing of yours to get restricted.

**Can I scrape a profile's entire post history?**
No. Instagram serves a logged-out reader 12 posts from the grid, plus 12 more
from the video tab — that is Instagram's limit, not a setting. This Actor is
built to check *many* accounts *often*; for one account's back catalogue it is
the wrong tool.

**Does it support pagination?**
There is no next page to request. `includeVideoTab` is the only way to get more
posts per profile, and it roughly doubles the output in the same request at no
extra charge.

**Can it read private accounts?**
No. Those need an approved follower's login, which this deliberately does not
have. A private account comes back as a row with `error: "private"` and the run
still succeeds.

**Why do I need a residential proxy?**
Instagram allows one address about nineteen requests before refusing it, and the
refusal was measured still in force 33 minutes later. Residential proxy sends
every attempt from a different IP. Switching to datacenter returns
`rate_limited` error rows.

**Why is the follower count null on some accounts?**
Some business accounts make Instagram's own profile endpoint return an internal
error, so they are read through a second endpoint that carries the posts but
only part of the profile. Those rows set `isProfilePartial: true` — a null there
means "could not be read", never zero.

**Does it return comment text?**
No. This endpoint carries comment *counts* but no comments. `firstComment` and
`latestComments` are present and empty so existing code does not break.

**Why did my run succeed when an account was not read?**
By design: a missing, private or mistyped account is an answer about that
account, not a fault, so it comes back as a row with an `error` field. Treat
those rows as the failure signal rather than the run status. Only *every*
account failing fails the run.

### Note

This reads publicly visible posts only — the same ones anyone can see without
logging in. It does not log in, does not use cookies, and cannot reach private
accounts, stories, direct messages, or anything else behind a login.

# Actor input Schema

## `startUrls` (type: `array`):

Public Instagram accounts to read, as links: https://www.instagram.com/nasa/. For bare usernames use "Usernames" below, which the platform accepts as plain text. Private accounts cannot be read without a login and are reported as such.

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

Accounts given as usernames rather than links — "nasa", "@nasa" or "NASA" all work. Use this when you have a column of usernames. Both lists are read as one.

## `outputFormat` (type: `string`):

**One row per post** (default) gives every post its own row, with the full profile repeated on each — the shape to pipe into a warehouse, and what existing code reading this Actor expects. **One row per account** gives each account a single row: the profile at the top level, and its latest posts under `posts` with all 21 of these fields — id, type, shortCode, url, caption, hashtags, mentions, timestamp, timestampUnix, likesCount, commentsCount, isVideo, videoUrl, videoViewCount, **displayUrl** (the full-size image, so you do not need the other format for pictures), alt, locationName, isPinned, childPostCount, ownerUsername and source. It drops only the long tail: the smaller image renditions, per-slide carousel detail, tag coordinates, music credits and streaming-manifest fields. Same price either way; the row is a fraction of the size, and it is the one to pick when you are looking at accounts rather than collecting posts.

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

How many of the latest posts to return per account, newest first. Instagram returns 12 from the grid, and 12 more from the video tab if you turn that on — so the ceiling is 12, or 24 with the video tab. Leave it unset to take everything available.

## `includeVideoTab` (type: `boolean`):

Instagram serves a second timeline alongside the grid — the account's long-form and IGTV videos — in the same request, and it shares no posts with the grid. Turn this on to include it and roughly double what a run returns at no extra cost. Off by default because that tab reaches years further back, which would change what "latest" means; every item says which timeline it came from in `source`.

## `includeRelatedProfiles` (type: `boolean`):

Attach the accounts Instagram suggests alongside this one (up to fifty, already included in the same request) under `owner.relatedProfiles`. Off by default because the owner record rides on every post, so this multiplies the size of a run's output.

## `proxy` (type: `object`):

Defaults to Apify residential proxy, which this Actor needs. Instagram allows an address about nineteen requests before refusing it for far longer than it says, so every attempt goes out from a different IP. A datacenter pool is too small for that and a run on one comes back with rate\_limited error rows.

## `includeRaw` (type: `boolean`):

Attach Instagram's untouched post object under `raw`. Turn this on when you need a detail that has no named field; it makes each item much larger, so leave it off otherwise.

## `onlyPostsNewerThan` (type: `string`):

Keep only posts published after this point. Takes a window like "20 hours", "last 3 days" or an ISO date such as 2026-08-01. Because a run reads only an account's latest posts, the run reports the account rather than returning a partial answer if the window reaches further back than those posts do — so an empty result always means "nothing new", never "we did not look".

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.instagram.com/nasa/"
    }
  ],
  "usernames": [
    "nasa"
  ],
  "outputFormat": "posts",
  "maxPosts": 12,
  "includeVideoTab": false,
  "includeRelatedProfiles": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "includeRaw": false,
  "onlyPostsNewerThan": "3 days"
}
```

# Actor output Schema

## `posts` (type: `string`):

One row per post with its headline numbers.

## `accounts` (type: `string`):

One row per account — for runs using the row-per-account output format.

## `media` (type: `string`):

Image and video URLs for each post.

## `engagement` (type: `string`):

Likes, comments and video views per post.

## `full` (type: `string`):

Caption, owner, tags and flags.

## `raw` (type: `string`):

Every field of every item, exactly as pushed.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.instagram.com/nasa/"
        }
    ],
    "usernames": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("simple.actor/instagram-profile-posts").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 = {
    "startUrls": [{ "url": "https://www.instagram.com/nasa/" }],
    "usernames": [],
}

# Run the Actor and wait for it to finish
run = client.actor("simple.actor/instagram-profile-posts").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 '{
  "startUrls": [
    {
      "url": "https://www.instagram.com/nasa/"
    }
  ],
  "usernames": []
}' |
apify call simple.actor/instagram-profile-posts --silent --output-dataset

```

## MCP server setup

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

```

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/zE7qb1zffMnyoPldd/builds/arEJMZ3r9yV3Ilc5f/openapi.json
