# Instagram Comments Scraper — Scrape Instagram Comments, Replies (`steadyfetch/instagram-comments-scraper`) Actor

Scrape Instagram comments and their replies for any post, reel or IGTV URL — text, author, likes, timestamps. No login. Replies open thread by thread until the run's reply budget is spent, and each comment says whether its thread was opened. A reply costs the same. From $0.60 per 1,000 comments.

- **URL**: https://apify.com/steadyfetch/instagram-comments-scraper.md
- **Developed by:** [Steadyfetch Team](https://apify.com/steadyfetch) (community)
- **Categories:** Social media, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 1,000 comments

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?

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 Comments Scraper — Scrape Instagram Comments, Replies

Paste the Instagram post, reel or IGTV links you want and get the comments underneath them: text, author, likes, timestamp, and the replies in each thread. No login, no cookies, no proxy to configure. **Your limit is exact and it is per post — set 200 and you get 200 from each link**, and a comment we could not deliver is never charged. **Replies cost the same as a top-level comment**, on the same single event. Nothing is charged for starting a run.

**Using an AI agent?** Pin this actor in Apify's MCP server with one link: `https://mcp.apify.com?tools=steadyfetch/instagram-comments-scraper`

- **Actor id:** `steadyfetch/instagram-comments-scraper`
- **Input:** `{ "postUrls": ["CA2aJYrg6cZ"] }` — the one field you have to set. A bare post code as shown, or the whole post, reel or IGTV link you copied. Add `"resultsLimit": 200` and `"includeReplies": true` to set the per-post limit and pull the threads.
- **Cap the bill:** set `maxTotalChargeUsd` on the run (a run option, not Actor input), e.g. `0.50` — the run stops when it reaches it.
- **Price:** from **$0.60 per 1,000 comments** on Gold and above, $2.40 per 1,000 on the Apify free plan — a reply is charged on that same event, at that same price. No start fee, and a comment we could not deliver is never charged. Full table below.
- **How often it changes:** comments keep arriving for days after a post goes up and the busiest hours are the first few, so the useful pattern is the same links again tomorrow — the ones you already have are skipped rather than charged again, so a daily watch pays only for what is new.

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

### What you get

One row per comment, at one price — a reply is a row like any other, and there is no second charge for "threads":

| Column | What it holds |
|---|---|
| `commentId` | the comment's own Instagram id |
| `text` | what they wrote (empty when the comment is a GIF — `isGif` says so) |
| `username` · `userFullName` · `userId` · `userIsVerified` | who wrote it |
| `likeCount` | likes on the comment |
| `isReply` · `parentCommentId` · `repliedToCommentId` | whether it is a reply, to which comment, and to which reply inside the thread |
| `replyCount` · `repliesCollected` | how many replies the comment has, and whether this run opened the thread |
| `createdAt` | when it was posted, ISO-8601 UTC |
| `isPinned` · `isEdited` · `isByPostAuthor` | pinned by the author, edited, or left by the account that posted |
| `postShortcode` · `postUrl` · `source` | which post it belongs to (`source` is always `instagram`) |
| `charged` · `status` · `statusReason` | whether this row was billed, and why, so your invoice reconciles from the dataset itself |

Replies land directly under the comment they answer, so the thread reads in order. The requesting network address is stripped out of every link before it reaches your dataset.

### What a row looks like

One delivered row from a real run — the post `DcOX3hWFiey`, 2026-09-11. Every comment is one `comment` charge, and a reply costs the same as a top-level comment: `isReply` and `parentCommentId` tell them apart. Profile pictures are signed and time-limited by Instagram, so they are shown here as placeholders.

```json
{
  "commentId": "17979728991110710",
  "postShortcode": "DcOX3hWFiey",
  "postUrl": "https://www.instagram.com/p/DcOX3hWFiey/",
  "text": "Image description: The Tarantula Nebula—vibrant and colorful. Clouds of red, yellow, orange, and green crest over a background of deep blue haze, black space, and red stars. Bright spots of light line the center, causing parts of the clouds to glow luminous gold.",
  "username": "nasa",
  "userId": "528817151",
  "userFullName": "NASA",
  "userIsVerified": true,
  "userProfilePicUrl": "https://…",
  "isReply": false,
  "parentCommentId": null,
  "repliedToCommentId": null,
  "likeCount": 2213,
  "replyCount": 0,
  "createdAt": "2026-08-19T14:11:50.000Z",
  "isPinned": true,
  "isEdited": false,
  "isByPostAuthor": true,
  "isGif": false,
  "repliesCollected": true,
  "source": "instagram",
  "input": "https://www.instagram.com/p/DcOX3hWFiey/",
  "sourceIndex": 0,
  "scrapedAt": "2026-09-11T10:25:40.411Z",
  "repeat": false,
  "charged": true,
  "status": "delivered",
  "retryable": false,
  "statusReason": null,
  "#k": "17979728991110710",
  "#ce": {"comment":1},
  "#ask": {"delivered":1}
}
```

### Price

One charged event, `comment`, and it lands only on a comment that was actually delivered. A reply is charged on that same event, at that same price.

| Apify plan | Per comment | Per 1,000 comments |
|---|---|---|
| Free plan | $0.0024 | $2.40 |
| Bronze | $0.0014 | $1.40 |
| Silver | $0.0009 | $0.90 |
| Gold and above | $0.0006 | **$0.60 per 1,000** |

Platform usage is included in that price — there is no separate compute bill on top, and no start fee. Set `maxTotalChargeUsd` on the run and it is a hard ceiling: the run stops cleanly under it and the last row says what is left.

### Honest rows: what "not charged" actually means

Every run closes its own books. A post that returned less than it was asked for leaves an uncharged row saying which of nine things happened, and none of them is billed:

- **`post_not_found`** — Instagram has nothing at that link: wrong code, private account, or the post was deleted. A definitive answer about your link.
- **`no_comments`** — Instagram's own answer about the post, and the row says which half it is. The post is real and has none, because comments are turned off for it or nobody has commented yet; or it served no further page after the comments you were given. A definitive answer, not a failure.
- **`skipped.page_cap`** — one of this actor's own bounds stopped the walk: it reads at most 400 comment pages for a single post, so one post cannot spend a whole run. Instagram had more to give — that bound is ours, the row says so, and it tells you how to carry on from where the run stopped.
- **`skipped.all_repeat`** — this actor's other bound: three pages in a row held only comments you already have, so the walk stopped rather than keep buying pages that add nothing. That bound is ours too, and the row names the setting that collects them again.
- **`stopped_at_limit`** — one of your own limits, or the run clock, stopped it first. The row names which.
- **`vendor_unavailable`** — the read did not go through. Temporary, says nothing about your post, and a re-run is the fix. If comments for that post had already been delivered before it went dark, the row opens with how many: those were charged, as each of their rows says, and the re-run is for the rest.
- **`source_refused`** — Instagram refused to answer for that link at all, and it refuses it the same way every time, so a re-run cannot change it. Open the link in a browser; if the post is there, say so on the Issues tab.
- **`unsupported_shape`** — Instagram answered in a shape this actor does not read yet. That is on us, not on your post, and a re-run gets the same answer — tell us on the Issues tab which post it was and support for it will be added.
- **`vendor_budget`** — this actor reached its own monthly collection allowance and stopped rather than collecting more.

The last row of every run is a receipt: delivered, how many were replies, asked for, what was skipped, what stopped it, and the charged-event totals. A time limit ends the collecting, never the delivering — comments already in hand are always written out.

### How often this data changes

Comments keep arriving on a post for days after it goes up, and the busiest hours are the first few. So the useful pattern here is not one run — it is the same links again tomorrow. Re-running would otherwise re-buy the comments you already have, so it does not: every run remembers what it delivered to your account, in the key-value store `ig-comments-account` in your own Apify account, and a later run skips those. Not delivered again, not charged, and the slot goes to the next new comment instead, so you still get the number you asked for. The memory is a key-value store in your own Apify account, so a run started with a scoped API token in restricted-access mode needs key-value store Read, Write and Create permission (Write is what lets the run remember what it delivered; without it every run pays again) or Actor runs set to Full access; without it the run says so on its status line and on an uncharged row, and cannot skip the comments you already have. That makes a daily watch on the same posts cost only what is new, as long as the token the watch runs on can open that store. Switch **Include comments you already have** on to get them back anyway, marked `repeat: true` and still uncharged, and use **Skip comments in this dataset** to carry the same memory across accounts.

### Replies, and when a thread is not opened

With **Include replies** on (the default), a comment that has a thread gets that thread collected and delivered right under it, at the same price. A very long run of one-reply threads costs more to collect than the comments are priced at, so the run bounds that: when it happens, the remaining threads are left unopened, those comments carry `repliesCollected: false`, and the run line and the receipt both say so rather than leaving you to notice. Top-level comments are never affected.

### Reliability

The comments come from a licensed data feed rather than from scraping Instagram's own web pages, which is why there is nothing to log into and nothing to configure. When that feed cannot answer, the run says so on an uncharged row and finishes successfully — a failed read is never billed and never dressed up as a post with no comments.

### Related actors

- One account's own posts, to feed this one links: **Instagram Profile Posts Scraper** — https://apify.com/steadyfetch/instagram-profile-posts
- The spoken words inside a reel: **Instagram Reel Transcript Scraper** — https://apify.com/steadyfetch/instagram-reel-transcript-scraper

### Limits and good manners

**Your settings are never refused for being too big.** This actor delivers up to 5,000 comments per post (`resultsLimit`) and runs for up to 1 hour (`maxRunSeconds`, minimum 30 seconds). Ask for more than that — or for a shorter clock — and the run still starts: it continues at the nearest limit and leaves one uncharged row saying what you asked for and what bound it. Nothing is refused before it starts.

Only public posts are returned. Instagram serves comments in its own ranked order rather than strictly newest-first, and a very deep ask on a post will eventually run out — the run says so instead of padding. This actor may stop working if Instagram changes how the data is served; if it does, failed items are never charged.

### Support

Something off, or a column you need that is not here? Open an issue on the Issues tab — we usually reply within a couple of hours.

If it earned its keep, a rating helps other buyers find it, and saving the actor keeps it one click away.

# Actor input Schema

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

Instagram post links, one per line — https://www.instagram.com/p/CA2aJYrg6cZ/, https://www.instagram.com/reel/DcOX3hWFiey/, an /tv/ link, or just the code (DcOX3hWFiey); "\_demo" is the sample switch, not a link. A link that does not resolve, and a post with comments turned off, are delivered as labelled rows and are never charged. A profile link or a hashtag link is refused with a row naming the actor that does take it. Leave it empty and the run returns built-in sample rows instead of collecting anything, so you can see the output shape at no result fee.

## `resultsLimit` (type: `integer`):

Hard cap on the comments delivered for EACH post link — three links at 200 collect up to 600 in total. Replies count against this limit like any other comment. A post that runs short leaves an uncharged row saying so. This actor delivers at most 5,000 comments per post: ask for more and the run continues at 5,000, with one uncharged row saying so, rather than being refused.

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

ON (default): the replies under a comment are collected and delivered right after it, with `isReply: true` and `parentCommentId` set — at the SAME price as a top-level comment, and counted against the same limit. OFF: only top-level comments. A comment whose thread was not opened carries `repliesCollected: false`.

## `maxRunSeconds` (type: `integer`):

The run stops cleanly before this many seconds and reports what is left, instead of being killed by a timeout. A time limit ends the collecting, never the delivering: comments already in hand are always written out. The clock runs between 30 seconds and 1 hour: set it outside that and the run continues at the nearest one, with one uncharged row saying so, rather than being refused.

## `includeSeen` (type: `boolean`):

OFF (default): a comment already delivered to your account is skipped — no row, no charge — and the next new comment takes its place, so you still get the number you asked for. ON: those comments are delivered again anyway, marked `repeat: true`, and still not charged. Comments older than 90 days in your account's memory count as new again either way.

## `resumeFromDatasetId` (type: `string`):

The dataset ID of a previous run of this actor. Comments already in it are treated as comments you already have and are skipped, so a follow-up run collects only what is new even from a different Apify account. Selecting it here is what grants this run permission to read it.

## Actor input object example

```json
{
  "postUrls": [],
  "resultsLimit": 100,
  "includeReplies": true,
  "maxRunSeconds": 600,
  "includeSeen": false
}
```

# Actor output Schema

## `comments` (type: `string`):

One row per comment delivered: text, author, user id, likes, reply count, timestamp and the post it belongs to. A reply is a row like any other, marked `isReply: true` with `parentCommentId` set, and it is charged at the SAME price as a top-level comment — there is no second event for replies and no charge for starting a run. Every row carries `charged` and `status`, so the invoice reconciles from the dataset itself: only rows with charged = true were billed. Comments your account already had are skipped before they take a slot and are never charged again — that check reads a key-value store in your own account, so a run on a scoped API token without key-value store permission cannot make it and says so on its status line and on an uncharged row; with "Include comments you already have" they come back marked `repeat: true`, still uncharged.

## `misses` (type: `string`):

One uncharged row for every post that returned less than it was asked for, saying which of nine things happened: `post_not_found` (the link is wrong, the account is private, or the post was deleted), `no_comments` (Instagram's own answer — the post is real and has none, or it served no further page), `skipped.page_cap` (this actor reads at most 400 comment pages for one post — that bound is ours, and the row says how to carry on), `skipped.all_repeat` (three pages in a row held only comments you already have, so the walk stopped — that bound is ours too), `stopped_at_limit` (one of your own limits or the run clock stopped it first), `vendor_unavailable` (the read failed and a re-run is the fix), `source_refused` (Instagram refused to answer for that link at all, the same way every time — a re-run cannot change it), `unsupported_shape` (Instagram answered in a shape this actor does not read yet — on us, and a re-run gets the same answer), `vendor_budget` (this actor reached its own monthly collection allowance). None of these is charged.

## `summary` (type: `string`):

Comments delivered, how many of them were replies, what was asked for, comments your account already had, what stopped the run, the charged-event totals, and — when this run's token could not open the repeat memory — `repeatCheck: "unavailable_scope"`.

# 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": []
};

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steadyfetch/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/599Fdg1hn6Pga2rEG/builds/XNR1ZoieVz2ILG5Ut/openapi.json
