# Threads Replies Scraper: Comments of Any Post (`automation_craft/threads-replies-scraper`) Actor

Scrape the replies of any Threads post without a login: every reply Threads serves to a logged out visitor, page by page, plus the replies of replies to the depth you choose. Text, author, time, likes, media, depth and parent on each row. JSON, CSV, Excel, API. Pay per reply.

- **URL**: https://apify.com/automation_craft/threads-replies-scraper.md
- **Developed by:** [Automation Craft](https://apify.com/automation_craft) (community)
- **Categories:** Social media, Marketing, AI
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.06 / 1,000 replies

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

### Threads Replies Scraper: Comments of Any Post

**Scrape the replies of any Threads post** (threads.com, Meta's text app) without a login: give post URLs and get every reply Threads serves to a logged out visitor, page by page, plus the replies of those replies to the depth you choose. One row per reply with the text, the author, the time, likes, its own reply count, media, its depth in the conversation and the reply it answers, and one row for the post itself with its view count. Pay per reply, from $0.06 per 1,000.

Built for comment analysis and sentiment research, community and brand monitoring, giveaway and contest checks, and AI data sets of real conversations. Name a memory and every later run delivers and charges only the replies it has not delivered before. Rows carry what Threads shows publicly about each reply's author (handle, display name, id, profile picture); use them under the rules that apply to you.

### Quick start

1. Open the Actor and paste one or more Threads post URLs into **Post URLs** (`https://www.threads.com/@user/post/CODE`, the short `/t/CODE` form, threads.net links or a bare post code all work).
2. Set **Replies per post** (100 when left out; the Console form starts at 20) and, if you like, the **Order of replies** (oldest first, the order Threads shows them, or newest first) and the **Reply depth** (2 by default: the replies and the replies of those replies).
3. Run. Rows land in the dataset as JSON, CSV, Excel or through the API; a free coverage row per post says how many replies Threads shows, how many were read, delivered and charged, and why the walk stopped.
4. For a monitor: give a **Memory name** and schedule the run; each reply comes once. With a memory the run reads the newest replies first at depth 1 (unless you set them), so new replies come first; it then reads on through replies it already delivered, free, within the work allowance (about 1,000 known direct replies a run), so on a busier post a run ends with `stoppedBy: "work_allowance"` after its new replies.

### What you get

One `reply` row per reply, charged once per run, and one `post` row per post you gave (once per run day). Fill rates measured on 1,198 distinct replies of 4 posts delivered by the real actor's test matrix of 2026-10-08 (build 0.1.1, `platform-results/fill-rates-r1.json`):

| Field | What it is | Filled |
|---|---|---|
| `code`, `url`, `postId` | the reply's short code, its own page and its numeric id | 100% |
| `text` | the full text of the reply | 94.6% (the rest are a sticker, an image or a GIF only) |
| `postedAt`, `postedAtUnix` | when it was posted (UTC) | 100% |
| `authorUsername`, `authorId`, `authorVerified`, `authorProfilePic`, `authorUrl` | the reply's author | 100% (7.5% verified) |
| `authorFullName` | the author's display name | 95.2% |
| `likeCount`, `replyCount`, `repostCount`, `quoteCount` | engagement at the time of reading; `replyCount` is the reply's own replies | 100% |
| `depth` | 1 for a direct reply to the post, 2 for a reply to a reply, and so on | 100% (81.2% at depth 1, 15.9% at depth 2, 2.9% deeper) |
| `parentPostCode`, `replyToUsername` | the post or reply this reply answers, and its author | 100% |
| `rootPostCode`, `rootPostUrl`, `rootPostAuthorUsername` | the post you gave | 100% |
| `isPostAuthor`, `likedByPostAuthor`, `authorThread`, `pinned` | written by the post's author, liked by the post's author (as Threads shows it), part of the author's own continuation of the post, a reply the author pinned | always set; 0.5% by the post author, 1.6% liked by the post author |
| `language`, `languageTag`, `languageSource`, `languageUnknown` | the language as an ISO code: Threads' own label when it has one (Chinese, Japanese and Korean), else a statistical classification of texts of 80 characters or more (`languageSource: "text"`); unknown and kept when neither gives one | 34.9% (0.6% Threads' own label, 34.3% from the text); replies are short, so most stay unknown |
| `mediaType`, `hasMedia`, `imageUrl`, `images`, `videos`, `carousel` | text, image, video or carousel, with the media URLs (they expire after some days) | 5.5% carry media |
| `linkAttachment` | the link card (url, title, image) | 2.2% |
| `isQuote`, `quotedPost`, `repostedPost` | a quoted or reposted post inside the reply | when present (0.3%) |

The `post` row carries the same fields for the post you gave plus `viewCount`, the view count its page shows (4 of 4 posts measured), and `lookupDay`.

Free rows next to the replies: one `coverage` row per post (`replyCountShown`, the count Threads shows on the post; `repliesRead`, `topLevelRead`, `repliesDelivered`, `repliesCharged`, `replyCalls`, `threadsExpanded`, `threadsCut`, filtered, repeated and known counts, `stopReason`, `walkEnded`), a `status` row for a post that does not exist, an unusable value or posts a stopped run did not start, and one `summary` row with every counter and what was charged.

#### How deep it goes

A Threads post page shows at most about 10 replies to a visitor who is not logged in. This Actor reads past it: it asks Threads for the next replies the way Threads' own page does, about 90 direct replies per call, until Threads serves no more, and with **Reply depth** 2 or more it walks the replies of every reply that has more replies than were shown under it. Measured on 2026-10-08 on posts read to the end:

| Post | Replies Threads shows | Replies read (direct and nested) | Calls after the page |
|---|---|---|---|
| a NASA post | 95 | 73 at depth 1, 89 at depth 2 | 1 at depth 1, 5 at depth 2 |
| a Duolingo post | 185 | 146 | 2 |
| a Zuckerberg post | 518 | 372 | 5 |
| a Zuckerberg post | 2,562 | 1,000 in 62 seconds (a run's cap, still going) | 24 |

Logged out, Threads stops serving replies before the count it shows: on the four posts the spike walked to the end, the direct replies it served were 51 to 74 percent of that count; counting the nested replies too, the rows read on the three posts of the table read to their end were 72 to 94 percent of it (372 of 518, 146 of 185, 89 of 95). The rest needs a login, which this Actor never uses. So no post is ever reported as complete: the coverage row gives the count Threads shows next to the replies read, and `walkEnded: true` when the walk read until Threads served no more.

### How much does it cost to scrape Threads replies?

Pay per event. Prices per 1,000 events (USD):

| Event | Free plan | Bronze | Silver | Gold |
|---|---|---|---|---|
| Reply (one reply row) | 0.09 | 0.08 | 0.07 | 0.06 |
| Post (the post you gave, looked up, once per run day) | 2.00 | 2.00 | 2.00 | 2.00 |
| Actor start (once per run, per GB of memory; runs use 256 MB, so one) | 0.007 per run | 0.007 per run | 0.007 per run | 0.007 per run |

Platinum and Diamond plans pay the Gold price. There is no discount on the Actor start event.

The Store pricing card shows these same prices per 1,000 events: "$2.00 / 1,000" on the Post row means one post costs 0.2 cents, and "$0.06 / 1,000" on the Reply row means one reply costs 0.006 cents.

Worked examples on the Free plan: one post with 20 replies costs 0.007 + 0.002 + 20 x 0.00009 = $0.0108; one post with 1,000 replies costs 0.007 + 0.002 + 0.09 = $0.099; 10 posts with 100 replies each cost 0.007 + 0.02 + 0.09 = $0.117.

Free: a post that does not exist or could not be read, filtered replies, a reply already delivered in the run, replies a named memory knows, the coverage, status and summary rows, and every row of a run that cannot bill safely (a memory that could not be read or saved, a run the platform restarted).

#### The work allowance

Work that may end without a charge (a post that does not exist, a page of replies that all fail your filters or that the memory already knows) is bounded per run: 40 seconds of such unpaid work, plus 6 seconds for each charged post and 1 second for each charged reply. When it is spent the run stops, says so in the summary (`stoppedBy: "work_allowance"`) and lists the posts it did not start in a free status row. Calls that deliver charged replies never use it up.

### Input

| Field | Type | What it does |
|---|---|---|
| `postUrls` | list | Threads post URLs (threads.com or threads.net, `/@user/post/CODE`, `/t/CODE`, `/post/CODE`) or bare post codes; a reply's URL works too (its own replies are read). Up to 200 per run. Codes are case sensitive. |
| `maxRepliesPerPost` | integer | Replies to keep per post, nested ones included (default 100, up to 5,000; 0 delivers the post row only). |
| `replySort` | `oldest_first` or `newest_first` | Threads' default order (the first replies first) or the newest first (`top` and `recent` are accepted too). Default `oldest_first`; `newest_first` with a memory name. |
| `replyDepth` | `1` to `6` | 1: direct replies and the nested replies Threads shows inside them; 2: also the replies of every reply that has more than were shown; up to 6 levels. Default `2`; `1` with a memory name. |
| `postedAfter`, `postedBefore` | text | Keep replies in a date range (UTC): `YYYY-MM-DD`, `YYYY-MM-DDTHH:MM`, `today`, `yesterday`, `7 days`, `6 hours`. Replies outside are free. With `oldest_first` the walk stops at the first page of direct replies newer than `postedBefore`. |
| `minLikes` | integer | Keep replies with at least this many likes. |
| `excludePostAuthor` | boolean | Leave out the post author's own replies. |
| `onlyWithMedia` | boolean | Keep replies with an image, a video or a carousel. |
| `languages`, `dropUnknownLanguage` | list, boolean | Keep replies in these languages (en, es, ja); replies with no detected language are kept unless `dropUnknownLanguage` is on. |
| `maxItems` | integer | A cap of post and reply rows for the whole run (no cap when left out). |
| `memoryName`, `resetMemory` | text, boolean | Pay once per reply across runs: a named memory this Actor keeps in your account; each run reads the conversation again (newest first), charges only replies it has not delivered before, and reads known replies free within the work allowance (about 1,000 known direct replies a run). |
| `proxyConfiguration` | object | Apify datacenter proxy. Threads answers it fully, so a residential or other Apify proxy group is not used (the run says so in its summary); your own proxy URLs are used as given. |

#### API examples

```bash
curl -X POST "https://api.apify.com/v2/acts/automation_craft~threads-replies-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"postUrls": ["https://www.threads.com/@zuck/post/Dd1MqfcG0aH"], "maxRepliesPerPost": 200}'
```

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation_craft/threads-replies-scraper').call({
    postUrls: ['https://www.threads.com/@nasa/post/DeKza99kXKb'],
    replySort: 'newest_first',
    maxRepliesPerPost: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((x) => x.type === 'reply').length, 'replies');
```

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_TOKEN")
run = client.actor("automation_craft/threads-replies-scraper").call(run_input={
    "postUrls": ["https://www.threads.com/@duolingo/post/DUi82igCcv4"],
    "replyDepth": "3",
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["type"] == "reply":
        print(item["depth"], item["authorUsername"], item["text"])
```

### What this Actor does NOT do

- It does not read the replies Threads hides from visitors who are not logged in: on the posts measured a walk ends at about half to three quarters of the reply count Threads shows. It never logs in and never asks for your cookies or a Threads account.
- It does not read the quotes or the reposts of a post: Threads serves no list of them to a logged out visitor.
- It does not sort replies by likes or by Threads' ranking: it reads Threads' default order (oldest first) or the newest first.
- It does not read profiles, searches, hashtags or followers lists: give it post URLs. Followers lists need a login.
- It does not use residential proxies: Threads answers the Apify datacenter pool, and a residential group in the input is replaced by it (the summary says so).
- It does not report a post's replies as complete, and it does not promise a number of replies per post.
- It does not give view counts for replies; Threads shows a view count on a post's own page only, delivered on the post row.

### FAQ

#### How many replies can it read from one post?

Every reply Threads serves to a logged out visitor, about 90 per call, page by page until Threads serves no more. On the posts measured the direct replies served were 51 to 74 percent of the reply count Threads shows, and with the nested replies 72 to 94 percent of it was read. The coverage row of each post gives the count Threads shows next to the replies read.

#### Can I see all replies on a Threads post without logging in?

Not all: logged out, Threads stops serving replies at about half to three quarters of the count it shows. This Actor reads all of what it serves and never logs in or asks for cookies, and it never reports a post as complete.

#### How do I export Threads comments to CSV or Excel?

Paste the post URLs into `postUrls` and run. Each reply is one row, so the dataset downloads as CSV, Excel or JSON from the run page or through the API, with the author, text, time, likes, depth and parent as columns.

#### Does it get the replies to replies?

Yes. Rows carry `depth` (1 for a direct reply) and `parentPostCode`. `replyDepth` 2, the default, also walks the replies of every reply that has more replies than were shown under it; up to 6 levels.

#### Why does this Actor run with limited permissions?

Least privilege: it only needs its own storages and the named memory store it creates itself, so it asks for nothing else in your account.

### More data tools by Automation Craft

- [Threads Scraper: Posts, Search, Tags, Replies](https://apify.com/automation_craft/threads-scraper)
- [TikTok Profile Scraper: Exact Stats, No Login](https://apify.com/automation_craft/tiktok-profile-scraper)
- [Telegram Channel Scraper: Posts, Views, Dates](https://apify.com/automation_craft/telegram-channel-scraper)
- [Substack Scraper: Posts, Notes and Profiles](https://apify.com/automation_craft/substack-scraper)
- [Instagram Ads Library Scraper - Creatives, Video](https://apify.com/automation_craft/instagram-ads-library-scraper)
- [Meta Ad Library Scraper - All Placements, Filters](https://apify.com/automation_craft/meta-ads-library-scraper)

### Changelog

- 0.1 (2026-10-08): first release. Replies of Threads posts walked page by page without a login, about 90 per call, the replies of replies to depth 6, oldest or newest first, date, likes, media, language and post author filters, a cross run memory, a coverage row per post, free rows for misses.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation_craft/threads-replies-scraper/changelog.md

# Actor input Schema

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

Threads posts whose replies you want, one per line: a post URL (threads.com or threads.net, /@user/post/CODE or /t/CODE) or a bare post code. A reply's URL works too (its own replies are read). Up to 200 per run. A post that does not exist gets a free status row.

## `maxRepliesPerPost` (type: `integer`):

Replies to keep per post, nested replies included; the walk of that post stops when it is reached. Default 100 when left out (the Console form starts at 20), at most 5,000. 0 delivers the post row only.

## `replySort` (type: `string`):

oldest_first: the order Threads shows replies in, the first replies first. newest_first: the newest replies first; with a cap this keeps the latest replies. Default oldest_first, and newest_first when a memory name is given. top and recent (Threads' own names) are accepted too.

## `replyDepth` (type: `string`):

1: the post's direct replies, with the nested replies Threads shows inside them. 2: also walks the replies of every reply that has more replies than were shown under it. 3 to 6: that many levels. Every row carries its depth and its parent. Default 2, and 1 when a memory name is given.

## `postedAfter` (type: `string`):

Keep replies from this moment on (UTC): YYYY-MM-DD, YYYY-MM-DDTHH:MM, today, yesterday, "7 days", "2 weeks" or "6 hours" (counted back from the run's start). Older replies are read and left out free (a new reply can sit under an old one, so the walk reads on).

## `postedBefore` (type: `string`):

Keep replies up to this moment (UTC): YYYY-MM-DD (the end of that day), YYYY-MM-DDTHH:MM, today, yesterday or "N hours". With oldest_first the walk stops at the first page of direct replies newer than this (every reply under them is newer still).

## `minLikes` (type: `integer`):

Keep only replies with at least this many likes. Default 0.

## `excludePostAuthor` (type: `boolean`):

Drop replies written by the author of the post (including the author's own continuation of the post). Off by default; every row carries isPostAuthor.

## `onlyWithMedia` (type: `boolean`):

Keep only replies with an image, a video or a carousel. Off by default.

## `languages` (type: `array`):

Keep only replies whose language is one of these codes: en, es, ja, pt. Threads itself labels only Chinese, Japanese and Korean posts; every other reply is classified from its text here (languageSource says which; texts under 80 characters stay unknown). Unknown replies are KEPT and marked languageUnknown true unless dropUnknownLanguage is on.

## `dropUnknownLanguage` (type: `boolean`):

With a languages list: drop replies that carry no language value instead of keeping them as unknown. Off by default so nothing is dropped silently.

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

The cap of post and reply rows for the whole run, across every post. No cap when left out (Replies per post and your charge limit bound the run), at most 100,000.

## `memoryName` (type: `string`):

Give a name and every later run with the same name delivers and charges only replies it has not delivered before: the way to monitor a post for new replies on a schedule. With a memory the defaults are newest_first and depth 1, so new replies come on the first pages; each run then reads on through replies it already delivered (free) within the work allowance, about 1,000 known direct replies a run, so on a busier post a run ends with stoppedBy work_allowance after its new replies. The post row is delivered on each run day with its fresh counts. The memory is a named key-value store this Actor creates in your account. Leave empty for a one off run.

## `resetMemory` (type: `boolean`):

Forget every reply the named memory holds before this run, so every reply counts as new again (and is charged again).

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

Apify datacenter proxy (one exit address at a time; a run moves to a fresh exit when Threads asks it to wait, up to 4 times). Threads answers it fully, so a residential or other Apify proxy group is not used and the run says so; your own proxy URLs are used as given.

## Actor input object example

```json
{
  "postUrls": [
    "https://www.threads.com/@zuck/post/Dd1MqfcG0aH"
  ],
  "maxRepliesPerPost": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (type: `string`):

No description

# 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.threads.com/@zuck/post/Dd1MqfcG0aH"
    ],
    "maxRepliesPerPost": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation_craft/threads-replies-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.threads.com/@zuck/post/Dd1MqfcG0aH"],
    "maxRepliesPerPost": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("automation_craft/threads-replies-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.threads.com/@zuck/post/Dd1MqfcG0aH"
  ],
  "maxRepliesPerPost": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call automation_craft/threads-replies-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation_craft/threads-replies-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/0CdqOrnMAesULJbPk/builds/P96c0v5YWY8AN2YGC/openapi.json
