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

Scrape Meta Threads (threads.com) without login: profile posts, Replies and Reposts tabs, post replies, keyword and hashtag search (60-85 posts per keyword), account search with emails from bios. Monitoring mode for new posts. From $1.50 per 1,000.

- **URL**: https://apify.com/alom/threads-scraper.md
- **Developed by:** [Alom Dev](https://apify.com/alom) (community)
- **Categories:** Social media, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

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** (threads.com, formerly threads.net, Meta's text network with 400M+ monthly users) into JSON, CSV or
Excel: **profile info and a profile's posts, replies and reposts, a post and its replies, keyword or #hashtag search,
and account search**. You get likes, replies, reposts, quotes and reshares, text, images, videos, links, mentions,
hashtags and topic tags, quoted posts, engagement rate, plus follower counts, bios, bio links, e-mails and verification.

✅ **No login needed:** it collects what a logged-out visitor sees, so no account of yours can be banned (logging in with your own cookie is optional and never required).
✅ **Cheaper than the popular Threads scrapers:** from $1.50 per 1,000 results, platform usage (compute and proxy) included.
✅ **Switching takes a minute:** keep your input, change the Actor ID. Field names match the most-used Threads scrapers.
✅ **Monitoring mode:** on a schedule, return only posts and replies you haven't received yet (and don't pay for the rest).
✅ **3-4x more search results without login:** ~60-85 posts per keyword instead of the ~20 one result page shows.
✅ **Hashtag and keyword search, Replies and Reposts tabs, account search:** everything a profile page shows, without logging in.
✅ **Contacts for lead generation:** e-mails and phone numbers that people put in their bio, plus their bio links.
✅ **Optional: your own cookie unlocks the real Recent tab** with deep paging (300 recent posts for "news" in our test, vs 155 without).

### What can you use Threads data for?

- **Brand and competitor monitoring:** what competitors post, how often, and how much engagement each post gets.
- **Brand mentions and hashtag tracking:** every new post that mentions your brand or uses your #hashtag, hourly.
- **Lead generation:** find accounts in a niche by keyword and collect the contact e-mails they list in their bio.
- **Influencer vetting:** followers, verification, posting frequency and real engagement per post.
- **Audience research:** read the replies under a viral post, or what people write about a keyword or #hashtag.
- **Social listening and alerts:** schedule a run every hour; get only what's new.
- **AI and research datasets:** post text with timestamps, authors and engagement counts.

### Switching from another Threads scraper?

1. Keep your input. `usernames`, `searchQueries`, `maxPosts`, `includeProfile`, `postedAfter` and `postedBefore` work
   the same way. Also accepted: `mode: "user"`, `keywords`, `max_posts`, `start_date`/`end_date`, `startUrls`.
2. Change the Actor ID.
3. Rows use the familiar field names (`postId`, `code`, `username`, `text`, `likeCount`, `replyCount`, `repostCount`,
   `quoteCount`, `mediaType`, `media`, `hashtags`, `mentions`, `urls`, `isReply`, `isRepost`, `timestamp`, `date`,
   `url`; profiles: `followerCount`, `biography`, `isVerified`, `profilePicUrl`), so your pipeline keeps working.

**What it costs:** 10,000 posts cost **$25 on the Free plan and $15 on Business** here. The popular Threads scrapers
charge $25-50 on every plan (plus per-run fees), and the search-only actors $80-200.

### What can it scrape?

| `mode` | Input | Output |
|---|---|---|
| `posts` (default for usernames) | usernames or profile URLs | one profile row + the account's posts, newest first, with paging |
| `profile` | usernames or profile URLs | profile rows only (followers, bio, links, topics): 1 request each |
| `post` | post URLs | the post, the author's own thread continuation, and its replies (with nested reply chains) |
| `user_replies` | usernames or profile URLs | the replies the account wrote (its **Replies** tab), each with the post it answered (`repliedToPost`) |
| `user_reposts` | usernames or profile URLs | the posts the account reposted (its **Reposts** tab): the original post + `repostedByUsername`, `repostedAt` |
| `search` | keywords, `#hashtags`, search or `/tag/` URLs | posts for each keyword: top order, or newest first (`searchSort: "recent"`) |
| `accounts` | keywords | accounts matching each keyword (up to 100), with followers, bio, bio links, e-mails and phones from the bio |

Leave `mode` empty and fill in any mix of `usernames`, `postUrls` and `searchQueries`: every group runs.

```json
{ "usernames": ["zuck", "@mosseri", "https://www.threads.net/@nasa"], "maxPosts": 100 }
```

```json
{ "postUrls": ["https://www.threads.com/@zuck/post/Dd1MqfcG0aH"], "maxReplies": 300 }
```

```json
{ "searchQueries": ["espresso machine", "#coffee"], "searchSort": "recent", "requireKeywordMatch": true }
```

```json
{ "mode": "user_replies", "usernames": ["mosseri"], "maxPosts": 100 }
```

```json
{ "mode": "accounts", "searchQueries": ["coffee roaster", "specialty coffee"], "maxPosts": 50 }
```

Also accepted from other scrapers: `mode` `user_reposts` / `reposts`, `user_replies` / `replies` (with usernames),
`profiles` (with keywords only = account search), `search_filter`. In the profile modes, `keywords` don't search: they
flag matching rows (`keywordMatch`), nothing is removed.

### Output example

```json
{
    "type": "post",
    "postId": "3994109866205639942",
    "code": "Ddt7cL5EfUG",
    "url": "https://www.threads.com/@zuck/post/Ddt7cL5EfUG",
    "text": "Agrippa said it's time to get back to work 😎",
    "username": "zuck",
    "fullName": "Mark Zuckerberg",
    "userId": "63055343223",
    "isVerified": true,
    "likeCount": 5007,
    "replyCount": 491,
    "repostCount": 258,
    "quoteCount": 47,
    "reshareCount": 140,
    "mediaType": "carousel",
    "media": [{ "type": "image", "url": "https://scontent…webp", "width": 2160, "height": 2700 }],
    "hashtags": [],
    "topicTag": null,
    "mentions": [],
    "urls": [],
    "linkPreview": null,
    "isReply": false,
    "replyToUsername": null,
    "parentPostId": null,
    "rootPostId": null,
    "isPinned": false,
    "isRepost": false,
    "repostedFrom": null,
    "quotedPost": null,
    "timestamp": 1790355021,
    "date": "2026-09-25T16:50:21.000Z",
    "source": "user:zuck",
    "scrapedAt": "2026-09-30T11:35:10.000Z"
}
```

(`media` is shortened here: this carousel has 9 images and videos.)

- **Replies** (`type: "reply"`) have the same fields plus `rootPostId` (the post you asked for) and `parentPostId`
  (what they answer: the root, or another reply in a chain).
- **More on every post:** `quotedPost` / `repostedFrom` with the original's text, likes, replies, reposts and date;
  `isQuotePost`, `linkPreview` (url, title, description, image), `mentionedAccounts` (with user ids), `taggedUsers`,
  `isEdited`, `isPaidPartnership`, `isAiGenerated` (Meta's AI label), `isSpoiler`, `language`, `accessibilityCaption`,
  `hasAudio`, `music` (title, artist), `location`, `replyControl`, `isLikedByAuthor`.
- **Engagement rate:** posts from a profile carry `authorFollowerCount` and `engagementRate` =
  (likes + replies + reposts) / followers. It is `null` where followers are unknown (search, post URLs, reposts).
- **Where it came from:** `sourceTab` (`posts`, `replies`, `reposts`), `searchKeyword` and `keywordMatch` (the text,
  topic tag or link title contains the keyword).
- **Profiles** (`type: "profile"`): `username`, `fullName`, `userId`, `biography`, `followerCount`, `isVerified`,
  `isPrivate`, `profilePicUrl` (largest), `bioLinks`, `emails` and `phones` (written in the bio), `profileTags`, `url`;
  `followingCount` when Threads shows it (usually `null`); `searchKeyword` in accounts mode.
- `source` says which input produced the row (`user:zuck`, `post:Dd1MqfcG0aH`, `search:#coffee`).
- Values Threads doesn't show are `null`, never a made-up `0` (for example, likes the author has hidden).
- A post that several inputs return is delivered (and charged) once per run.

### How much does it cost?

**From $1.50 per 1,000 results**, plus $0.005 per run. One result = one post, one reply, or one profile.

| Apify plan | Price per 1,000 results |
|---|---|
| Free | $2.50 |
| Starter (Bronze) | $2.00 |
| Scale (Silver) | $1.75 |
| Business (Gold) and above | $1.50 |

- 100 posts from each of 10 competitors: **$2.50** on the Free plan, **$1.50** on Business
- 500 replies under a viral post: **$1.25** on the Free plan

You only pay for results you get: nothing for failed inputs, duplicates or posts a monitor already returned.
Platform usage is included.

### Monitoring (only new posts)

Turn on **"Only new posts since my last run"**, give it a name (e.g. `competitors-daily`), and schedule the run in
Apify Console. Each run returns only posts no earlier run with that name returned. For profiles it stops at the first
post it already knows, so an hourly run of 20 accounts costs almost nothing when nobody posted. It also works for
replies (only new replies under a post) and search.

```json
{ "usernames": ["nike", "adidas", "puma"], "maxPosts": 50, "onlyNewSinceLastRun": true, "monitorName": "competitors-daily" }
```

### Own session cookie (optional, at your own risk)

You never need it. If you paste the cookies of **your own** logged-in threads.com tab into `sessionCookie` (the
`Cookie` header or a JSON export with at least `sessionid`), searches use Threads' real **Recent** tab and page
through it like the app does: in our test a Recent search for "news" returned 300 posts from the last 6 hours, against
155 without a cookie. A search never fails because of the cookie: if Threads rejects a logged-in request, the run
notes it in the log and fills up from logged-out result pages.

⚠️ Requests are then made **as your account** (from one IP, at most 2 in parallel). Meta may rate-limit, checkpoint or
ban accounts that scrape: use a spare account, and you carry that risk. The cookie is stored encrypted as a secret
input and is never logged or written to the results. If Threads doesn't accept it (expired, logged out), the run says
so in the log and continues logged out.

### Limitations

- **Search:** Threads shows logged-out visitors ~20 posts per result page and no "next page". The Actor opens several
  result pages (keyword search, tag search, tag page) from fresh IPs and merges them: **measured 60-85 unique posts per
  keyword**; it stops when 3 pages in a row bring nothing new. Without a session cookie, `searchSort: "recent"` sorts
  what was found newest first (Threads' own "Recent" tab needs login), and date filters are applied to what was found.
- **Profiles:** Threads lets logged-out visitors page back only so far: measured 138 threads for @nasa, 165 (~260
  posts) for @zuck and 419 for @mosseri. `maxPosts` above that returns what Threads shows. Replies and Reposts tabs page
  the same way.
- **Replies:** about the first 1,000 replies of a post, in Threads' "top" order or newest first (`repliesSort`).
- **Not available without login:** view counts, following and post counts, polls. Account search returns up to ~100
  accounts per keyword.
- An unknown username or post URL fails that input (the run finishes as `partial`), not the whole run. Threads
  answers them with a redirect to its login or home page, so we double-check from a second IP first.

### FAQ

**Do I need a Threads or Instagram account?** No. Nothing is logged in by default, so there is nothing to ban. The
optional session cookie only unlocks Threads' real "Recent" search tab and deeper search paging, at your own risk.

**Is it legal?** It collects publicly visible data. You are responsible for how you use it, including data
protection rules for personal data (usernames, replies).

**The run says `partial`. What does it mean?** Some inputs didn't finish: an unknown username, a deleted post, or the
run hit its timeout. The status message and the log say which. You are only charged for rows you received.

### More scrapers from the same developer

- [Threads Account Finder](https://apify.com/alom/threads-lead-finder): Threads accounts by keyword with followers, bio links and the contacts they list
- [Threads Hashtag & Keyword Monitor](https://apify.com/alom/threads-keyword-monitor): only the new posts for your keywords and #hashtags, for scheduled runs
- [Google Trends API & Scraper](https://apify.com/alom/google-trends-scraper): interest over time, regions, related queries and Trending Now, a pytrends alternative
- [YouTube Scraper](https://apify.com/alom/youtube-scraper): videos, channels, Shorts, comments, subtitles and community posts without the API quota
- [Bilibili Scraper](https://apify.com/alom/bilibili-scraper): videos, creators, full comment threads and danmaku from B站, no login
- [Google Hotels Scraper](https://apify.com/alom/google-hotels-scraper): hotel prices from every booking site across dates, room rates and reviews
- [Google Ads Transparency Scraper](https://apify.com/alom/google-ads-transparency-scraper): every Google ad a competitor runs, with the real ad copy

### Feedback

Missing a field, found a bug, or need a feature? Open an issue in the **Issues** tab and I'll take a look. If this
Actor saved you time, a short review on the Store page helps other people find it.

# Actor input Schema

## `mode` (type: `string`):

Leave empty to run everything you filled in below (usernames = posts, post URLs, keywords = post search). Pick a mode for the Replies / Reposts tabs of profiles or to find accounts by keyword.

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

e.g. <code>zuck</code>, <code>@mosseri</code> or <code>https://www.threads.com/@nasa</code> (threads.net works too). Used by the three Profiles modes.

## `includeProfile` (type: `boolean`):

In the profile modes (posts, replies, reposts), also return one <code>type: profile</code> row per account. It comes from the same page, so it adds no extra request.

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

e.g. <code>https://www.threads.com/@zuck/post/Dd1MqfcG0aH</code>. Returns the post, the author's own continuation of the thread and (optionally) its replies.

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

Return the replies to each post URL as <code>type: reply</code> rows (including nested reply chains), most relevant first.

## `maxReplies` (type: `integer`):

Threads shows logged-out visitors roughly the first 1,000 replies of a post.

## `repliesSort` (type: `string`):

<b>Newest first</b> is what a monitor wants: with <i>Only new posts since my last run</i> it stops at the first reply an earlier run already returned.

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

Keywords, <code>#hashtags</code>, or threads.com search / <code>/tag/</code> URLs. Threads shows logged-out visitors ~20 posts per result page, so the Actor opens several result pages (keyword search, tag search, tag page) from fresh IPs and merges them: typically <b>60-85 unique posts per keyword</b>. In <b>accounts</b> mode: keywords to find profiles by (up to 100 per keyword). In the profile modes: keywords only <i>flag</i> matching rows (<code>keywordMatch</code>).

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

<b>Recent</b> returns the posts found newest first. The real "Recent" tab of Threads needs a logged-in account: with your own session cookie (below) it is used, without one the Actor sorts what it found.

## `requireKeywordMatch` (type: `boolean`):

Threads' search also returns loosely related posts. Turn on to drop hits whose text, topic tag or link title does not contain the keyword (dropped posts are not charged). Every search row has <code>keywordMatch</code> either way.

## `maxPosts` (type: `integer`):

Upper limit per username, per keyword and (accounts mode) accounts per keyword (max 100).

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

Date (UTC), ISO timestamp or relative like <code>7 days</code>. Applies to posts, replies, reposts (repost time) and search results. Profile paging stops once older posts are reached.

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

Date (UTC) or ISO timestamp (exclusive).

## `onlyNewSinceLastRun` (type: `boolean`):

Skip posts, replies, reposts and accounts an earlier run with the same monitor name already returned (you are not charged for them). Pair with a Schedule.

## `monitorName` (type: `string`):

Keep separate memories for different monitors (e.g. <code>competitors-daily</code>). Defaults to <code>default</code>.

## `sessionCookie` (type: `string`):

<b>Optional and never required.</b> Paste the cookies of a threads.com tab where you are logged in (the <code>Cookie</code> header, or a JSON export; at least <code>sessionid</code>, ideally also <code>csrftoken</code> and <code>ds\_user\_id</code>). Searches then use Threads' real "Recent" tab and page through it like the app (about twice as many recent posts in our tests). Requests are then made <b>as your account</b> (one IP, at most 2 in parallel): Meta may rate-limit, checkpoint or ban accounts that scrape, so use a spare account. If the cookie is invalid or expired, the run says so and continues without it. Stored encrypted, never logged or returned.

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

Profiles / posts / keywords processed in parallel.

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

The default Apify datacenter proxy works and is included in the price. If Threads starts blocking it, the run switches to residential proxies by itself (logged).

## Actor input object example

```json
{
  "usernames": [
    "zuck"
  ],
  "includeProfile": true,
  "includeReplies": true,
  "maxReplies": 50,
  "repliesSort": "top",
  "searchSort": "top",
  "requireKeywordMatch": false,
  "maxPosts": 30,
  "onlyNewSinceLastRun": false,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (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 = {
    "usernames": [
        "zuck"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("alom/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"] }

# Run the Actor and wait for it to finish
run = client.actor("alom/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"
  ]
}' |
apify call alom/threads-scraper --silent --output-dataset

```

## MCP server setup

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