# Instagram Comment Scraper with Sentiment (`omaraw/instagram-comment-scraper`) Actor

Scrape Instagram comments from single post URLs or from an account's recent posts, with a sentiment label and score on every comment. $1.725 per 1,000 comments: 33% below the Free plan price of apify/instagram-comment-scraper, and the same flat rate on every plan.

- **URL**: https://apify.com/omaraw/instagram-comment-scraper.md
- **Developed by:** [itnlab](https://apify.com/omaraw) (community)
- **Stats:** 2 total users, 1 monthly users, 75.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.75 / 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.

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 Comment Scraper with Sentiment

Scrape **Instagram comments** from an account's recent posts at a flat **$1.75 per 1,000 comments** for every buyer, with a **sentiment label and score already on every comment**.

No second pipeline, no second API bill. Each row arrives as `positive`, `neutral` or `negative` with a confidence score, so "what did people actually say about this launch" is one run rather than a scrape followed by a classification job.

### Two kinds of input, and they can be mixed

Give it a **post URL** — `https://www.instagram.com/p/DCMUrLltVlM/`, or the `/reel/` and `/tv/` spellings of the same thing — and it reads the comments under exactly that post. Give it an **account** — `https://www.instagram.com/nasa/` or `nasa` — and it walks that account's most recent posts, collecting the comments under each. One list may hold both, and the same post named twice is collected once.

Since 3 September 2026. Before that a post URL was refused, because the query behind a single post was reachable only to a signed-in visitor; it is not any more, so it is a target.

What is still refused, by name and with a reason rather than as an empty dataset: hashtag pages, place pages, stories, `/tagged/` URLs, the reels *home* feed (`instagram.com/reels/` with no post after it) and Instagram-wide search. Those are outside what a logged-out visitor can see at all, which is not a limit this Actor can lift.

### How to use the Instagram comment scraper

1. Paste post URLs (or profile URLs / handles) into **Instagram posts or reels URLs** (`directUrls`).
2. Set **Up to \[number] of comments** (`resultsLimit`) — up to 5,000 per post.
3. Leave **Include replies** (`includeNestedComments`) off — see the FAQ.
4. Press **Start**.

Those are the whole form: the same three fields as `apify/instagram-comment-scraper`, so an input written for it runs here unchanged. An account in `directUrls` reads the comments under its 12 most recent posts (one feed page), so an account run is roughly 12 × `resultsLimit` comments at most. The collector paces itself at about one comment per second per post, deliberately, because a client that transacts at a constant rate for twenty-four hours is not a person.

### How it compares

What this listing does that the others on the Store do not: **comments** come back one row each with `sentimentLabel` and `sentimentScore` on them. Sentiment is sold here and only here — the sibling listings, **Instagram Scraper** and **Instagram Profile Scraper**, return no sentiment columns. A run of *this* listing writes comment rows and nothing else.

Prices below marked *measured* come from runs made on 2 September 2026 against an identical list of public accounts; those marked *published* are the vendor's own figure and were not re-tested here. Rows left as — are outside that Actor's scope rather than a shortcoming.

| Feature | This Actor | apify/instagram-scraper | apify/instagram-profile-scraper | apify/instagram-comment-scraper | apidojo/instagram-scraper |
| ----- | ---- | ----------- | ----------- | ------------- | ------------- |
| Pricing plan | pay per result | pay per result | pay per result | pay per result | pay per result |
| Price per 1,000 profiles or posts | — | $2.70 *(Free plan, tiered)* | $2.60 *(Free plan, tiered)* | — | $0.50 *(published)* |
| Price per 1,000 comments | **$1.75** *(flat)* | $2.70 *(Free plan - it charges per result, whatever the row)* | — | $2.60 *(Free plan, tiered)* | — |
| Sentiment label + score on every comment | ✔️ | ❌ | ❌ | ❌ | ❌ |
| Usable on the Apify free plan | ✔️ | ✔️ | ✔️ | ✔️ | ❌ *(demo only, 10 items)* |
| Profile URLs | ✔️ | ✔️ | ✔️ | ❌ | ✔️ |
| Single post URLs | ✔️ | ✔️ | ❌ | ✔️ | reels URLs *(published)* |
| Hashtag, place, audio URLs | ❌ | hashtag + place *(published)* | ❌ | ❌ | ✔️ |
| An error row for every failed input | ✔️ | ✔️ *(published)* | ✔️ *(published)* | ✔️ *(published)* | ❓ |
| `parentCommentUrl` on comment rows | ✔️ *(null, as theirs is)* | — | — | ✔️ *(published)* | — |
| `hashtags`, `mentions`, `taggedUsers`, `dimensions*` on post rows | ✔️ *(tagged accounts minimised; on Instagram Scraper)* | ✔️ *(published)* | ✔️ *(published)* | — | ✔️ *(published)* |

### How many comments you get

`resultsLimit` is a number of comments **per post**, and it is a promise: ask for 1,000 and a run returns 1,000, or every comment the post exposes when that is fewer. A post with 145 comments answers with 145 rows and no error.

Measured 7 September 2026 on `p/Dc6zTucHTv8` (@kimkardashian): three consecutive runs asking for 1,000 comments, three runs returning exactly 1,000.

Where fewer come back it is Instagram's doing, not this Actor's, and the number to compare against is not `commentsCount`. That is Instagram's own total and it counts **replies and filtered comments a logged-out reader is never served** — the same post declares ~4,350 and its logged-out connection carries ~3,350. This Actor also does not take Instagram's first "there are no more" for an answer: the page that announced the end is fetched again before a run reports one, because an unconfirmed end was measured arriving hundreds of comments early. When a walk still ends short, the run log names the post, what was asked for, what came back and what Instagram declares, so a short answer never has to be guessed at.

### Pricing

Pay per comment, no platform usage charged on top.

| Result | Price each | Per 1,000 | apify/instagram-comment-scraper, Free plan |
|---|---|---|---|
| Comment | $0.00175 | **$1.75** | $2.60 |

A run of this Actor writes comment rows and nothing else, so this is the only rate it can charge.

**One flat rate, for everybody.** $1.75 per 1,000 — no plans, no tiers, no volume brackets, and no Apify platform usage charged on top. That is 32% below the $2.60 that `apify/instagram-comment-scraper` charges its Free-plan buyers (read from Apify's public API, 17 September 2026); their rate moves with the subscription a buyer is on, and ours does not move at all. The sentiment label on every row is not priced separately at all: no comparable listing on the Store sells it, so there is no rate of theirs to hold it against.

A post that turns out to have no comments costs nothing — you pay for comment rows, not for asking.

### Output example

One comment row, exactly as it lands in the dataset. Values are from a real run
on 10 September 2026 against a public post:

```json
{
  "postUrl": "https://www.instagram.com/p/Dc6zTucHTv8",
  "commentUrl": "https://www.instagram.com/p/Dc6zTucHTv8/c/18010741988975472",
  "id": "18010741988975472",
  "text": "Зустрічааайййтеееее 🙌",
  "ownerUsername": "kalytsynskyi",
  "ownerProfilePicUrl": "https://scontent.cdninstagram.com/v/t51.82787-19/631648810_...jpg",
  "timestamp": "2026-09-10T14:32:50.000Z",
  "repliesCount": null,
  "replies": null,
  "parentCommentUrl": null,
  "likesCount": 0,
  "owner": {
    "username": "kalytsynskyi",
    "profile_pic_url": "https://scontent.cdninstagram.com/v/t51.82787-19/631648810_...jpg",
    "is_verified": false,
    "id": "48010607833",
    "full_name": null,
    "is_mentionable": null,
    "is_private": null,
    "profile_pic_id": null,
    "latest_reel_media": null
  },
  "sentimentLabel": "positive",
  "sentimentScore": 0.9
}
```

**Fourteen keys, and the list is closed.** The first twelve are
`apify/instagram-comment-scraper`'s, under its names, in its order, with its
nulls — diffed key for key against a run of that Actor on 10 September 2026. The
last two are `sentimentLabel` and `sentimentScore`, which are the whole reason to
pick this Actor over that one. Nothing else is on the row, so an existing
pipeline reads these rows unchanged and a spreadsheet built on that Actor's
columns keeps working.

**Thirteen until 14 September 2026.** The fourteenth is `parentCommentUrl`, the
last column of the reference row this Actor did not write. It is null on every
row, and that is not a placeholder: only top-level comments are served to a
logged-out reader, so no comment collected here *has* a parent. Same argument as
`repliesCount` below — the column is theirs, the null is honest, and a consumer
switching over finds the field where they left it instead of losing it from an
export.

**A failed input writes a row too.** A URL that cannot be reached, or one this
Actor refuses by name, produces a row carrying `inputUrl`, `url`, `error`,
`errorDescription` and `requestErrorMessages` rather than disappearing — so fifty
URLs in is always fifty rows out, and which three of them died is a filter rather
than a diff. Error rows are not comment rows and are not charged as comments.

**`repliesCount` and `replies` are null here and null there.** Instagram renders
no comment replies to a logged-out reader and does not count them either,
measured on 217 comment nodes across three posts on 4 September 2026: the reply
count came back null on every single one. A `repliesCount: 0` would be an
invented measurement. `includeNestedComments` is refused with that explanation
rather than accepted and ignored.

**`ownerProfilePicUrl` and `owner/profile_pic_url` are signed URLs that expire**
in four to five days, in this Actor and in the reference one. They are a lease on
the image, not a copy of it: fetch them during the run or not at all.

**Earlier runs of this Actor wrote six more fields** — `postId`,
`postShortCode`, `postOwnerUsername`, `dataType`, `collectedAt` and `inputUrl` —
and they were removed on 10 September 2026 so that the row equals the reference
scraper's plus the sentiment pair. Everything they carried is still reachable:
the shortcode is in `postUrl`, the run's input and timing are in the run's own
log and input record, and a caller who needs Instagram's numeric media id can
read it off a post row's `id` in **Instagram Scraper**.

### Input example

The same input as `apify/instagram-comment-scraper`, unchanged — post URLs, comments per post, and the replies flag left off:

```json
{
  "directUrls": ["https://www.instagram.com/p/Dc6zTucHTv8"],
  "includeNestedComments": false,
  "resultsLimit": 500
}
```

That object is `apify/instagram-comment-scraper`'s own published example, pasted here unchanged and run as-is. Its form is three fields and this one is the same three — checked field for field against its published input schema on 15 September 2026, and pinned in `tests/test_actor_variants.py` so the day either side moves, the suite says so.

That is up to 500 comments from that one post, about $0.86. There is no `resultsType` to set and nothing else to fill in: this listing returns comments. An account instead of a post — `["https://www.instagram.com/nasa/"]` with `"resultsLimit": 100` — is 12 posts × up to 100 comments, at most 1,200 comments, about $2.07.

### Sentiment

Every comment carries `sentimentLabel` (`positive` / `neutral` / `negative`) and `sentimentScore` (0–1 confidence).

Scores describe the **collected sample**, not every comment that exists. `resultsLimit` is a cap per post, so a post with more comments than you asked for is sampled and any distribution you compute over it is a sample statistic — the run log names the post, what was asked for and what came back, rather than making you guess.

`sentimentLabel` can be `null`, and when it is, that is the honest answer rather than a failure to report. The classifier is asked in batches; a batch that comes back unreadable is asked a second time, and if the label still does not arrive the comment ships without one instead of with a guess. Comments are what you paid for and the label is derived from them, so a labelling problem never costs you the comment. In practice this is rare — 2,000 comments measured on 11 September 2026 came back with 2,000 labels — but code that reads this field should treat it as nullable, which is what the dataset schema declares.

### FAQ

**Can I get replies to comments?** No, and `includeNestedComments` is *refused* rather than ignored. Measured on 3 September 2026 across Instagram's own JavaScript bundles: the comment connection a logged-out visitor can read returns top-level comments and a reply count, and every query that would expand a thread is behind the login wall. Accepting the flag and returning top-level comments anyway would make a run look like it had walked the threads and found them empty.

**What about `isNewestComments`?** Accepted, and it does nothing. It is not on this form, since 15 September 2026 — a tickbox whose own label says it has no effect is not a control. Comments arrive in the order Instagram's own connection serves them and are not reordered here; the key is still accepted so an input copied from another Actor runs unchanged instead of failing on an unknown key.

**Why did a profile return nothing?** Private accounts expose their header and nothing else. The run reports it as a failed target with the reason rather than as an empty success.

**How fresh is it?** Answers come from a snapshot rather than a live call at read time, and the run log records that snapshot's age. `collectedAt` is not one of the fourteen keys: it describes the run rather than the comment, which is why it left the row on 10 September 2026.

**Is this legal?** Public data only. You are responsible for how you use it, including under GDPR where the comments concern people in the EU.

### Related

- **Instagram Scraper** — the same engine with profiles, posts and reels in front, at the same $1.75 per 1,000.
- **Instagram Profile Scraper** — handles in, one row per account with its posts nested, at the same $1.75 per 1,000.

# Changelog

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

# Actor input Schema

## `directUrls` (type: `array`):

The posts whose comments you want, one URL per line: https://www.instagram.com/p/Dc6zTucHTv8/, or the /reel/ and /tv/ spellings of the same thing. A profile URL or a bare handle (https://www.instagram.com/nasa/, nasa) also works and reads the comments under that account's 12 most recent posts. Exactly the three fields of apify/instagram-comment-scraper, so an input written for it runs here unchanged.

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

How many comments to take from each post, up to 5000. It is a promise: ask for 500 and a run returns 500, or every comment the post exposes when that is fewer. Comments are the slow part - roughly one per second per post - so this number is what sets the length of the run.

## `includeNestedComments` (type: `boolean`):

Accepted for compatibility with apify/instagram-comment-scraper and must stay OFF. There is no logged-out query that returns comment replies (measured 2026-09-03 across Instagram's own JavaScript bundles: the comment connection returns top-level comments and a reply count, and every query that would expand a thread is behind the login wall), so a run with this ON is refused by name rather than quietly returning top-level comments as if the threads had been walked and found empty.

## Actor input object example

```json
{
  "directUrls": [
    "https://www.instagram.com/p/Dc6zTucHTv8/"
  ],
  "resultsLimit": 100,
  "includeNestedComments": false
}
```

# Actor output Schema

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

One row per collected comment.

# 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 = {
    "directUrls": [
        "https://www.instagram.com/p/Dc6zTucHTv8/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("omaraw/instagram-comment-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 = { "directUrls": ["https://www.instagram.com/p/Dc6zTucHTv8/"] }

# Run the Actor and wait for it to finish
run = client.actor("omaraw/instagram-comment-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 '{
  "directUrls": [
    "https://www.instagram.com/p/Dc6zTucHTv8/"
  ]
}' |
apify call omaraw/instagram-comment-scraper --silent --output-dataset

```

## MCP server setup

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