# Threads Scraper — Posts, Replies, Profiles & Search (`innolabs/threads-scraper`) Actor

Threads scraper API: posts, replies and profiles from Threads (Meta) by username, post URL or keyword search, with likes, reposts, replies, media and follower counts. No login. JSON/CSV/Excel.

- **URL**: https://apify.com/innolabs/threads-scraper.md
- **Developed by:** [Tristan Leroux](https://apify.com/innolabs) (community)
- **Categories:** Social media, Marketing
- **Features:** Limited permissions
- **Stats:** 20 total users, 6 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

Pay per event

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 Scraper

Scrape **Threads (Meta) posts, replies and profiles** by username, post URL or keyword
search: post text, created date, likes, replies, reposts, quotes and shares, images and
videos with their sizes, links and link previews, mentions, hashtags and topic tags,
author details, plus profile records with bio, follower count, verified badge and
profile picture. No login, no Threads or Instagram account, no API key. Export as JSON,
CSV or Excel, or pull the data through the API.

### What does Threads Scraper do?

Threads Scraper reads the public Threads website the way a logged-out visitor sees it and
returns clean records:

- **Profiles by username** — enter `zuck`, `@mosseri` or a profile link and get one
  profile record (name, bio, follower count, verified badge, profile picture, bio links,
  profile topics) plus the account's posts, newest first, paged back through its history.
- **Post URLs** — paste a post link and get the post with all its counters and media,
  followed by its replies (ranked the way Threads shows them), page after page.
- **Keyword search** — enter keywords or hashtags such as `coffee` or `AI agents` and get
  the posts Threads shows for them in the **Top** or **Recent** tab, or both.
- **Replies everywhere** — optionally collect the replies under every post found by
  username or keyword, with a cap per post.
- **Clean post data** — every post has its id, short code and URL, full text, ISO
  timestamp, like/reply/repost/quote/share counts, media type, image and video URLs with
  width and height (all carousel items), outbound links and the link preview card,
  mentions, hashtags, the Threads topic tag, quoted post, pinned flag and language.
  Replies carry `replyTo` (the post they answer) and `replyToUsername`.

One run can mix usernames, post URLs and keywords. Links on threads.net and threads.com
both work.

### Use cases

- **Social media managers and brands** — track your own and competitors' accounts: what
  they post, how often, and which posts get the most likes, replies and reposts.
- **Marketing and PR teams** — monitor a keyword, product or campaign hashtag on Threads
  and read the conversation in the replies.
- **Influencer marketing** — compare creators by follower count, verified badge, posting
  rhythm and engagement per post before reaching out.
- **Researchers and journalists** — archive the posts and reply threads of public figures
  or around an event, with exact timestamps and counts.
- **Customer insight and sentiment** — collect the replies under a brand's posts and feed
  them into sentiment analysis or topic clustering.
- **Data and AI teams** — build text and image datasets from public Threads posts for
  classification, summarisation or trend detection.

### How much does it cost?

You pay only for the results you get (pay-per-event). Duplicates and items over your
`maxItems` limit are never charged.

| Event | What you pay for | Free plan | Paid plans (from) |
| --- | --- | --- | --- |
| `post` | One post in the dataset (from a profile, a keyword search or a post URL) | $0.44 per 1,000 | $0.40 per 1,000 |
| `reply` | One reply to a post in the dataset | $0.22 per 1,000 | $0.13 per 1,000 |
| `profile` | One profile record (usernames only) | $0.22 per 1,000 | $0.13 per 1,000 |

"Paid plans (from)" is the Gold-and-above price; Bronze and Silver plans sit in between.
Apify also charges a tiny actor-start fee ($0.00005 per run). Profile records can be
switched off with **Include profile records**.

Worked examples (Free plan prices):

1. **Competitor monitoring** — 10 accounts × 100 posts = 1,000 posts + 10 profile records:
   1,000 × $0.44 / 1,000 + 10 × $0.22 / 1,000 = $0.44 + $0.0022 = **$0.44**.
2. **Conversation research** — 20 post URLs with 100 replies each: 20 posts × $0.44 /
   1,000 + 2,000 replies × $0.22 / 1,000 = $0.0088 + $0.44 = **$0.449**.

The Apify Free plan includes $5 monthly credit, enough for about 11,000 posts or 22,000
replies every month.

### How to use Threads Scraper

In Apify Console:

1. Open Threads Scraper and click **Try for free**.
2. Enter **Usernames**, paste **Post URLs** and/or type **Search keywords**. The form is
   prefilled with two accounts, one post and one keyword.
3. Set **Max posts per username or keyword** and, for post URLs, **Max replies per post**.
   **Max items** caps the whole run.
4. Optionally switch on **Include replies for username and keyword posts**, and pick the
   **Search results** tab (Top, Recent or both).
5. Click **Start**. The prefilled input (about 40 items) takes about 5 seconds; 3,000
   items take under a minute.
6. Open the **Output** tab and export the dataset as JSON, CSV, Excel or HTML.

Through the API:

1. Get your API token in Apify Console → Settings → Integrations.
2. Call the actor with your input JSON (see [Integrations and API](#integrations-and-api)).
3. Read the dataset items from the run.

### Input

Fill in at least one of `usernames`, `postUrls` or `searchQueries`.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `usernames` | array | – | Accounts as `zuck`, `@zuck` or `https://www.threads.com/@zuck`. Each gives one profile record and its posts. |
| `postUrls` | array | – | Post links such as `https://www.threads.com/@zuck/post/DfNR0zhEdWv` or `https://www.threads.net/t/DfNR0zhEdWv`. Each gives the post and its replies. |
| `searchQueries` | array | – | Keywords or hashtags searched on Threads. |
| `searchSort` | string | `top` | `top`, `recent`, or `both` (both tabs merged without duplicates). |
| `maxPostsPerSource` | integer | `50` | Posts per username and per keyword. |
| `includeProfiles` | boolean | `true` | Output one `profile` record per username. |
| `includeReplies` | boolean | `false` | Also collect replies under every post found by username or keyword. Post URLs always include replies. |
| `maxRepliesPerPost` | integer | `20` | Reply records per post. `0` skips replies for post URLs too. |
| `maxItems` | integer | `1000` | Hard cap on all items (posts, replies and profiles) in the run. |
| `proxyConfiguration` | object | Apify Proxy | Proxy settings. The default datacenter proxy works for Threads. |
| `debug` | boolean | `false` | Log every page fetched. |

Example — three accounts, 200 posts each, without profile records:

```json
{
    "usernames": ["zuck", "mosseri", "nasa"],
    "maxPostsPerSource": 200,
    "includeProfiles": false,
    "maxItems": 600
}
```

Example — the conversation under two posts, 300 replies each:

```json
{
    "postUrls": [
        "https://www.threads.com/@zuck/post/DfNR0zhEdWv",
        "https://www.threads.net/t/DeZtp-TkQHh"
    ],
    "maxRepliesPerPost": 300
}
```

Example — keyword monitoring in both search tabs, with 10 replies per post:

```json
{
    "searchQueries": ["coffee", "AI agents"],
    "searchSort": "both",
    "includeReplies": true,
    "maxRepliesPerPost": 10
}
```

### Output

Every item has `type` (`post`, `reply` or `profile`), `source` (`profile`, `search` or
`post`) and `sourceInput` (the username, keyword or URL it came from). Examples from a
real run on 2026-11-05 (long URLs shortened).

#### Post

```json
{
    "type": "post",
    "id": "4020948437830849967",
    "code": "DfNR0zhEdWv",
    "url": "https://www.threads.com/@zuck/post/DfNR0zhEdWv",
    "text": "Tried to get the family to dress as burger night, but couldn't corral the littlest burger",
    "createdAt": "2026-11-01T17:33:48Z",
    "author": {
        "id": "63055343223",
        "username": "zuck",
        "fullName": "Mark Zuckerberg",
        "isVerified": true,
        "profilePicUrl": "https://scontent-dfw5-2.cdninstagram.com/v/t51.82787-19/825322135_...jpg"
    },
    "likeCount": 4274,
    "replyCount": 782,
    "repostCount": 187,
    "quoteCount": 34,
    "reshareCount": 103,
    "mediaType": "carousel",
    "media": [
        {
            "type": "image",
            "url": "https://scontent-dfw6-1.cdninstagram.com/v/t51.82787-15/879845210_...webp",
            "width": 2279,
            "height": 2849,
            "altText": null
        }
    ],
    "links": [],
    "linkPreview": null,
    "hashtags": [],
    "topicTag": null,
    "mentions": [],
    "isReply": false,
    "replyTo": null,
    "replyToUsername": null,
    "quotedPost": null,
    "repostedPost": null,
    "isPinned": false,
    "language": "en",
    "isPaidPartnership": false,
    "source": "post",
    "sourceInput": "https://www.threads.com/@zuck/post/DfNR0zhEdWv",
    "searchQuery": null,
    "scrapedAt": "2026-11-05T09:28:17Z"
}
```

#### Reply

Same fields as a post, with `type: "reply"` and `replyTo` pointing at the post it answers:

```json
{
    "type": "reply",
    "id": "4020950034274160894",
    "code": "DfNSMCUgZD-",
    "url": "https://www.threads.com/@aj77_12/post/DfNSMCUgZD-",
    "text": "A burger is a cheeseburger. 👍",
    "createdAt": "2026-11-01T17:36:58Z",
    "author": { "id": "72018667292", "username": "aj77_12", "fullName": "AJ", "isVerified": false, "profilePicUrl": "https://..." },
    "likeCount": 1,
    "replyCount": 0,
    "isReply": true,
    "replyTo": {
        "id": "4020948437830849967",
        "code": "DfNR0zhEdWv",
        "url": "https://www.threads.com/@zuck/post/DfNR0zhEdWv",
        "username": "zuck",
        "text": "Tried to get the family to dress as burger night, but couldn't corral the littlest burger"
    },
    "replyToUsername": "zuck",
    "source": "post",
    "sourceInput": "https://www.threads.com/@zuck/post/DfNR0zhEdWv"
}
```

#### Profile

```json
{
    "type": "profile",
    "id": "63055343223",
    "username": "zuck",
    "url": "https://www.threads.com/@zuck",
    "fullName": "Mark Zuckerberg",
    "biography": "Mostly superintelligence and MMA takes",
    "followerCount": 5758059,
    "isVerified": true,
    "isPrivate": false,
    "profilePicUrl": "https://scontent-fra5-1.cdninstagram.com/v/t51.82787-19/825322135_...s640x640...jpg",
    "bioLinks": [],
    "profileTags": ["aithreads", "UFCThreads", "AI", "MMA", "memes"],
    "source": "profile",
    "sourceInput": "zuck",
    "scrapedAt": "2026-11-05T09:28:17Z"
}
```

Field notes:

- `replyCount` counts direct replies. `reshareCount` is how often the post was shared;
  `repostCount` and `quoteCount` are Threads reposts and quote posts.
- `mediaType` is `text`, `image`, `video` or `carousel`. Every carousel item is in `media`
  with its own `width` and `height`; videos also have `thumbnailUrl`.
- `linkPreview.url` is the real target of the link card (unwrapped from Threads' redirect).
- `topicTag` is the topic Threads shows next to the author's name; `hashtags` are the
  `#words` in the text.
- In profile feeds a thread the author continued with their own replies gives one `post`
  record per part, each pointing at the previous part in `replyTo`. In search results a
  hit can come with another user's reply shown under it; that one is a `reply` record.
- `createdAt` and `scrapedAt` are UTC (ISO 8601).

### Integrations and API

Run the actor and get the items back in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/innolabs~threads-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"usernames": ["mosseri"], "maxPostsPerSource": 50}'
```

Python with [apify-client](https://docs.apify.com/api/client/python):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("innolabs/threads-scraper").call(
    run_input={
        "searchQueries": ["AI agents"],
        "searchSort": "recent",
        "includeReplies": True,
        "maxRepliesPerPost": 20,
    }
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["type"] == "post":
        print(item["likeCount"], item["author"]["username"], item["text"][:80])
```

- **Make, Zapier, n8n** — use the Apify app/node to start the actor and send posts to
  Google Sheets, Airtable, Notion or Slack.
- **Scheduling** — create a schedule in Apify Console (e.g. every morning) to track
  accounts or a keyword over time.
- **Webhooks** — trigger your pipeline when a run finishes.
- **AI agents (MCP)** — connect Claude, Cursor or other MCP clients through the Apify MCP
  server at https://mcp.apify.com and let your agent research Threads on demand.

### FAQ

#### Is it legal to scrape Threads?

The scraper only collects data Threads shows publicly to any visitor without logging in:
public profiles, their posts and replies. It does not log in and cannot see private
accounts. Posts and profiles contain names of people, which may be personal data under
laws such as GDPR, so make sure you have a legitimate reason to store them and that your
use complies with Threads' terms.

#### Do I need a Threads or Instagram account, or proxies?

No. The scraper needs no login, cookies or API key, and the default Apify Proxy setting
works. Rate-limited requests are retried automatically on a fresh connection.

#### How many posts can I get?

Profiles are paged back through their history (tested with 1,500 posts from one account).
Replies are paged too (453 replies under one post in a test).
Keyword search returns what Threads shows logged-out visitors: up to about 25 posts per
keyword and tab, so use **Top and Recent** and several related keywords for more. See
[Limits](#limits).

#### Why are view counts missing?

Threads does not show view counts to logged-out visitors, so they are not in the data.
Likes, replies, reposts, quotes and shares are.

#### What if a username or link fails?

Each username, post URL and keyword is processed on its own. If an account does not
exist or is private, or a post was deleted, the run continues with the rest and the
problem is listed in the run log and the run's `SUMMARY` record. You are not charged for
failed inputs. If every input fails, the run is marked as failed with the reason.

#### Can I download the images and videos?

Yes. The dataset contains direct image and video file URLs, which you can download with
any tool. The links are signed by Meta and expire after a few days, so download soon
after the run.

### Limits

- **Keyword search is capped by Threads for logged-out visitors**: up to about 25 posts
  per keyword in each tab, sometimes fewer (measured 2026-11-05: `coffee` 22–23 Top /
  16 Recent, `AI agents` 4–20 Top / 20 Recent), with no further pages.
  `maxPostsPerSource` above that has no effect for keywords.
- Profiles: the posts of the profile's Threads tab, including the author's self-reply
  continuations. Most accounts page back through their history (1,500 posts tested); a
  few end earlier (one account stopped at 201 posts).
- Replies are ranked the way Threads shows them (top first). Threads lists fewer replies
  to logged-out visitors than the reply counter says (a post with 782 replies listed 453);
  replies hidden by the author or restricted to followers are not visible.
- View counts are not available logged out. Bio links appear only when Threads shows them
  to logged-out visitors.
- Private accounts show no posts to logged-out visitors. Accounts that do not exist, and
  posts that were deleted, are reported as failed inputs.
- Media URLs are signed and expire after a few days.

# Changelog

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

# Actor input Schema

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

Threads profiles to scrape: one profile record (bio, followers, links) plus the latest posts of each. Accepts 'zuck', '@zuck' or a profile URL such as https://www.threads.com/@zuck (threads.net links work too).

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

Threads post links, e.g. https://www.threads.com/@zuck/post/DfNR0zhEdWv or https://www.threads.net/t/DfNR0zhEdWv. Each returns the post plus its replies (up to 'Max replies per post').

## `searchQueries` (type: `array`):

Keywords or hashtags searched on Threads, e.g. 'coffee' or 'AI agents'. Returns the posts Threads shows for the keyword (up to about 25 per keyword and tab without login, see Limits in the README).

## `searchSort` (type: `string`):

Which search tab to read: Top (most relevant), Recent (newest first) or both tabs merged without duplicates.

## `maxPostsPerSource` (type: `integer`):

How many posts to collect per username and per search keyword. Profiles are paged back through their history; search returns what Threads shows to logged-out visitors.

## `includeProfiles` (type: `boolean`):

For usernames, also output one profile record (name, bio, follower count, verified badge, bio links, profile picture).

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

Also collect the replies under every post found by username or keyword (up to 'Max replies per post' each). Post URLs always include replies.

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

Cap on reply records per post (direct replies and the replies shown under them). Set 0 to skip replies for post URLs too.

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

Hard cap on dataset items (posts, replies and profiles together). The run stops cleanly when reached.

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

Apify Proxy settings. The default datacenter proxy works for Threads; switch to the RESIDENTIAL group if pages start failing.

## `debug` (type: `boolean`):

Log every page and API call.

## Actor input object example

```json
{
  "usernames": [
    "zuck",
    "mosseri"
  ],
  "postUrls": [
    "https://www.threads.com/@zuck/post/DfNR0zhEdWv"
  ],
  "searchQueries": [
    "coffee"
  ],
  "searchSort": "top",
  "maxPostsPerSource": 10,
  "includeProfiles": true,
  "includeReplies": false,
  "maxRepliesPerPost": 10,
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "debug": false
}
```

# Actor output Schema

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

All items produced by the run, one JSON object per record.

# 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"
    ],
    "postUrls": [
        "https://www.threads.com/@zuck/post/DfNR0zhEdWv"
    ],
    "searchQueries": [
        "coffee"
    ],
    "maxPostsPerSource": 10,
    "maxRepliesPerPost": 10,
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("innolabs/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 = {
    "usernames": [
        "zuck",
        "mosseri",
    ],
    "postUrls": ["https://www.threads.com/@zuck/post/DfNR0zhEdWv"],
    "searchQueries": ["coffee"],
    "maxPostsPerSource": 10,
    "maxRepliesPerPost": 10,
    "maxItems": 100,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("innolabs/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 '{
  "usernames": [
    "zuck",
    "mosseri"
  ],
  "postUrls": [
    "https://www.threads.com/@zuck/post/DfNR0zhEdWv"
  ],
  "searchQueries": [
    "coffee"
  ],
  "maxPostsPerSource": 10,
  "maxRepliesPerPost": 10,
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call innolabs/threads-scraper --silent --output-dataset

```

## MCP server setup

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