# Threads Profile Scraper 🧵 Posts, Replies & Reposts (`gatherworks/threads-profile-scraper`) Actor

Scrape any Threads (threads.com) creator profile plus their posts, replies and reposts. Followers, bio, verification, links and engagement counts on every row. Paste handles or profile URLs. Meta Threads data as JSON, CSV or Excel. No login, no API key.

- **URL**: https://apify.com/gatherworks/threads-profile-scraper.md
- **Developed by:** [GatherWorks](https://apify.com/gatherworks) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.30 / 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?

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

## Threads Profile Scraper 🧵 Posts, Replies & Reposts

**Give it a Threads handle and get the creator's profile plus everything they
posted, replied to and reposted — as clean, sortable rows, all at one price.**

**No account, no cookies, no API key.** Paste usernames and run.

***

### 🔍 What is the Threads Profile Scraper?

Give it handles — `zuck`, `@zuck` or a full profile URL, all normalised for you.
For each creator it returns:

- 👤 the profile itself — followers, bio, bio link, verification, avatar
- 📝 their posts, with text, media and engagement counts
- 💬 their replies under other people's posts
- 🔄 their reposts

Every one of those is a row, and **every row costs exactly the same**.

### ⚡ What can it do?

- **👥 Take many creators at once** and return one dataset covering all of them.
- **🔀 Choose what you collect** — posts, replies, reposts, or the profile alone.
  Replies and reposts are extra sources you switch on, not an extra charge.
- **📅 Filter by date.** Keep only items inside a window you set.
- **🔢 Filter by engagement.** Set a minimum like count and ignore the rest.
- **🚧 Cap how far a filter searches**, so a narrow filter stops rather than
  running away with your budget.
- **🔎 Filter before you pay.** A row that does not match is never stored and
  never charged for.
- **📄 Export anywhere.** JSON, CSV, Excel — the dataset is yours.

#### 🎯 One charge, one price, every row

There is one billable event and one price. A profile row costs the same as a
post. Ask for ten creators and a thousand posts and the bill is one thousand and
ten rows at the same rate — no run fee, no minimum, no separate per-creator
charge, and nothing at all for a handle that turns out not to exist.

### ⚡ Speed and accuracy

**Speed.** Measured on a real run: **1 profile and 5 posts in 5 seconds**.

**Accuracy.** Handles are normalised, so `zuck`, `@zuck` and a full profile URL
all resolve to the same creator and are collected once. Every content row carries
`rowType` — `post`, `reply` or `repost` — so the three are never conflated.
Engagement figures are numbers, not display text. Timestamps are ISO 8601. A
handle that does not resolve is reported and skipped rather than returned empty.

### 📊 What data does it return?

**Profile rows** (`rowType: "profile"`):

| Field | Notes |
|---|---|
| 🏷️ `handle`, `fullName` | Handle and display name |
| 🔗 `profileUrl` | Direct link to the profile |
| 📝 `bioText` | The bio text |
| 🔢 `followersCount` | Audience size, as a number |
| ✅ `isVerified` | Whether the account carries a verified badge |
| 🚩 `isPrivate` | Whether the account's posts are publicly visible |
| 🔗 `externalUrl` | The link in the bio, if there is one |
| 🖼️ `avatarUrl` | Profile picture |
| 🆔 `profileId` | Stable account ID |

**Post, reply and repost rows** (`rowType: "post"`, `"reply"` or `"repost"`):

| Field | Notes |
|---|---|
| 📝 `postText`, `postTextLength` | The post text, and its length |
| 🔗 `postUrl` | Direct link to the post |
| 📅 `publishedAt` | When it was posted, ISO 8601 |
| 📊 `likesCount`, `repliesCount`, `repostsCount`, `quotesCount` | Engagement, as numbers |
| 🔢 `engagementTotal` | All four added up — one column to sort by |
| 🖼️ `imageUrls`, `videoUrls`, `mediaKind`, `hasMedia` | Media attached |
| 🔗 `linkUrl` | A link previewed in the post |
| 💬 `isReply`, `replyToUsername` | Whether it answers someone, and whom |
| 👤 `ownerUsername`, `ownerFullName`, `ownerIsVerified`, `ownerFollowersCount` | The author on the row |
| 🏷️ `sourceUsername` | Which creator you asked for this row |
| 🆔 `postId`, `postShortCode` | Stable post identifiers |
| 🌐 `languageCode` | Detected language, where the platform reports one |

#### 🚫 What it does **not** return

- **Anything from a private account.** Its posts are not publicly visible.
- **Follower lists.** Audience size is a number; the individual followers are not returned.
- **Following count, post count, or a linked Instagram handle.** Threads does not
  publish them, so they are not shipped as empty columns.
- **Deleted posts**, drafts, or direct messages.
- **Analytics only the account owner can see** — impressions, reach, profile views.

### 💰 How much does it cost?

You pay per row, for what the run actually produces — never for runtime, and
never on a subscription.

Standard rate: **💵 $0.0022 per row** — profile rows and post rows alike.

| Run | Rows | Cost |
|---|---|---|
| 🟩 10 creators, 100 posts each | 1,010 | 💵 $2.22 |
| 🟨 50 creators, 50 posts each | 2,550 | 💵 $5.61 |
| 🟧 100 creators, profiles only | 100 | 💵 $0.22 |

**A handle that does not resolve is free.** So is a row your filters remove.
**A month you do not run it costs nothing at all** — there is no plan to keep alive.

#### What you get for that

| | Included here |
|---|---|
| 👤 Full profile with every creator | ✅ same price as any other row |
| 🔀 Posts, replies and reposts distinguished by `rowType` | ✅ |
| 💬 Replies and 🔄 reposts switched on | ✅ more rows, same rate |
| 📊 Engagement counts as numbers, not display text | ✅ |
| 🔎 Date and engagement filters | ✅ free |
| 🏷️ Handle, @handle and profile URL all accepted | ✅ free |
| 💳 Minimum spend | none |

### 🚀 How do I use it?

1. Add one or more **usernames or profile URLs**.
2. Choose whether you also want **replies** and **reposts** — posts are on by
   default. Each one you switch on returns more rows at the same single rate.
3. Set **maximum items per creator** to control cost.
4. Optionally set a date or like-count filter.
5. Run, then export the dataset as JSON, CSV or Excel.

### ⚙️ Input

| Field | Default | Notes |
|---|---|---|
| 👤 `usernames` | — | Handles, @handles or profile URLs |
| 📝 `includePosts` | `true` | Collect their posts |
| 💬 `includeReplies` | `false` | Also collect replies they wrote under other posts — more rows, same rate |
| 🔄 `includeReposts` | `false` | Also collect posts they reposted — more rows, same rate |
| 🔢 `maxItemsPerUser` | `100` | How many items to return per creator |
| 📅 `postedAfter`, `postedBefore` | none | Keep only items inside this window |
| 🔢 `minLikes` | none | Keep only items with at least this many likes |
| 🚧 `maxScanned` | `2000` | How far a filter may search before stopping |

```json
{
  "usernames": ["zuck", "https://www.threads.com/@mosseri"],
  "includePosts": true,
  "includeReplies": false,
  "maxItemsPerUser": 100
}
```

### 📄 Output example

A real row, captured from a live run:

```json
[
  {
    "rowType": "profile",
    "handle": "zuck",
    "profileUrl": "https://www.threads.com/@zuck",
    "fullName": "Mark Zuckerberg",
    "bioText": "Mostly superintelligence and MMA takes",
    "followersCount": 5720420,
    "isVerified": true,
    "isPrivate": false,
    "externalUrl": null,
    "profileId": "63055343223"
  },
  {
    "rowType": "post",
    "sourceUsername": "zuck",
    "postUrl": "https://www.threads.com/@zuck/post/Db2wI-DilLt",
    "postText": "Today we're also opening the weights for Muse Glimmer, a great 30B parameter dense model that can run locally. Soon we'll also release the weights for Muse Spark 1.2, our latest foundation model. Meta is a strong supporter of open source and I'm proud of these releases. Congrats to @alexanddeer and the MSL team for all your great work on these models.",
    "publishedAt": "2026-08-10T10:01:59.000Z",
    "likesCount": 1897,
    "repliesCount": 505,
    "repostsCount": 83,
    "quotesCount": 0,
    "engagementTotal": 2485,
    "ownerUsername": "zuck",
    "ownerIsVerified": true,
    "mediaKind": "text",
    "hasMedia": false
  }
]
```

### ❓ Frequently asked questions

**Do I need a Threads or Instagram account?** No. No account, no cookies, no API key.

**What forms of username work?** `zuck`, `@zuck`, and
`https://www.threads.com/@zuck` all work and resolve to the same creator.

**Does the profile cost extra?** No. The profile row is billed at the same single
rate as every post row — there is no separate per-creator charge.

**Do replies and reposts cost more per row?** No. They are extra sources, not
extra charges. Switching them on returns more rows, each billed at the same
single rate.

**Can I collect only profiles, without posts?** Yes — switch all three content
options off and you pay for one row per creator.

**What about private accounts?** Their posts are not publicly visible, so nothing
is returned for them.

**Can I search by topic instead of by handle?** That is a different job — use the
Threads Creator Finder, which takes keywords.

### 🏷️ Pricing

| Event | Price | Covers |
|---|---|---|
| 🧵 `result` — **primary** | 💵 $0.0022 | Any row returned, profile or post, at one price |

Prices shown are the standard rate. Bronze, Silver and Gold accounts pay less:

| Tier | Per row |
|---|---|
| 🟦 Free | 💵 $0.0022 |
| 🟫 Bronze | 💵 $0.0019 |
| ⬜ Silver | 💵 $0.0016 |
| 🟨 Gold | 💵 $0.0013 |

#### 🛑 You are never billed past your limit

Set a maximum charge on the run and the Actor stops when it reaches it, with
every creator collected up to that point complete and saved. Filtered-out rows
are never stored and never billed.

# Actor input Schema

## `usernames` (type: `array`):

One or more Threads handles. Any of these forms work and are normalised for you: <code>zuck</code>, <code>@zuck</code>, or <code>https://www.threads.com/@zuck</code>.

## `includePosts` (type: `boolean`):

Collect the posts each creator published. On by default — this is what most runs are for.

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

Collect replies the creator wrote under other people's posts. Returns more rows, each billed at the same single rate.

## `includeReposts` (type: `boolean`):

Collect posts the creator reposted. Returns more rows, each billed at the same single rate.

## `maxItemsPerUser` (type: `integer`):

Caps how many posts, replies and reposts are returned for each creator. Cost scales linearly with this number.

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

Keep only items published on or after this date.

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

Keep only items published on or before this date.

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

Keep only items with at least this many likes. Leave empty for no minimum.

## `maxScanned` (type: `integer`):

Safety cap on how many items are examined while filtering. A narrow filter can walk a long way for few matches; this stops the run rather than letting it grow.

## Actor input object example

```json
{
  "usernames": [
    "zuck",
    "mosseri"
  ],
  "includePosts": true,
  "includeReplies": false,
  "includeReposts": false,
  "maxItemsPerUser": 100,
  "maxScanned": 2000
}
```

# Actor output Schema

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

One row per creator profile, plus a row for each post, reply or repost collected.

# 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 = {
    "usernames": [
        "zuck",
        "mosseri"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gatherworks/threads-profile-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 = { "usernames": [
        "zuck",
        "mosseri",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("gatherworks/threads-profile-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 '{
  "usernames": [
    "zuck",
    "mosseri"
  ]
}' |
apify call gatherworks/threads-profile-scraper --silent --output-dataset

```

## MCP server setup

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