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

Threads API alternative with no login, no Meta token and no 500-search weekly cap. Scrape Threads posts, Threads replies and comments with full reply trees, profiles and keyword search, or schedule it to get only new posts. JSON, CSV, Excel.

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

## Pricing

from $1.20 / 1,000 post scrapeds

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 and API: Posts, Replies, Profiles and Keyword Search

Scrape [Threads](https://www.threads.com) (Meta) at scale. This Threads scraper returns posts, full reply trees with conversation depth, profiles, and keyword search results as clean, structured JSON, CSV or Excel.

**No login. No cookies. No account pool. No session tokens to supply.**

**New: only new posts since the last run.** Put it on a schedule, turn on monitoring mode, and each run returns and bills only posts you have not received before, with follower change for every profile. [How it works](#monitoring-mode-only-new-posts-since-the-last-run)

### Sample output (real run, keyword "anthropic")

| Author | Post | Likes | Replies | Reposts |
|---|---|---|---|---|
| @bodylikeseafoam | In case you need another reason to hate AI, OpenAI just scraped... | 11,305 | 182 | 2,049 |
| @novaramedia | An artificial intelligence researcher has quit Anthropic, accusing... | 287 | 17 | 89 |
| @aditijain.ai | Former OpenAI and Anthropic researcher Jacob Coxon, who publicly... | 10 | 1 | 1 |

Every row also carries the post URL, timestamp, language, media, quoted post, and the keyword that found it.

### Why choose this Threads scraper

| Feature | This Actor | Official Threads API | Typical Store alternative |
|---|---|---|---|
| Keyword searches | Unlimited | About 500 per 7 days | Varies |
| Login or Meta token | None | App review plus token | Often none |
| Reply trees with conversation depth | Yes, up to 6 levels | Limited | Rarely |
| Profile posts beyond the first page | Yes | Limited | Rarely |
| Price per 1,000 posts | **$1.80**, no start fee | Free, capped | $2.50 to $20, often plus $0.02 per run |

**What it costs:** 1,000 posts cost $1.80. Monitoring 10 brand keywords at 200 posts each is about $3.60 per run.

### A Threads API alternative: no login, no weekly search cap

Meta's official Threads API allows roughly **500 keyword searches per rolling seven days**. That is unusable for social listening, brand monitoring, or lead generation, and it is why teams look for a Threads scraper in the first place.

This Actor reads the same public pages a logged-out visitor sees. There is no weekly search quota, nothing to authenticate, and nothing to keep alive.

It also handles the three things other Threads scrapers get wrong:

| Area | Typical Threads scraper | This Actor |
|---|---|---|
| Reply trees | Flat list, or fails outright | Full tree with conversation depth |
| Profile posts | First page only, about 25 | Pages through to your limit |
| Quote posts with no added text | Row with empty text, looks broken | Quoted post resolved inline |
| Only new posts on scheduled runs | Rarely | Built in |
| Login required | Often | Never |
| Breaks when Meta ships an update | Often | Built to keep working |

### What you can scrape

**Keyword search:** every public post matching a term, by relevance or recency, each result tagged with the keyword that found it.

**Profiles:** handle, display name, bio, bio links, follower count, verification and privacy status, profile pictures, plus that account's posts well beyond the first page.

**Posts and reply trees:** the post plus its replies, each carrying its depth in the conversation, so you can reconstruct who answered whom and how deep the thread went.

### Input

Provide at least one of `searchQueries`, `profiles`, or `postUrls`. You can combine them in one run.

```json
{
  "searchQueries": ["openai", "claude ai"],
  "profiles": ["@zuck", "https://www.threads.com/@mosseri"],
  "postUrls": ["https://www.threads.com/@zuck/post/DdCYWl7GktV"],
  "searchSort": "both",
  "includeReplies": true,
  "replyDepth": 3,
  "maxPostsPerProfile": 200,
  "maxRepliesPerPost": 300,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}
```

**Running with no input.** If you start a run without setting anything, the Actor returns a small live sample so you can see the output shape immediately. Set your own keywords, profiles, or post URLs for a real run.

Handles are accepted as `zuck`, `@zuck`, or a full URL. Posts are accepted as a full URL or a bare shortcode.

`searchSort` accepts `default` (relevance), `recent` (reverse chronological), or `both`. The two orderings return overlapping but different sets, so `both` gives the widest coverage at twice the request cost.

`replyDepth` controls how far down the reply tree the Actor walks. 1 returns the first page of replies, and higher values (up to 6) open each reply's own page to pull replies to replies. The total stays capped by `maxRepliesPerPost`.

### Output

One record per post, reply, or profile. Post records look like this:

```json
{
  "type": "post",
  "id": "3981852126213720917_63055343223",
  "pk": "3981852126213720917",
  "code": "DdCYWl7GktV",
  "url": "https://www.threads.com/@zuck/post/DdCYWl7GktV",
  "text": "Mostly superintelligence and MMA takes",
  "language": "en",
  "createdAt": "2026-09-08T14:56:24.000Z",
  "takenAtTimestamp": 1788893784,
  "likeCount": 2715,
  "replyCount": 1602,
  "repostCount": 231,
  "quoteCount": 142,
  "reshareCount": 293,
  "isReply": false,
  "replyToAuthor": null,
  "rootPostCode": null,
  "replyDepth": 0,
  "isQuotePost": false,
  "quotedPost": null,
  "mediaType": "text",
  "media": [],
  "linkPreview": null,
  "author": {
    "id": "63055343223",
    "username": "zuck",
    "fullName": "Mark Zuckerberg",
    "isVerified": true,
    "profilePicUrl": "https://...",
    "profileUrl": "https://www.threads.com/@zuck"
  },
  "authorUsername": "zuck",
  "source": "profile",
  "scrapedAt": "2026-09-10T04:31:00.000Z"
}
```

Five details that matter if you are moving this into a database:

- **`pk` is a string.** Threads primary keys exceed 2^53, so any pipeline that reads them as JSON numbers silently corrupts the last digits. This Actor derives them from the string id and never emits them as numbers.
- **Missing counters stay `null`, never `0`.** You can tell "no likes" apart from "not published by Threads".
- **`replyDepth`** is `0` for the conversation root, `1` for a direct reply, and deeper for nested replies.
- **`authorUsername` is the flat copy of `author.username`.** Spreadsheet and table views cannot read nested paths, so use the flat field for CSV exports and the nested `author` object in code.
- **Filter on `type`, not `source`.** A run can mix posts and profiles in one dataset. `type` is `"post"` or `"profile"`, while `source` tells you which surface a post came from (`search`, `profile`, `post`, or `replies`), and a profile's own posts are tagged `source: "profile"` too.

### Monitoring mode: only new posts since the last run

Turn on **Only new since last run** (`onlyNewSinceLastRun`) and put the Actor on a schedule, hourly or daily. The first run returns everything it finds and remembers it. Every run after that returns only the posts it has not delivered before, **so you pay only for what is new** and your sheet, Slack channel or pipeline receives only fresh items.

- **Applies to keyword search and profile posts.** Post URLs and reply trees are always returned in full.
- On profiles it also reports `previousFollowerCount` and `followerChange` since the last run.
- **Memory is kept per keyword and profile**, in a key-value store named `threads-monitor-…` in your own Apify account. Nobody else can see it.
- **Running several schedules?** Give each one its own **Monitor name** (`monitorName`), one per client or campaign for example, and they keep separate memory.
- **Every run writes a `SUMMARY` record** to its key-value store with the number of new and skipped items, which suits integrations and AI agents.
- **To start over,** change the monitor name.

```json
{
  "searchQueries": ["your brand"],
  "profiles": ["@competitor"],
  "onlyNewSinceLastRun": true,
  "monitorName": "weekly-report"
}
```

### Pricing

Pay per result, not per minute. A run that returns nothing costs nothing.

| Event | What it covers |
|---|---|
| Post scraped | One post from search or a profile, with engagement, media, and any quoted post |
| Reply scraped | One reply, with its depth in the conversation |
| Profile scraped | One profile with follower count, bio, links, and verification |

Replies are billed separately because deep reply traversal is the expensive path. If you only want search results, you never pay for it.

### Limits, stated plainly

**Reply depth.** By default the Actor returns the first page of replies (about 25 to 30, at depth 1). Raise `replyDepth` up to 6 to walk replies to replies, bounded by `maxRepliesPerPost`.

**Profile posts.** The Actor pages through a profile's posts up to `maxPostsPerProfile`, well beyond the first page Threads shows.

**Keyword search depth.** Threads serves the first page of search results to logged-out visitors, about 25 per keyword and ordering. Deeper search pages are only returned to logged-in sessions, and this Actor runs without login, so run both orderings to widen coverage per term.

**Proxies.** Threads rate-limits single IP addresses quickly. Residential proxies are strongly recommended for anything beyond a handful of requests. The Actor warns you if it is running without one.

**Public data only.** Private accounts, direct messages, and anything behind a login are out of scope by design.

### Legal and compliance

This Actor reads only public Threads pages, the same ones any logged-out visitor can open. It does not log in, does not bypass an access control, and does not touch private accounts.

Threads posts and profiles contain personal data. If you are in the EU or UK, GDPR applies to what you collect and what you do with it, and having a lawful basis is your responsibility as the data controller. Do not resell profile-level personal data as a lead list without the regional carve-outs that apply to you.

### FAQ

**Do I need a Threads or Instagram account?**
No. Nothing in this Actor authenticates, and there is nowhere to enter credentials.

**Does it break when Meta ships an update?**
Less often than most. Meta changes Threads often, and many scrapers break with each change. This Actor is engineered to keep working through those changes, so your runs and integrations stay stable.

**Can I get more than 25 replies on a post?**
Yes. By default (`replyDepth` = 1) you get the first page of replies, roughly the top 25, with accurate tree depth. Raise `replyDepth` to open each reply's own page and pull replies to replies: 2 adds the replies under each top-level reply, 3 goes one level deeper, and so on up to 6. The total is still capped by `maxRepliesPerPost`, and a reply is billed the same whatever depth it was found at.

**Can I get more than 25 posts from a profile?**
Yes. The Actor follows the profile's post feed page by page up to `maxPostsPerProfile`, so you can pull hundreds of posts from a single handle.

**How do I monitor a keyword continuously?**
Schedule the Actor and give it your keywords. Each result carries `searchQuery` and `scrapedAt`, so appending runs to one dataset and de-duplicating on `id` gives you a time series.

**Why is `text` empty on some records?**
Those are quote posts where the author added no words of their own. The post they quoted is in `quotedPost`, with its text and author. The record is complete, not broken.

**Does this work with threads.net links?**
Yes. Threads moved from threads.net to threads.com, and both forms are accepted, as are bare handles and bare post shortcodes. You do not need to rewrite old links.

**How is this different from the official Threads API?**
Meta's Threads API needs an app, an access token, and it caps keyword search at roughly 500 queries per rolling seven days. This Actor needs none of that and has no weekly quota, because it reads the same public pages a logged-out visitor sees.

**Can I use it from Make, Zapier, n8n, or a script?**
Yes. Every Apify Actor is callable over the REST API and through Apify's integrations, and results come back as JSON, CSV, or Excel.

**Is Threads the same as Instagram?**
Threads runs on Instagram's infrastructure and shares its account system, but the content is separate. This Actor scrapes Threads only.

### Use it as an API

Run it from your own code and get the results back in one call. Replace `YOUR_TOKEN` with your [Apify API token](https://console.apify.com/settings/integrations).

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

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run_input = {'searchQueries': ['openai'], 'maxPostsPerQuery': 100}
run = client.actor("headply/threads-scraper").call(run_input=run_input)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

It also works from Make, Zapier, n8n, Google Sheets, and as a tool for AI agents through the [Apify MCP server](https://mcp.apify.com/?actors=headply/threads-scraper).

### More data tools from the same developer

- [TikTok and YouTube Transcript API](https://apify.com/headply/tiktok-youtube-transcript-scraper): video to text, even without captions
- [Google Trends Scraper and API](https://apify.com/headply/google-trends-scraper): hundreds of keywords on one scale, daily history
- [Reddit RSS Feed Replacement](https://apify.com/headply/reddit-rss-feed): new posts and comments from subreddits, users, and searches since your last run
- [Reddit Comments Scraper](https://apify.com/headply/reddit-comments-scraper): full comment threads with replies, depth, and parent IDs
- [Reddit Search Scraper](https://apify.com/headply/reddit-search-scraper): posts by keyword across Reddit or inside subreddits, no API key
- [Instagram Highlights Scraper](https://apify.com/headply/instagram-highlights-scraper): archive and download story highlights, no login

# Actor input Schema

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

Keywords or phrases to search on Threads. Each term is scraped separately and every result is tagged with the term that found it. Threads' own API allows roughly 500 keyword searches per week; this has no such cap.

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

Threads handles or profile URLs, for example zuck, @zuck or https://www.threads.com/@zuck. Returns the profile plus its most recent posts.

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

Threads post URLs or bare shortcodes. Returns the post and its reply tree, with each reply's depth in the conversation.

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

Threads ranks search results either by relevance or by recency. The two orderings return overlapping but different sets, so 'Both' gives the widest coverage at twice the request cost.

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

For each post URL, also scrape its replies with conversation depth. Turn off to fetch the posts alone.

## `includeProfilePosts` (type: `boolean`):

Return each profile's recent posts alongside the profile record. Turn off for profile details only.

## `maxPostsPerQuery` (type: `integer`):

Upper bound on results returned for each keyword. Keyword search returns the first page of results; Threads only serves deeper search pages to logged-in sessions, and this Actor runs without login.

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

Upper bound on posts returned for each profile. The Actor pages through the profile's posts to reach this number, beyond the first page shown on the profile.

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

Upper bound on replies returned for each post. Set to 0 to skip replies entirely.

## `replyDepth` (type: `integer`):

How deep into the reply tree to traverse. 1 returns the first page of replies embedded in the post (fastest and cheapest, roughly the top ~25). Higher values open each reply's own page to gather replies-to-replies, costing one extra page fetch per reply that has them; the total is still capped by Max replies per post, and each reply is billed the same either way.

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

For scheduled runs. Returns only posts not delivered by an earlier run with the same Monitor name, so you never pay for the same post twice. Applies to keyword searches and profile posts; post URLs and replies are always returned in full. Profiles also get followerChange since the last run. The first run returns everything and remembers it.

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

Label for this monitor's memory. Runs with the same name share memory; use a different name per schedule, client or campaign to keep them separate. Change it to start over. Only used when monitoring mode is on.

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

Threads rate-limits single IP addresses quickly. Residential proxies are strongly recommended for anything beyond a handful of requests.

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

Parallel requests. Raise for speed, lower if you see blocks.

## `maxRequestRetries` (type: `integer`):

How many times a failed request is retried, with a fresh proxy address each time, before it is given up on.

## Actor input object example

```json
{
  "searchQueries": [
    "openai"
  ],
  "searchSort": "default",
  "includeReplies": true,
  "includeProfilePosts": true,
  "maxPostsPerQuery": 100,
  "maxPostsPerProfile": 100,
  "maxRepliesPerPost": 200,
  "replyDepth": 1,
  "onlyNewSinceLastRun": false,
  "monitorName": "default",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 10,
  "maxRequestRetries": 4
}
```

# Actor output Schema

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

Dataset containing all scraped data

# 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 = {
    "searchQueries": [
        "openai"
    ]
};

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

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

```

## MCP server setup

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