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

Scrape Threads without an account: profile feeds with pagination, single posts with top replies and keyword search, about 15 posts per second. Posts include text, image and MP4 URLs, like/reply/repost/quote counts, link previews, dates and author; profiles add bio and followers. JSON/CSV, API.

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

## Pricing

from $1.75 / 1,000 posts

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 Scraper: Profiles, Posts, Replies & Search

Extract public data from **Threads** (threads.com) without an account, cookies,
or a browser. Give the Actor `@handles`, post URLs, or search keywords and get
clean JSON: post text, images and videos, like / reply / repost / quote counts,
timestamps, link previews, and author details — plus profile records with bio,
follower count, and verification status. This is an **unofficial** tool and is
not affiliated with Meta.

### What does Threads Scraper do?

- **Profiles** – one profile record (name, bio, bio links, followers, verified,
  avatar) and the newest posts of the profile, paginated as deep as you ask.
- **Posts** – paste a post URL to get the post and the top replies shown on its
  page (about 20 per post in the logged-out view), each as its own record.
- **Search** – keyword search returns the first results page Threads shows to
  visitors (about 20 posts per query).

Everything is read from the same public pages and endpoints a logged-out
browser uses, so no login is ever required and no account can be banned.

### Why use Threads Scraper?

- **No cookies, no risk** – only logged-out data is read; you never hand over
  session tokens.
- **Engagement metrics included** – likes, replies, reposts, quotes, and
  reshares per post.
- **Media URLs** – full-resolution image and MP4 video URLs, including every
  item of carousel posts.
- **Fast and cheap** – no browser is started; a profile page plus one
  pagination request delivers ~15 posts in about a second.
- **Pay per result** – you are charged per post and per profile actually
  saved. Failed handles, missing posts, and empty searches cost nothing.

### How to use it

1. Enter one or more **Profiles** (`@zuck` or `https://www.threads.com/@zuck`)
   and set **Maximum posts per profile**.
2. Optionally add **Post URLs** and decide whether to **Include replies**.
3. Optionally add **Search queries**.
4. Click **Start**. Results appear in the **Dataset** tab; export as JSON, CSV,
   or Excel, or fetch them through the API and integrations.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `profiles` | array of strings | – | `@handle` or profile URLs. |
| `maxPostsPerProfile` | integer | 50 | Newest posts per profile (1–2000). |
| `postUrls` | array of strings | – | `https://www.threads.com/@user/post/CODE`. |
| `includeReplies` | boolean | `true` | Save the replies shown on each post page. |
| `searchQueries` | array of strings | – | Keywords; first results page per query. |
| `proxyConfiguration` | object | Apify Proxy on | Session rotates every 15 requests. |
| `profilePostsDocId` | string | (current) | Advanced: Threads' GraphQL query id for profile pagination; change only if pagination breaks after a Threads update. |

Example:

```json
{
  "profiles": ["@zuck", "@meta"],
  "maxPostsPerProfile": 100,
  "postUrls": ["https://www.threads.com/@zuck/post/Db2wI-DilLt"],
  "searchQueries": ["open source AI"]
}
```

### Output

Each item has `type` (`post` or `profile`), `source` (which handle, URL, or
query produced it) and `scrapedAt`.

```json
{
  "type": "post",
  "id": "3960564644938666733",
  "code": "Db2wI-DilLt",
  "url": "https://www.threads.com/@zuck/post/Db2wI-DilLt",
  "text": "Today we're also opening the weights for Muse Glimmer ...",
  "createdAt": "2026-08-10T10:01:59+00:00",
  "likeCount": 1945,
  "replyCount": 510,
  "repostCount": 88,
  "quoteCount": 46,
  "reshareCount": 91,
  "mediaType": "text",
  "images": [],
  "videos": [],
  "linkPreview": null,
  "isReply": false,
  "author": {"id": "63055343223", "username": "zuck", "fullName": "Mark Zuckerberg", "isVerified": true},
  "authorUsername": "zuck",
  "source": "profile:@zuck",
  "scrapedAt": "2026-09-15T02:30:00+00:00"
}
```

### Data fields

**Post** (`type: "post"`): `id`, `code`, `url`, `text`, `createdAt`,
`likeCount`, `replyCount`, `repostCount`, `quoteCount`, `reshareCount`,
`mediaType` (`text`, `image`, `video`, `carousel`), `images[]` (`url`,
`width`, `height`), `videos[]` (`url`, `width`, `height`, `thumbnailUrl`),
`linkPreview` (`url`, `displayUrl`, `title`, `imageUrl`), `isReply`,
`replyToUsername`, `replyToPostCode` (for replies collected from a post URL),
`language`, `isPaidPartnership`, `author` (`id`, `username`, `fullName`,
`isVerified`, `profilePicUrl`, `profileUrl`), `authorUsername`.

**Profile** (`type: "profile"`): `id`, `username`, `url`, `fullName`, `bio`,
`bioLinks[]`, `followerCount`, `isVerified`, `isPrivate`, `profilePicUrl`.

### Pricing

Pay per event: one `post` event for every post or reply saved and one
`profile` event for every profile record. Failed or private profiles, missing
posts, and empty searches are never charged. Set a maximum charge per run to
cap spending; the Actor stops cleanly when the limit is reached.

### Tips

- Threads shows logged-out visitors the top ~20 replies of a post and the first
  \~20 search results; deeper reply and search pagination requires a login and
  is out of scope.
- Profile pagination goes as deep as Threads allows for public profiles;
  `maxPostsPerProfile` caps it.
- Private profiles and deleted posts return an `error`-level warning in the
  log and are skipped without charge.
- Keep Apify Proxy enabled for large runs; Threads tolerates moderate request
  rates but rotating sessions avoids throttling.

### FAQ

**Does it need my Threads / Instagram login?** No, and it never asks for it.
Only public, logged-out data is collected.

**Why did pagination stop early on a profile?** Meta ships new web builds
regularly and occasionally changes the GraphQL query id. The Actor then
delivers the posts it already fetched and logs a warning; the `profilePostsDocId`
input lets you supply the new id immediately, and the Actor is updated
promptly.

**Can it scrape the home feed, followers, or likes lists?** No. Those views
require an account.

**Is scraping Threads legal?** The Actor reads only publicly visible content.
You are responsible for using the data in line with Meta's terms and the
privacy laws that apply to you, especially when storing author information.

**Something is broken.** Open an issue on the Actor page; issues are answered
within a few days.

# Actor input Schema

## `profiles` (type: `array`):

Threads profiles to scrape, one per line, as @handle or profile URL (https://www.threads.com/@zuck). Each profile yields one profile record plus up to 'Maximum posts per profile' of its newest posts.

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

How many of the newest posts to collect from each profile (the Actor paginates automatically).

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

Individual Threads post URLs (https://www.threads.com/@user/post/CODE), one per line. Each yields the post itself and, optionally, its replies.

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

For each post URL, also save the reply posts shown on the post page (about 20 top replies, logged-out view). Each reply is a separate post record.

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

Keywords to search on Threads, one per line. Each query returns the first page of results (about 20 posts) that Threads shows to visitors without an account.

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

Apify Proxy settings. Enabled by default; the Actor rotates the proxy session every 15 requests.

## `profilePostsDocId` (type: `string`):

Only change this if profile pagination stops working after a Threads web update. The value is the doc\_id of Threads' BarcelonaProfileThreadsTabRefetchableDirectQuery for logged-out visitors.

## Actor input object example

```json
{
  "profiles": [
    "@zuck"
  ],
  "maxPostsPerProfile": 20,
  "postUrls": [],
  "includeReplies": true,
  "searchQueries": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "profilePostsDocId": "28150103917987977"
}
```

# 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 = {
    "profiles": [
        "@zuck"
    ],
    "maxPostsPerProfile": 20,
    "postUrls": [],
    "includeReplies": true,
    "searchQueries": [],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("kyungminlee/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 = {
    "profiles": ["@zuck"],
    "maxPostsPerProfile": 20,
    "postUrls": [],
    "includeReplies": True,
    "searchQueries": [],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("kyungminlee/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 '{
  "profiles": [
    "@zuck"
  ],
  "maxPostsPerProfile": 20,
  "postUrls": [],
  "includeReplies": true,
  "searchQueries": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call kyungminlee/threads-scraper --silent --output-dataset

```

## MCP server setup

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