# Instagram Post Scraper (`dami_studio/instagram-post-scraper`) Actor

Instagram post scraper API for posts and reels from links or shortcodes. One row per post: caption, likes, comments, publish time, the media (every carousel image and video URL), owner, hashtags, mentions, tagged users, location and music. $0.18 per 1,000 posts, less on paid plans.

- **URL**: https://apify.com/dami_studio/instagram-post-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Social media
- **Features:** Limited permissions
- **Stats:** 89 total users, 41 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.17 / 1,000 instagram 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 post scraper for post, reel or IGTV links, one row per post**: the full caption, like and
comment counts, the exact publish time, every image and video URL including all the frames of a
carousel, the owner, the hashtags, the mentions and the tagged users. Bare shortcodes, numeric post
ids and links from the app's Share button work too, so a messy list is fine.

Two things are missing on purpose, and it is better to know now: there is no comment text, and
`videoViewCount` is usually `null`, because Instagram does not publish the play count to a
logged-out reader. Likes and comments are there on both posts and reels.

| | |
|---|---|
| **Input** | Post, reel or IGTV links, share links, bare shortcodes or numeric post ids |
| **Output** | One row per post: caption, likes, comments, publish time, every image and video URL, owner, hashtags, mentions, tagged users, location and its coordinates, music, the sound's id |
| **Ceiling** | 5,000 links per run |
| **Account needed** | None |
| **Price** | $0.18 per 1,000 posts on the free plan, down to $0.174 on Business. The free plan's $5 a month covers about 27,000. Optional plays, shares and Facebook counts, paid plans only: $1.38 per 1,000 posts checked on Starter, down to $1.34 on Business; from 16 November 2026, $4.94 on Starter, down to $4.83 on Business |

### 🔍 What Instagram Post Scraper does

You give it links, it gives you rows. `instagram.com/p/<code>/`, `/reel/<code>/`, `/tv/<code>/`, a
link copied from a profile grid or the Share button, the numeric post id, or just `DbtErSrlB2J` on its
own. Mix them freely in one run.

Carousels come back whole: `images` and `videos` hold every frame in order, and `childPosts` has one
object per frame with its own shortcode and size. A reel fills in `videoUrl` and `videoDuration`.

Duplicate links are removed before anything is charged, including the same post supplied once as a
URL and once as a bare code. A link that is dead, deleted or withheld comes back as a free labelled
row rather than a charge. A run the platform restarts never charges a post twice.

### 📋 What data you get from each Instagram post

| What you get | Field |
|---|---|
| Link, shortcode and numeric id | `url`, `shortCode`, `id` |
| What kind of post it is | `type`, `productType`, `isReel` |
| Caption, and the hashtags and handles in it | `caption`, `hashtags`, `mentions` |
| Likes and comments | `likesCount`, `commentsCount` |
| Video views and length | `videoViewCount`, `videoDuration` |
| Publish time | `timestamp` |
| Cover, video file and every carousel frame | `displayUrl`, `videoUrl`, `images`, `videos`, `childPosts`, `childPostsCount` |
| Size and alt text | `dimensionsWidth`, `dimensionsHeight`, `alt` |
| The account that posted it | `ownerUsername`, `ownerFullName`, `ownerId`, `ownerIsVerified`, `ownerIsPrivate`, `ownerProfilePicUrl`, `ownerProfileUrl` |
| Tagged people, co-authors and sponsors | `taggedUsers`, `coauthorProfiles`, `coauthorProducers`, `sponsorProfiles` |
| Paid partnership, hidden likes, comments off | `isSponsored`, `isPaidPartnership`, `likesAndViewCountsDisabled`, `isCommentsDisabled` |
| Location and music | `locationName`, `locationId`, `locationLatitude`, `locationLongitude`, `musicArtist`, `musicTitle` |
| The sound: its id, own or licensed, explicit | `audioId`, `isOriginalAudio`, `isExplicitAudio` |
| The share link a row came from | `shareUrl` |
| Plays, shares, saves, reposts and the sound's link, as a paid extra | `instagramPlayCount`, `totalPlayCount`, `sharesCount`, `savesCount`, `repostsCount`, `audioUrl` |
| Facebook plays, likes and comments, in the same paid extra | `facebookPlayCount`, `facebookLikesCount`, `facebookCommentsCount`, `totalLikesCount`, `totalCommentsCount`, `facebookCountsFound` |

### ▶️ How to scrape Instagram posts

1. Open [Instagram Post Scraper](https://apify.com/dami_studio/instagram-post-scraper) and click
   **Try for free**.
2. Paste your links into **Post or reel links**, one per line.
3. Set **Maximum posts** while you are testing, since you pay per post.
4. Click **Start**.
5. Download the dataset as JSON, CSV or Excel, or pull it from the Apify API.

Hand it no links at all and it writes one free sample row, so you see the shape before spending
anything.

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

**$0.18 per 1,000 posts.** That is the free plan's price; paid plans pay a little less: $0.178 on Starter, $0.176 on Scale and $0.174 on Business. On the free plan, the $5 Apify
gives you each month covers about 27,000 posts.

You pay per post row delivered. Duplicate links are collapsed before the run starts, so the same
post is never charged twice. The sample row, every diagnostic row, posts left out by the date
filter, and links that were never valid Instagram links cost you nothing.

**Plays, shares and Facebook counts, if you tick them: $1.38 per 1,000 posts checked on Starter,
$1.36 on Scale and $1.34 on Business**, on top of the post price. From 16 November 2026 they cost
$4.94 per 1,000 on Starter, $4.89 on Scale and $4.83 on Business. Every post checked is charged,
whatever numbers come back for it, and the option needs a paid Apify plan.

### 📥 What you give it

```json
{
  "postUrls": [
    "https://www.instagram.com/p/DbtErSrlB2J/",
    "https://www.instagram.com/reel/DcCH2ZygIiP/",
    "DbwWBxpDm_0"
  ],
  "maxItems": 10
}
```

| Field | Default | What it is |
|---|---|---|
| `postUrls` | none | The links you want: post, reel or IGTV links, links from the Share button, bare shortcodes, or numeric post ids written as text. Up to 5,000 per run, duplicates removed first. |
| `maxItems` | none | Stop after this many posts, counting from the top of your list. Leave it empty to read every link. The input form starts you at 10. |
| `onlyPostsNewerThan` | none | Keep posts published on or after a day (`2026-08-01`, UTC) or within a window (`7 days`, `2 months`). Older posts are left out, not charged, and listed in one free `LEFT_OUT_BY_DATE` row. Anything that is not a date stops the run before it reads a link. |
| `includeFacebookCounts` | off | Adds Instagram plays, shares, saves, reposts and the sound's link, plus Facebook plays, likes and comments for posts also shared there, and the Instagram-and-Facebook totals. $1.38 per 1,000 posts checked on Starter, down to $1.34 on Business ($4.94 down to $4.83 from 16 November 2026), on top of the post price, and it needs a paid Apify plan. |
| `sessionCookies` | none | Optional, and rarely needed. Your own Instagram cookie, tried on a post a logged-out run could not read. |
| `proxyUrls` | none | Leave it empty unless you have your own servers you want the traffic to leave through. |

### 📤 What you get back

A real row from a run on 21 September 2026. The caption is cut short in the middle, the media links
are trimmed, and the eight carousel frames are shown as two:

```json
{
  "ok": true,
  "charged": true,
  "recordType": "post",
  "inputUrl": "https://www.instagram.com/p/DbtErSrlB2J/",
  "id": "3957840214710558089",
  "shortCode": "DbtErSrlB2J",
  "url": "https://www.instagram.com/p/DbtErSrlB2J/",
  "type": "Sidecar",
  "productType": "carousel_container",
  "isReel": false,
  "isSponsored": false,
  "caption": "Where in the (Martian) world is Curiosity?\n \nOur Curiosity Mars rover has started its climb up a Martian valley called Valle Grande. ...\n \n#NASA #Mars #CuriosityRover",
  "hashtags": ["NASA", "Mars", "CuriosityRover"],
  "mentions": [],
  "likesCount": 284607,
  "commentsCount": 1759,
  "videoViewCount": null,
  "likesAndViewCountsDisabled": false,
  "isCommentsDisabled": false,
  "timestamp": "2026-08-06T15:49:01.000Z",
  "displayUrl": "https://scontent-mia3-3.cdninstagram.com/v/t51.82787-15/766074877_...",
  "videoUrl": null,
  "videoDuration": null,
  "images": [
    "https://scontent-mia3-3.cdninstagram.com/v/t51.82787-15/766074877_...",
    "https://scontent-mia5-1.cdninstagram.com/v/t51.82787-15/766287913_..."
  ],
  "videos": [],
  "childPosts": [
    { "id": "3957839638910891589", "shortCode": "DbtEi6bPnZF", "type": "Image", "displayUrl": "https://scontent-mia3-3.cdninstagram.com/..." }
  ],
  "childPostsCount": 8,
  "dimensionsWidth": 495,
  "dimensionsHeight": 292,
  "alt": null,
  "ownerId": "528817151",
  "ownerUsername": "nasa",
  "ownerFullName": "NASA",
  "ownerIsVerified": true,
  "ownerIsPrivate": false,
  "ownerProfilePicUrl": "https://scontent-mia5-1.cdninstagram.com/v/t51.2885-19/...",
  "ownerProfileUrl": "https://www.instagram.com/nasa/",
  "coauthorProducers": [],
  "coauthorProfiles": [],
  "taggedUsers": [],
  "sponsorProfiles": [],
  "locationName": null,
  "locationId": null,
  "musicArtist": null,
  "musicTitle": null,
  "scrapedAt": "2026-09-21T01:45:41.661Z",
  "audioId": null,
  "isOriginalAudio": null,
  "isExplicitAudio": null,
  "locationLatitude": null,
  "locationLongitude": null,
  "shareUrl": null
}
```

| Field | How to read it |
|---|---|
| `type` | `Image`, `Video` or `Sidecar`. Sidecar is Instagram's name for a carousel. |
| `productType` | The finer kind: `feed`, `clips` for a reel, `carousel_container`, `igtv`. |
| `shortCode` | Unique per post. The right key for joining runs together. |
| `timestamp` | When it was published, in UTC. It never moves, so sort and filter on this one. |
| `likesCount`, `commentsCount` | A snapshot at read time. Both come back `null` when the account has hidden its counts, and `likesAndViewCountsDisabled` says so. |
| `taggedUsers`, `coauthorProfiles`, `sponsorProfiles` | Profile objects with id, username, full name, link and picture. Tagged users carry x and y coordinates when Instagram supplies them. |
| `locationName`, `musicTitle`, `alt` | Filled in when the post carries them, `null` when it does not. Nothing is guessed. |
| `locationLatitude`, `locationLongitude` | The tagged place's coordinates, not where the phone was. `null` without a place. |
| `audioId` | The sound's id, the same on every reel that uses it. `instagram.com/reels/audio/<audioId>/` is its page. `isOriginalAudio` is `true` for the creator's own sound, `false` for a licensed track. All three are `null` on posts without a sound. |
| `shareUrl` | The share link you pasted, on the row it led to. `null` for every other link. |
| `facebookCountsFound` | Only with `includeFacebookCounts` on. `true` when Facebook numbers came back, `false` when Instagram showed the whole post and it carries none, `null` when this check could not tell. |
| `totalPlayCount`, `totalLikesCount`, `totalCommentsCount` | Instagram and Facebook added together, the way Instagram's app shows a shared post. The likes and comments totals are `null` unless Facebook numbers came back; the plays total is `null` only when the check could not tell. |
| `instagramPlayCount`, `sharesCount`, `savesCount`, `repostsCount`, `audioUrl` | Only with `includeFacebookCounts` on. Saves and reposts are not on every check, and `sharesCount` is `null` when the post has shares switched off. `audioUrl` is a signed, short-lived link, like the media links. |

### 🧾 Reading the output

Three kinds of row land in your dataset, and one flag separates them.

| Row | How to spot it | Billed |
|---|---|---|
| A real post | `ok: true` and `charged: true` | yes |
| The sample row | `_sample: true`, `charged: false` | no |
| A diagnostic | `_diagnostic: true`, `charged: false`, an `errorCode` | no |

Filter on `charged == true` and you have your posts. Do not filter on `recordType`: the sample row
carries `recordType: "post"` too. Read `charged` as the marker that separates a real row from a
sample or a diagnostic, not as a receipt.

| Code | What it means |
|---|---|
| `NOT_FOUND` | Instagram says the post is gone, or the link is wrong. |
| `LOGIN_REQUIRED` | The post is real, but it is behind a login. |
| `BAD_INPUT` | That entry is not an Instagram post link or a shortcode. |
|`RATE_LIMITED` | Instagram refused that link this time. Re-running usually clears it. |
| `NOT_SERVED` | This post was not served this run. Re-run it later. Runs before 13 October 2026 wrote `BLOCKED` here. |
| `SERVER_ERROR`, `NETWORK` | Instagram was unreachable or answered badly. Re-run it. |
| `TIME_BUDGET` | The run ran out of time before reading every link. One row lists the unread ones in `unreadShortCodes`. The run still ends as succeeded, with an amber status message, even when the time limit stopped it before any post came back. |
| `NO_RESULTS` | Nothing in the list parsed at all, so the run stopped rather than working through dead links. |
| `LEFT_OUT_BY_DATE` | Posts older than your `onlyPostsNewerThan` date. One row lists them in `leftOutShortCodes`. Not delivered and not charged. |
| `ENGINE_SWITCHED` | The run slowed down part way through. Nothing was lost and nothing extra was charged. |
| `FACEBOOK_COUNTS_OFF` | You ticked plays, shares and Facebook counts but this run could not use them: on a free Apify plan, or unavailable on this run. Nothing was looked up or charged for them. |
| `FACEBOOK_COUNTS_STOPPED` | Those checks stopped part-way, at the maximum cost you set for the run. Later posts have their fields empty. |
| `PROXY_INPUT_ADJUSTED` | A connection setting in your input could not be used and was replaced. The run carried on. |
| `CHARGE_ERROR`, `UNEXPECTED_ERROR` | Something went wrong inside the run. Not charged. If an unexpected error stopped the run before any post came back, the run ends as failed. |

A diagnostic row is never billed.

### 💡 What people use it for

- Turning a list of links from a brief into a table of captions, counts and media, in one run.
- Comparing how a competitor's carousels are built, frame by frame, for a creative review.
- Logging the like and comment counts of the same posts weekly to see how they age. Re-running the
  same links is safe and gives you a fresh snapshot.

A campaign report in three steps:

1. Paste the post and reel links your creators sent you here.
2. Keep the rows where `isReel` is `true` and copy their `url`.
3. Watch those reels and note the hook each one opens with.

### 🚧 What it does not do

- **No comment text.** The count is there, the comments are not. Those come from
  [Instagram Comments Scraper](https://apify.com/dami_studio/instagram-comments-scraper).
- **It will not walk a profile grid.** Give it links. If you want an account's posts,
  use [Instagram Scraper](https://apify.com/dami_studio/instagram-scraper).
- **Nothing private.** A post on a private account comes back as a free `NOT_FOUND` or
  `LOGIN_REQUIRED` row instead of data.
- **`videoViewCount` is usually `null`.** Instagram does not publish the play count to a logged-out
  reader. Ticking plays, shares and Facebook counts adds them, on paid plans.
- **A long list of dead links stops early.** After 40 consecutive failures with nothing parsed, the
  run writes a `NO_RESULTS` row and stops rather than burning the rest of your list.
- **If the run hits its deadline**, it stops a few seconds early, writes the posts it has, and adds
  one `TIME_BUDGET` row listing every link it did not read.
- **Media links expire.** They are signed, short-lived links.
- **Facebook numbers are not on every shared post.** They exist only for posts the account also shared
  to Facebook. When a check can't tell,
  the row says `facebookCountsFound: null` and the check is still charged.
- It is not an official Instagram tool. Dami's Studio is independent and is not affiliated with or endorsed by Instagram or Meta, or by any other company named on this page.

### 🧭 Which Instagram scraper do you need?

| If you want | Use |
|---|---|
| Posts and reels from links you already have | This one |
| Posts from an account's grid | [Instagram Scraper](https://apify.com/dami_studio/instagram-scraper) |
| One row per account, with engagement | [Instagram Profile Scraper](https://apify.com/dami_studio/instagram-profile-scraper) |
| Comments under a post or reel | [Instagram Comments Scraper](https://apify.com/dami_studio/instagram-comments-scraper) |
| Play counts for the reels on a profile | [Instagram Reel Scraper](https://apify.com/dami_studio/instagram-reel-scraper) |
| Posts under a hashtag | [Instagram Hashtag Scraper](https://apify.com/dami_studio/instagram-hashtag-scraper) |
| Posts that tag or mention an account | [Instagram Mentions Scraper](https://apify.com/dami_studio/instagram-mentions-scraper) |

### ❓ Questions people ask

#### Do I need an Instagram account to scrape posts?

No, you don't. The cookie field
is optional and takes your own Instagram cookie.

#### Does a carousel cost more than a photo?

No. One row, one charge, however many frames are in it.

#### Why do the numbers differ from what I see in the app?

Likes and comments are a snapshot at read time. If a post
comes back `LOGIN_REQUIRED`, open it in a private browser window to see whether Instagram asks you to log in
there too.

#### Why is the view count empty on reels?

Instagram does not show play counts to a logged-out reader on a single post. On a paid plan, tick
plays, shares and Facebook counts to get them. For a profile's reels, use Instagram Reel Scraper.

#### Can I call it from code or connect it to an AI assistant?

The [API tab](https://apify.com/dami_studio/instagram-post-scraper/api/python) has code for Python, JavaScript and the
CLI. MCP clients like Claude and ChatGPT use
`https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/instagram-post-scraper`; coding assistants can load [our
skill file](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/apify-instagram-SKILL.md). All three
bill your Apify account at the same price.

#### Is scraping Instagram legal?

This reads public posts only, never private accounts or messages. Results can still contain
personal data, which GDPR and similar laws cover, so have a reason for collecting it. Apify's
write-up on [the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/) is a good
starting point, and we are not lawyers.

### 🆘 If something breaks

Open the **Issues** tab on the actor page and include a link that failed and the run ID. The log
shows us the rest.

# Actor input Schema

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

One or more Instagram post, reel or IGTV links, for example https://www.instagram.com/p/DbtErSrlB2J/ or https://www.instagram.com/reel/DcCH2ZygIiP/. Bare shortcodes such as DbtErSrlB2J work too, and so do links copied from the app's Share button (instagram.com/share/...) and numeric post ids such as 3957840214710558089. Duplicates are removed before anything is charged. Up to 5,000 links per run.

## `maxItems` (type: `integer`):

Stop after this many posts, counting from the top of your list. Leave it empty to scrape every link you supplied. Keep it low while you are testing, you pay per post.

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

Keep only posts published on or after this date. Pick a day in the calendar, or switch to a relative window such as 7 days or 2 months, which suits a scheduled run. Days are counted in UTC. Older posts are read but left out: they are not delivered and not charged, and one free row lists them. Leave it empty to keep every post.

## `includeFacebookCounts` (type: `boolean`):

Adds Instagram plays, shares, saves, reposts and a link to the post's sound, plus, for posts also shared to Facebook, the Facebook plays, likes and comments and the Instagram-plus-Facebook totals. It costs $1.38 per 1,000 posts checked on Starter, down to $1.34 on Business ($4.94 down to $4.83 from 16 November 2026), on top of the post price, and every post checked is charged, whatever comes back. Not every check carries every number: saves, reposts and the Facebook side are missing on some, and those fields are then empty. Needs a paid Apify plan.

## `sessionCookies` (type: `array`):

Optional: your own session, used only for your run. You are responsible for using it within the site's terms. Runs work without it.

## `proxyUrls` (type: `array`):

Leave this empty for a normal run. Fill it in only if you want the traffic to leave through proxy servers you already pay for, one URL per line, in the form http://user:pass@host:port.

## Actor input object example

```json
{
  "postUrls": [
    "https://www.instagram.com/p/DbtErSrlB2J/",
    "https://www.instagram.com/reel/DcCH2ZygIiP/"
  ],
  "maxItems": 10
}
```

# Actor output Schema

## `results` (type: `string`):

Every row in the default dataset: shortCode, url, type, ownerUsername, caption, likesCount, commentsCount, timestamp, displayUrl, videoUrl, childPostsCount, hashtags, mentions, locationName, id, inputUrl, productType, isReel, isSponsored, images, videos, childPosts, videoDuration, videoViewCount, dimensionsWidth, dimensionsHeight, alt, likesAndViewCountsDisabled, isCommentsDisabled, ownerId, ownerFullName, ownerIsVerified, ownerIsPrivate, ownerProfilePicUrl, ownerProfileUrl, coauthorProducers, coauthorProfiles, taggedUsers, sponsorProfiles, locationId, musicArtist, musicTitle, scrapedAt, audioId, isOriginalAudio, isExplicitAudio, locationLatitude, locationLongitude, shareUrl, and with plays, shares and Facebook counts ticked the Facebook and total counts, instagramPlayCount, sharesCount, savesCount, repostsCount and audioUrl. An empty, blocked or unmatched run returns a single uncharged row explaining what happened instead.

# 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 = {
    "postUrls": [
        "https://www.instagram.com/p/DbtErSrlB2J/",
        "https://www.instagram.com/reel/DcCH2ZygIiP/"
    ],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/instagram-post-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 = {
    "postUrls": [
        "https://www.instagram.com/p/DbtErSrlB2J/",
        "https://www.instagram.com/reel/DcCH2ZygIiP/",
    ],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/instagram-post-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 '{
  "postUrls": [
    "https://www.instagram.com/p/DbtErSrlB2J/",
    "https://www.instagram.com/reel/DcCH2ZygIiP/"
  ],
  "maxItems": 10
}' |
apify call dami_studio/instagram-post-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/instagram-post-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/LDlZOcd22XzWoetpM/builds/2GV09xDON4AZSgiGD/openapi.json
