# Instagram Comments Scraper (`dami_studio/instagram-comments-scraper`) Actor

Instagram comments scraper: export the comments and replies on a public post or reel. Every row has the author's username and name, the text, the time, likes, reply count and the verified flag; GIF comments come with a title and link. One run reads the top 75 to 150 rows. $0.40 per 1,000 rows.

- **URL**: https://apify.com/dami_studio/instagram-comments-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Social media
- **Features:** Limited permissions
- **Stats:** 45 total users, 10 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.38 / 1,000 results

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 comments scraper for a post or reel link, comments and replies as flat rows**: who
wrote each comment, by username and by name, what it says, when, how many likes it picked up, how
many replies hang off it, and whether the author is verified. Every row links to its post and to
the comment itself. A comment that is only a GIF comes back with the GIF's title and a link to it.

Here is the part worth knowing before you start. A run reads each thread from the top down, in
Instagram's order, and stops at the number of rows you ask for, up to 2,000. By default, replies come
back only where Instagram sends them alongside a comment; switch on every reply for the rest. On the free Apify plan a run reads one page of the
first post.

| | |
|---|---|
| **Input** | Post or reel URLs, with or without the account name in front, bare shortcodes or numeric post ids |
| **Output** | One row per comment or reply: text, author username and name, verified and private flags, likes, reply count, time, post and comment links, GIF title and link |
| **Ceiling** | 2,000 rows per run |
| **Account needed** | None |
| **Price** | $0.40 per 1,000 rows on the Free plan, a little less on paid plans. The free plan's $5 a month covers about 12,500 |

### 🔍 What Instagram Comments Scraper does

You give it links, it gives you the conversation under them. Comments and replies land in the same
row shape, so you can drop the lot into a sheet without branching on type. A reply carries
`isReply: true` and a `parentCommentId`, which is enough to rebuild the tree or to filter replies
out with one condition.

You don't need an Instagram account.

If you would rather the run read through your own Instagram account, you can put your own session in
`sessionCookies`. It is used only for your run, and you are responsible for using it within the site's terms. Most people leave it
empty and never think about it again.

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

| What you get | Field |
|---|---|
| The comment's id, and the post it sits under | `id`, `mediaId` |
| What it says | `text` |
| A GIF-only comment's title and link | `hasGif`, `gifTitle`, `gifUrl` |
| Likes and how many replies it has | `likeCount`, `replyCount` |
| When it was written | `createdAt` |
| Who wrote it | `ownerUsername`, `ownerFullName`, `ownerId`, `ownerVerified` |
| Whether it is a reply, and to which comment | `isReply`, `parentCommentId` |
| Links to the post, the comment and, on a reply, its parent | `postUrl`, `commentUrl`, `parentCommentUrl` |
| Where it sits in Instagram's order for that post | `threadPosition` |
| Whether the author's account is private | `ownerIsPrivate` |
| The author's profile picture link, only when you ask for it | `ownerProfilePicUrl` |

### ▶️ How to scrape Instagram comments

1. Open [Instagram Comments Scraper](https://apify.com/dami_studio/instagram-comments-scraper) and
   click **Try for free**.
2. Paste your links into **Post URLs**, one per line.
3. Set **Max comments**. Start at 15 while you look at the output shape. For the same number from
   every post, also fill in **Max comments per post**.
4. Click **Start**.
5. Download the dataset as JSON, CSV or Excel, or read it from the Apify API.

Start it with no post at all and you get five labelled sample rows, which are not charged.

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

**$0.40 per 1,000 comment rows** on the Free plan. Paid plans pay a little less: $0.395 on Starter, $0.39 on Scale and $0.385 on Business. On the free plan, the
$5 Apify gives you each month covers about 12,500 rows. On the free plan a run stops after one page
and says so in a free row; a paid plan gets the full run.

You pay per row delivered. The same comment is never delivered twice in a run, so you never pay for
it twice. Sample rows and diagnostic rows are not billed, and neither is a post that turns out to
have nothing under it. Extra replies are rows like any other, at the same price.

### 📥 What you give it

```json
{
  "postUrls": ["https://www.instagram.com/p/Db9IVmrDvQ4/"],
  "maxComments": 100
}
```

| Field | Default | What it is |
|---|---|---|
| `postUrls` | none | Post, reel or IGTV links. `/p/`, `/reel/`, `/reels/` and `/tv/` all work, with or without the account name in front. As many as you like, one per line. |
| `shortcodes` | none | Or the bare code after `/p/` or `/reel/`, or the post's numeric id. You can mix the two fields. |
| `maxComments` | `100` | Rows for the whole run, split evenly across the links you gave. 1 to 2,000. Replies count towards it. |
| `maxCommentsPerPost` | none | Rows from every post instead of an even split, replies included. `maxComments` still caps the run, so raise it too: 10 posts at 100 each need 1,000. |
| `includeReplies` | `true` | Turn it off for top-level comments only. |
| `includeAllReplies` | `false` | Every reply, not only the ones Instagram shows beside a comment. The run writes a page of comments, then the replies under each one that has at least three more, in order. Each reply is a row at the comment price and counts towards `maxComments`. Needs `includeReplies` on; not used with your own cookies. |
| `maxRepliesPerComment` | none | With every reply on: at most this many replies under each comment, the ones Instagram shows included. |
| `includeProfilePicUrl` | `false` | Adds `ownerProfilePicUrl`, a link to the commenter's profile picture. The run never downloads or keeps the picture. |
| `sessionCookies` | none | Optional: your own session, if you would rather the run read through your account. It is used only for your run, and you are responsible for using it within the site's terms. |
| `proxyConfiguration` | none | Leave it alone unless you have servers of your own you want used. |
| `notionConnector`, `notionParentId` | none | Optional. Write each comment into a Notion data source as well as the dataset. |
| `notionMaxRows` | none | Send only the first this many rows to Notion. The dataset still gets them all. |

A link that is not a post, or a shortcode that cannot exist, gets a labelled row and no lookup.

### 📤 What you get back

A real row read on 25 October 2026, a brand account commenting on a football post. Its closing
emoji is cut from this block:

```json
{
  "ok": true,
  "type": "comment",
  "id": "18107041583611449",
  "mediaId": "3993517018170036322",
  "text": "THE GOAT",
  "hasGif": false,
  "gifTitle": null,
  "gifUrl": null,
  "likeCount": 28569,
  "replyCount": 132,
  "createdAt": "2026-09-24T21:18:00.000Z",
  "ownerUsername": "luxury",
  "ownerFullName": "Luxury",
  "ownerId": "1122059043",
  "ownerVerified": true,
  "isReply": false,
  "parentCommentId": null,
  "postUrl": "https://www.instagram.com/p/Ddr0pHIgJBi/",
  "commentUrl": "https://www.instagram.com/p/Ddr0pHIgJBi/c/18107041583611449/",
  "parentCommentUrl": null,
  "threadPosition": 2,
  "ownerIsPrivate": false,
  "ownerProfilePicUrl": null
}
```

A GIF-only comment from an earlier run, link shortened:

```json
{
  "text": "",
  "hasGif": true,
  "gifTitle": "The Boys Messi GIF",
  "gifUrl": "https://cdn.fbsbx.com/v/t59.2708-21/822297128_..._n.gif?..."
}
```

| Field | How to read it |
|---|---|
| `id` | Unique per comment. Use it as your dedupe key across runs. |
| `mediaId`, `postUrl` | The post the comment sits under, as Instagram's id and as a link. |
| `commentUrl`, `parentCommentUrl` | Built from the post and the ids on the row. They open the comment for someone signed in to Instagram. `parentCommentUrl` is `null` on a top-level comment. |
| `isReply`, `parentCommentId` | `false` and `null` on a top-level comment. On a reply, `true` and the id of its parent. |
| `replyCount` | How many replies Instagram says the comment has, which is often more than the number that came back. |
| `threadPosition` | 1 for the top comment of that post, in the order this run read it. A reply carries its parent's number. The order moves between runs. |
| `ownerFullName` | The name on the commenter's profile. `null` when they have not set one. |
| `ownerIsPrivate` | `true` when the commenter's account is private. |
| `ownerProfilePicUrl` | `null` unless you turned on `includeProfilePicUrl`. The link expires after a while. |
| `hasGif`, `gifTitle`, `gifUrl` | A comment that is only a GIF has an empty `text`. Then `hasGif` is `true` and the other two describe the GIF. Otherwise `false`, `null`, `null`. |
| `createdAt` | ISO timestamp, or `null` when unknown. Nothing is guessed. |

### 🧾 Reading the output

Three kinds of row can land in your dataset, and only the first kind is billed.

| Row | How to spot it | Billed |
|---|---|---|
| A comment or reply | `ok: true` and `type: "comment"` | yes |
| A sample row | `sampleData: true`, `charged: false` | no |
| A diagnostic | `ok: false` and an `errorCode` | no |

Filter on `ok == true` and you have your thread. Sample rows appear only when you start a run
without naming a post, so a run that names a real post never answers with invented data.

| Code | What it means |
|---|---|
| `BAD_INPUT` | That link is not a post or reel link, or that shortcode cannot exist. Nothing was looked up. |
| `NOT_FOUND` | The post is deleted, private, or the link is wrong. |
| `COMMENTS_DISABLED` | The owner switched comments off on that post. |
| `NO_RESULTS` | Instagram returned no comments for that post: it has none yet, comments are switched off, or it is no longer public. |
| `FREE_PLAN_LIMIT` | You are on the free Apify plan, so the run read only part of your input. `postsNotRead` lists what it skipped. A paid plan gets the full run. |
| `PARTIAL` | The run stopped before it had all you asked for: it read as many pages as one run may, or reached its maximum cost. With every reply on, it can also say how many comments came back without all of their replies (`commentsWithoutAllReplies`). What arrived is complete and correct. |
| `TIME_LIMIT` | The run reached its time limit and stopped on its own. The comments delivered before that are complete and charged. If none had come back yet, the run still ends as succeeded, with an amber status message. Give it more time, or ask for fewer comments. |
| `RATE_LIMITED` | Too much traffic on that post just now. Try again shortly. |
|`LOGIN_REQUIRED` | The thread could not be opened this time. If you supplied a cookie, check it is still valid. |
| `NOT_SERVED` | This request was not served this run. Re-run it later. A post from a private account always gets it. Runs before 13 October 2026 wrote `BLOCKED` here. |
| `SERVER_ERROR` | Instagram answered badly. Re-run it. |
| `UNEXPECTED_ERROR` | The run stopped early on an unexpected error. The results before this row are kept. |

A run that could not read what it was asked for ends as failed, with a labelled row telling you
why, because a green tick over an empty dataset is the worst answer a scraper can give. These cases
succeed instead, each with its own row: a post with no comments (`NO_RESULTS`), comments switched
off (`COMMENTS_DISABLED`), a link that is not a post (`BAD_INPUT`), a post that no longer exists
(`NOT_FOUND`) and a post that was not served (`NOT_SERVED`). If at least one link produced
comments, the run succeeds and the rest get their own rows. No diagnostic row is billed as a comment.

### 💡 What people use it for

- Reading what people actually said under a launch post, rather than counting the likes.
- Going through the entries on a giveaway or a competition post by hand.
- Collecting a few hundred comments across a set of posts to see which complaint keeps coming up.
- Checking whether a creator's engagement looks like a conversation or like the same ten accounts.

### 🚧 What it does not do

- **No more than 2,000 rows a run.** A post with 60,000 comments comes back as its top 2,000, in
  Instagram's order.
- **Every reply only when you ask.** Otherwise replies come only where Instagram shows them, and a
  comment with just one or two more keeps the ones shown.
- **The order is ranked, not newest first**, and you cannot change it.
- **No private accounts.** Public posts and reels only.
- **No comment likers**, no editing, no posting, no deleting. This reads.
- 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 |
|---|---|
| Comments under a post or reel | This one |
| The post itself, caption and counts | [Instagram Post Scraper](https://apify.com/dami_studio/instagram-post-scraper) |
| Everything one account has posted | [Instagram Scraper](https://apify.com/dami_studio/instagram-scraper) |
| Posts under a hashtag | [Instagram Hashtag Scraper](https://apify.com/dami_studio/instagram-hashtag-scraper) |
| Reels, with plays and audio | [Instagram Reel Scraper](https://apify.com/dami_studio/instagram-reel-scraper) |
| Posts that tag or mention an account | [Instagram Mentions Scraper](https://apify.com/dami_studio/instagram-mentions-scraper) |
| Comments under a Facebook post or reel | [Facebook Comments Scraper](https://apify.com/dami_studio/facebook-comments-scraper) |
| Comments under a TikTok video | [TikTok Comments Scraper](https://apify.com/dami_studio/tiktok-comments-scraper) |

### ❓ Questions people ask

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

No. Leave `sessionCookies` empty and it works. The field is there for people who would rather the
run read through their own account.

#### Can I get every comment on a huge post?

Not on a big one. A run reads up to 2,000 rows from the top of the thread, so a post with 60,000
comments comes back as its top 2,000.

#### Why did I get fewer rows than I asked for?

Either the thread ran out, or the run stopped early: it read as many pages as one run may, or it
reached its maximum cost. The `NO_RESULTS` or `PARTIAL` row in your dataset says which.

#### Why is the order different from what I see in the app?

The app and a run can put comments in a different order. Rows come back in Instagram's order, top first.

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

The [API tab](https://apify.com/dami_studio/instagram-comments-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-comments-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. Comments still contain personal
data, which GDPR and similar laws cover, so have a reason for collecting them. 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. Send the post link you used and the run ID. The code on
the diagnostic row usually explains it on its own.

# Actor input Schema

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

Instagram post or reel URLs to read comments from. Links copied from a profile, with the account name in front, work too. Paste as many as you like, one per line.

## `shortcodes` (type: `array`):

Or bare post shortcodes, the part of the URL after /p/ or /reel/, or numeric post ids.

## `maxComments` (type: `integer`):

How many comment rows to return in total, up to 2,000, spread evenly across the posts you gave. Replies that Instagram returns alongside a comment are delivered as their own rows, marked `isReply`, and count towards this number. You are charged per comment row returned. Rows come from the top of each thread down, in Instagram's order. If a run stops short it says so in a free row rather than pretending it finished.

## `maxCommentsPerPost` (type: `integer`):

Leave empty to split Max comments evenly across your posts. Set it to read up to this many rows from every post instead, replies included. Max comments still caps the run as a whole, so raise it too: for 10 posts at 100 each, set Max comments to 1,000.

## `includeReplies` (type: `boolean`):

On by default: replies that Instagram returns alongside a comment come back as their own rows, marked `isReply`. Turn it off to get top-level comments only. The replies are then neither delivered nor charged.

## `includeAllReplies` (type: `boolean`):

Off by default. When on, the run also reads the replies Instagram does not show alongside a comment, for comments with at least three more. Each reply is a row, billed like a comment, and counts towards Max comments. Needs Include replies on. Not used when you supply account cookies.

## `maxRepliesPerComment` (type: `integer`):

Only with every reply on. At most this many replies under each comment, the ones Instagram shows included. Leave empty for no limit per comment.

## `includeProfilePicUrl` (type: `boolean`):

Off by default. When on, each row carries `ownerProfilePicUrl`, a link to the commenter's profile picture as Instagram hosts it. The run never downloads or keeps the picture, and the link stops working after a while.

## `notionConnector` (type: `string`):

Optional Notion delivery.

## `notionParentId` (type: `string`):

Optional Notion data-source id.

## `notionMaxRows` (type: `integer`):

Leave empty to send every comment to Notion. Set it to send only the first this many rows; the dataset still gets them all.

## `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.

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

Optional, and normally not needed. If you want the run to go out through YOUR own proxy servers, put them in proxyUrls, they are used exactly as given.

## Actor input object example

```json
{
  "postUrls": [
    "https://www.instagram.com/p/Db9IVmrDvQ4/"
  ],
  "shortcodes": [
    "Db9IVmrDvQ4"
  ],
  "maxComments": 100,
  "includeReplies": true,
  "includeAllReplies": false,
  "includeProfilePicUrl": false
}
```

# Actor output Schema

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

Results in the default dataset.

# 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/Db9IVmrDvQ4/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/instagram-comments-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/Db9IVmrDvQ4/"] }

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/instagram-comments-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/Db9IVmrDvQ4/"
  ]
}' |
apify call dami_studio/instagram-comments-scraper --silent --output-dataset

```

## MCP server setup

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