# Threads Reply Tree Scraper - Nested Comments & Replies (`hypebridge/threads-scraper`) Actor

Scrape Threads posts with their full nested reply trees, the author's own follow-up posts, and recent posts from profiles. Plain HTTP, no login.

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

## Pricing

from $1.00 / 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

### What does Threads Reply Tree Scraper do?

**Threads Reply Tree Scraper** extracts a [Threads](https://www.threads.com) post and its complete conversation: every reply, every reply to a reply, down to ten levels, plus the follow-up posts the author added to their own thread. No official API key, no login, no browser. Each reply comes back as a flat record with its parent and depth, so the whole tree can be rebuilt exactly.

Give it a profile link instead and it lists the profile's recent posts and, if you want, fetches the reply tree under each one.

It only reads what a logged-out visitor can see. It does not log in, post, follow, or touch private accounts.

### Why scrape Threads reply trees?

- **Sentiment and brand monitoring**: pull every reaction to an announcement, launch, or controversy and analyse it with your own model.
- **Conversation datasets for AI**: reply trees come as parent/child edges, ready for LLM context windows, graph tools, or fine-tuning sets.
- **Community and customer research**: see what people actually say when a creator, brand, or founder posts, in their own words and languages.
- **Engagement analysis**: find which replies carried the discussion and who started the sub-threads.
- **Archiving**: keep a timestamped copy of a public discussion before it is edited or deleted.

### What data can Threads Reply Tree Scraper extract?

| Field | Type | Description |
|-------|------|-------------|
| `dataType` | String | `POST` for a post, `REPLY` for a reply, `PROFILE` for a profile |
| `id` / `code` | String | Numeric post id and the short code used in the URL |
| `url` | String | Canonical `threads.com` link to the post or reply |
| `username` | String | Handle of the author |
| `text` | String | Post text (empty for media-only posts) |
| `createdAt` / `timestamp` | String / Integer | Publish time as ISO-8601 UTC and as Unix seconds |
| `likeCount`, `replyCount`, `repostCount`, `quoteCount` | Integer | Engagement counts |
| `replyDepth` | Integer | `0` for the post, `1` for direct replies, `2` and deeper for nested replies |
| `parentPostId` / `rootPostId` | String | The post this one answers, and the post at the top of the tree |
| `replyToUsername` | String | Handle being replied to |
| `author` | Object | Author id, username, full name, verified and private flags, profile picture |
| `media` / `mediaCount` | Array / Integer | Images and videos with URL and size |
| `quotedPost` | Object | The post this one quotes, when there is one |
| `mentions` | Array | `@handles` mentioned in the text |
| `hasUnavailableReplies` | Boolean | On the top post: Threads says some replies are hidden from logged-out visitors |
| `language`, `replyControl`, `isEdited`, `likedByAuthor`, `isReply`, `topicTag` | Mixed | Detected language, who may reply, edit flag, author-liked flag, reply flag, and topic tag when present |

Profile rows add `fullName`, `biography`, `followerCount`, `isVerified`, `isPrivate`, `profilePicUrl` and `bioLinks`. Every record also carries `sourceInput` (the link you supplied) and `scrapedAt`.

### How to scrape a Threads reply tree

1. [Create a free Apify account](https://apify.com) if you don't have one.
2. Open **Threads Reply Tree Scraper** and click **Try for free**.
3. Paste Threads post links into **Threads post or profile links**.
4. Set **Max items per URL** to cap how many records each link returns.
5. Click **Start** and download results from the **Dataset** tab as JSON, CSV, Excel, or HTML.

#### Supported links

- Post links: `https://www.threads.com/@username/post/CODE`, the older `threads.net` domain, short links `/t/CODE` and `/post/CODE`, embed links ending in `/embed`, and links with tracking parameters such as `?xmt=` or `?igshid=`
- Profile links: `https://www.threads.com/@username`

Search, hashtag, home feed and profile tab links (`/replies`, `/media`, `/reposts`) are not supported. If any of them is in the input, **the run stops before fetching anything** and tells you which links to remove. Logged-out Threads returns unrelated posts for search and hashtags, and only a handful of posts for the profile tabs, so supporting them would hand you data that does not match what you asked for.

Duplicate links to the same post or profile are fetched once.

### Profile links: recent posts and their reply trees

A profile link returns the profile and its most recent posts. With **Get post details** on (the default), each post's reply tree is fetched too, so one profile link can give you the conversation under every recent post.

**What you get is the recent slice that Threads shows a logged-out visitor, not a full history.** Threads fails on certain posts regardless of who asks, so the listing stops at the first post it cannot read. In testing that was somewhere between about 40 and 250 or more posts depending on the profile. Use **Max posts per profile** to ask for fewer. The run summary records how many posts were listed and why the listing stopped.

### How much will it cost?

Pay-per-event pricing: you are charged for each record delivered, and the rate depends on what the record is. There is no start fee and no charge for compute.

| Record | What it is | Price per record |
|--------|------------|------------------|
| Reply | A direct reply to a post | $0.0015 |
| Nested reply | A reply to a reply, any depth | $0.0018 |
| Post | The post behind a link you supplied, or a profile post fetched with its reply tree | $0.0025 |
| Author follow-up post | A post the author added to their own thread | $0.0025 |
| Profile | A profile record | $0.004 |
| Listed post | A profile post listed without its reply tree | $0.002 |

Paid Apify plans get lower rates. Examples at the standard rate:

- 1,000 direct replies to one post: about $1.50
- A post with 200 direct replies and 100 nested replies: about $0.0025 + $0.30 + $0.18 = $0.48
- A profile, its 20 recent posts, and 25 replies under each: about $0.004 + $0.05 + $0.75 = $0.80
- A profile and its 20 recent posts only, no reply trees: about $0.004 + $0.04 = $0.044

Use **Max items per URL**, **Max posts per profile** and **Max replies per profile post** to cap spend.

### Input

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `startUrls` | Array | required | Threads post or profile links (up to 100) |
| `maxItems` | Integer | 100 | Records per link, counting everything (1 to 5,000) |
| `getPostDetails` | Boolean | true | For profile links, fetch each post's reply tree |
| `maxReplyDepth` | Integer | 5 | `0` post only, `1` direct replies, up to `10` levels of nesting |
| `maxPostsPerProfile` | Integer | 20 | Recent posts to take from a profile |
| `maxRepliesPerPost` | Integer | 25 | Replies to collect under each profile post |
| `repliesSince` | String | none | Only replies on or after this date, e.g. `2026-09-01` or `7 days` |
| `maxConcurrency` | Integer | 5 | Links processed in parallel (1 to 10) |
| `debugMode` | Boolean | false | Keep the raw body of failed requests (max 10 files) |

#### Example: a post and its first 200 replies

```json
{
  "startUrls": [{ "url": "https://www.threads.com/@zuck/post/Dd1MqfcG0aH" }],
  "maxItems": 200,
  "maxReplyDepth": 1
}
```

#### Example: a deep conversation tree

```json
{
  "startUrls": [{ "url": "https://www.threads.com/@zuck/post/Dd1MqfcG0aH" }],
  "maxItems": 2000,
  "maxReplyDepth": 8
}
```

#### Example: the reply trees under a brand's recent posts

```json
{
  "startUrls": [{ "url": "https://www.threads.com/@natgeo" }],
  "maxPostsPerProfile": 10,
  "maxRepliesPerPost": 50,
  "maxItems": 1000
}
```

#### Example: only the new replies since last week

```json
{
  "startUrls": [{ "url": "https://www.threads.com/@zuck/post/Dd1MqfcG0aH" }],
  "repliesSince": "7 days",
  "maxItems": 500
}
```

### Output

Everything is saved to one dataset in the **Dataset** tab. Use `dataType` to filter `PROFILE`, `POST` and `REPLY` rows in your own tooling. The Output tab also links to two ready-made views: **Reply tree** (depth first, with who replied to whom, plus likes, replies, reposts and quotes, ready for CSV or Excel export) and **Profiles**.

A **SUMMARY** record in the key-value store lists, per link, the status, record counts, why a run stopped early (`maxItems`, `maxReplyDepth`, or a safety limit), the replies Threads reports against the replies returned, and a count of any failures met on the way by type, so a 2,400-reply post that returned 1,200 rows is never a mystery.

#### Sample output

```json
{
  "dataType": "REPLY",
  "id": "3996164469004254295",
  "url": "https://www.threads.com/@pie_3.1415926._/post/Dd1Oml3CTxX",
  "username": "pie_3.1415926._",
  "surface": "POST_DETAIL",
  "sourceInput": "https://www.threads.com/@zuck/post/Dd1MqfcG0aH",
  "scrapedAt": "2026-10-02T15:40:46.770Z",
  "code": "Dd1Oml3CTxX",
  "text": "他頭貼變黑白我還以為他掛了",
  "createdAt": "2026-09-28T12:52:29.000Z",
  "timestamp": 1790599949,
  "likeCount": 56,
  "replyCount": 1,
  "repostCount": 2,
  "quoteCount": 0,
  "language": "zh_TW",
  "isEdited": false,
  "replyControl": "everyone",
  "likedByAuthor": false,
  "replyDepth": 1,
  "parentPostId": "3996155940894885511",
  "rootPostId": "3996155940894885511",
  "replyToUsername": "zuck",
  "author": { "id": "72703007294", "username": "pie_3.1415926._", "fullName": "圓周率", "isVerified": false, "isPrivate": false, "profilePicUrl": "https://..." },
  "media": null,
  "mediaCount": 0,
  "mentions": [],
  "topicTag": null,
  "isReply": true,
  "hasUnavailableReplies": null,
  "quotedPost": null,
  "enrichError": false
}
```

### Tips

- Replies are collected breadth first, so a low **Max items per URL** returns the top-level replies before any nested ones.
- When the author continues their own thread with follow-up posts, those come first as `REPLY` rows at depth 1, followed by everyone else's replies.
- A post's `replyCount` is usually higher than the number of rows you get back: Threads hides some replies from logged-out visitors. The top post carries `hasUnavailableReplies` when Threads says so.
- With **Replies since**, older replies are not returned or charged, but their nested replies are still checked, so a new reply under an old one is found. Its `parentPostId` may then point at a reply that is not in the output.
- To rebuild the conversation, join `parentPostId` to `id`. The **Reply tree** view is already shaped this way, with the depth first.
- Start with a small `maxItems` to check the data, then raise it.

### FAQ

#### Is scraping Threads legal?

Threads Reply Tree Scraper only extracts publicly available data. Always check the site's Terms of Service and make sure you have a legitimate purpose.

#### Do I need a Threads account or API key?

No. The Actor reads public posts the way a logged-out visitor does.

#### Why did a link return zero rows?

The post or profile was deleted, belongs to a private account, or is not visible to logged-out visitors. The run still succeeds, and the link is listed as `unavailable` in the SUMMARY record.

#### Why did my run stop immediately?

One of your links is a search, hashtag, home feed or profile tab link, which are not supported. The error message names each one. Remove them and run again.

#### Can it scrape search results or hashtags?

No. Logged-out Threads returns unrelated posts for search and hashtag pages, so the results would not match your query.

### Support

- Bugs and feature requests: **Issues** tab
- Programmatic access: **API** tab

> **Disclaimer**: Our Actors are ethical and do not extract any private user data, such as email addresses, gender, or location. They only extract what the user has chosen to share publicly. We therefore believe that our Actors, when used for ethical purposes by Apify users, are safe. However, you should be aware that your results could contain personal data. Personal data is protected by the GDPR in the European Union and by other regulations around the world. You should not scrape personal data unless you have a legitimate reason to do so. If you're unsure whether your reason is legitimate, consult your lawyers.

# Actor input Schema

## `startUrls` (type: `array`):

Public Threads post links (threads.com or threads.net, including /t/ short links) or profile links like threads.com/@username. A post link returns the post and its full reply tree. A profile link returns the profile and its recent posts. Search, hashtag, home feed and profile tab links are not supported and stop the run.

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

Maximum records returned per link, counting everything: the post, its replies, and for a profile link its posts and their replies. Replies are collected breadth-first, so a low limit returns top-level replies first.

## `getPostDetails` (type: `boolean`):

For profile links: fetch every listed post's own page and its reply tree. Turn off to get only the profile and a plain list of its recent posts. Post links always return the full tree.

## `maxReplyDepth` (type: `integer`):

How many levels of replies to follow. 0 returns only the post, 1 returns direct replies, 5 follows nested conversations five levels deep. Profile posts need at least 1 to get reply trees.

## `maxPostsPerProfile` (type: `integer`):

For profile links: how many recent posts to take. A post and the author's own follow-ups count once. Threads shows logged-out visitors a limited, uneven slice of a profile, typically tens to a few hundred recent posts, so a high value may not be reached.

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

For profile links with post details: the most replies to collect under each post, so one popular post cannot use the whole budget.

## `repliesSince` (type: `string`):

Only return replies posted on or after this date (UTC), for example 2026-09-01 or 7 days. The post itself is always returned. Older replies are not returned or charged, but their nested replies are still checked.

## `maxConcurrency` (type: `integer`):

Links processed in parallel. Keep low; Threads rate-limits aggressive clients.

## `debugMode` (type: `boolean`):

Save raw responses for failed requests to the key-value store (capped at 10 files).

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.threads.com/@zuck/post/Dd1MqfcG0aH"
    }
  ],
  "maxItems": 100,
  "getPostDetails": true,
  "maxReplyDepth": 5,
  "maxPostsPerProfile": 20,
  "maxRepliesPerPost": 25,
  "maxConcurrency": 5,
  "debugMode": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

Every record with all fields.

## `replyTree` (type: `string`):

Depth first, with who replied to whom and engagement counts.

## `profiles` (type: `string`):

Profile records for profile links.

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

Per-input status, counts and truncation reasons.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.threads.com/@zuck/post/Dd1MqfcG0aH"
        }
    ],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("hypebridge/threads-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 = {
    "startUrls": [{ "url": "https://www.threads.com/@zuck/post/Dd1MqfcG0aH" }],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("hypebridge/threads-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 '{
  "startUrls": [
    {
      "url": "https://www.threads.com/@zuck/post/Dd1MqfcG0aH"
    }
  ],
  "maxItems": 100
}' |
apify call hypebridge/threads-scraper --silent --output-dataset

```

## MCP server setup

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