# Threads Scraper | 25 Fields, 4 Modes, No Login, Monitor (`themineworks/threads-scraper`) Actor

Scrape Meta Threads (threads.com) posts, profiles, hashtags & search. No login, no cookies. Get text, media, likes, replies & reposts. Pay only per post delivered. Works in Claude, ChatGPT & any MCP agent.

- **URL**: https://apify.com/themineworks/threads-scraper.md
- **Developed by:** [The Mine Works](https://apify.com/themineworks) (community)
- **Categories:** Social media, Lead generation, MCP servers
- **Features:** Limited permissions
- **Stats:** 475 total users, 112 monthly users, 100.0% runs succeeded, 4 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

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

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: Posts, Profiles, Hashtags & Search (No Login)

> **Part of the Social & Market Research MCP.** This actor's data is also available to AI agents through our [Social & Market Research MCP server](https://apify.com/themineworks/social-research-mcp?fpr=ymnoit\&utm_source=apify-readme\&utm_medium=referral), eight social, news and search-interest tools behind one endpoint. No result, no charge.

> ⚡ Pay only per post delivered. No login, no cookies, no ban risk.
> 💸 Empty searches, blocked pages and failed runs are never billed.

### What does Threads Scraper do?

Threads Scraper turns any public Meta Threads target, a profile, a search keyword, a hashtag, or a direct post URL, into structured JSON: post text, media URLs, likes, replies, reposts, quote counts, author info, and full timestamps. Meta never shipped a public Threads API, so the usual options are manual copy-paste or a brittle browser build. This actor reads the same public pages any logged-out visitor sees on threads.com, four modes in one actor, full engagement counts and media captured on every post, and you pay only for posts actually delivered.

**Built for unattended running.** This actor has served more than 155,000 runs to date, with per-request IP rotation and a deadline watchdog that returns partial results instead of dying mid-run. A run that delivers no posts never pays for posts, and from 22 October 2026 it costs nothing at all.

✅ No login | ✅ No cookies | ✅ Pay per post delivered | ✅ Empty runs free from 22 October | ✅ MCP-ready for AI agents

### Who is it for?

Brand and social teams tracking mentions, hashtags, and competitor accounts. Market researchers building sentiment or trend datasets from live conversation. Growth and content teams watching which formats and hooks are working on Threads right now. Anyone feeding an AI agent that needs current Threads data without building a scraper of their own.

### How much does it cost to scrape Threads posts?

You pay per post delivered, plus a $0.002 run fee. From 22 October 2026 the run fee is charged only on runs that deliver at least one post, so a run that delivers nothing costs nothing. No subscription, no monthly minimum.

| Event | Price | You pay when |
| --- | --- | --- |
| Post delivered | $0.0012 on Gold, $0.002 on Free | A post record lands in your dataset |
| Run fee | $0.002 | Every run until 21 October 2026. From 22 October 2026, only runs that deliver at least one post |

| Apify plan | Price per 1,000 posts |
| --- | --- |
| Free | $2.00 |
| Bronze | $1.80 |
| Silver | $1.60 |
| Gold and above | $1.20 |

The Pricing tab on this page is the single source of truth and always shows the rate for your own plan. If this table and the Pricing tab ever disagree, the Pricing tab is right.

**What a real job costs.** Apify's Free plan includes $5 of usage credit every month, about 2,400 posts a month at no cost to you. A 500-post hashtag pull costs about $0.60 on Gold plus the $0.002 run fee. A monitor that polls every hour pays for its new posts plus the run fee; from 22 October 2026 it pays the run fee only on the polls that find new posts, so if 10 of its 24 daily polls find something, that is $0.02 a day in run fees.

**What is never charged.** Empty searches, blocked pages and failed runs never pay for posts, and from 22 October 2026 they pay no run fee either. Post charges fire only after a post record is in your dataset. Many Threads scrapers on the Store charge a start fee on every run, including the empty ones, and on a monitor that polls many times a day that fee is most of the bill. Compare total cost, not just the headline rate.

### How does it work?

The actor requests the same public threads.com pages a logged-out visitor gets, then extracts the structured post data Threads' own front-end embeds in the page to render it, the same JSON payload the site itself uses, just read instead of displayed. No account, no cookies, no anti-bot fragility, and every request goes out through a fresh proxy session so one throttled IP never stalls a whole run.

For each post it flattens Threads' deeply nested response into one analysis-ready record. Reply and repost posts are filtered out by default; toggle the input to include them, and use the `is_reply` / `is_repost` flags to sort them out downstream. For search mode, the actor fans out across every logged-out surface that carries results for your query: the recent and top feeds, the query's own tag feed, and repeated recency windows over the run, all deduplicated into one result set, which is how it exceeds the roughly 20-post single-feed cap. Each run's summary lists the exact `search_windows` yield in the run's OUTPUT record, so you can audit it.

#### How many posts should I expect per run?

Set expectations by mode before you run it, because Threads is not uniform here:

| Mode | Realistic yield per run | Notes |
| --- | --- | --- |
| **Search** | tens to hundreds | Best mode for volume. Fans out across several logged-out surfaces and deduplicates. |
| **Hashtag** | tens | Recent or top feed for a tag. |
| **Post** | exactly what you pass | One record per post URL, plus quoted or parent context. |
| **Profile** | **about 4 to 5 recent posts per username** | Threads' own logged-out limit, not a cap we set. List several usernames to reach a higher total. |

That profile figure is deliberate honesty rather than a bug report. A logged-out visitor is served only the handful of most recent posts on a profile, and the timeline then repeats the same posts instead of paging deeper, so raising Max posts does not produce more. We verified this repeatedly against live profiles across several weeks. The actor detects the repeat and stops within about three requests rather than paying to spin, and you are never charged for posts that do not exist. If you need many posts about a person rather than by them, use search mode with their name or handle as the query, then filter on the `username` field if you only want their own posts.

**Reaching a high `maxPosts` in profile mode:** the fix is more usernames, not a bigger number. List several in `profileUsernames` and the run draws from each in turn until the total is met or the list runs out, skipping any username that returns nothing rather than failing the run. Verified: 6 usernames, `maxPosts: 20`, returned exactly 20 posts spread 5/4/4/4/2/1 across 5 of the 6, one had nothing to give.

### 🧾 What input does it take?

```json
{
  "mode": "hashtag",
  "hashtag": "ai",
  "maxPosts": 25,
  "resultType": "top",
  "includeReplies": false,
  "includeReposts": false
}
```

| Input | Required | What it does |
| --- | --- | --- |
| `mode` | Yes | `profile`, `post`, `search`, or `hashtag` |
| `profileUsernames` | For profile mode | One or more usernames, without the @ |
| `postUrls` | For post mode | One or more direct threads.com post URLs |
| `searchQuery` | For search mode | Keyword to search. Single words work best, see the limitations below |
| `hashtag` | For hashtag mode | Tag to pull, without the # |
| `maxPosts` | No | Caps how many posts are returned, which is how you cap cost |
| `resultType` | No | `recent` or `top`, applies to search and hashtag modes |
| `broadenPhrase` | No | Search mode: if Threads has no posts for your whole phrase, fall back to its single words. On by default; rows found this way carry `matched_term` |
| `includeReplies` | No | Include reply posts, excluded by default |
| `includeReposts` | No | Include repost posts, excluded by default |
| `monitorMode` | No | Deliver and charge only posts not seen in a prior run, see Monitor mode below |

### 📤 What data do you get back?

```json
[
  {
    "post_id": "3937491905269768921",
    "code": "DakyAavlKLZ",
    "url": "https://www.threads.com/@zuck/post/DakyAavlKLZ",
    "username": "zuck",
    "user_full_name": "Mark Zuckerberg",
    "user_pic_url": "https://scontent-iad6-1.cdninstagram.com/v/t51.82787-19/550174606_17925811725103224_8363667901743352243_n.jpg...",
    "user_verified": true,
    "text": "Today we're releasing Muse Spark 1.1, a strong agentic and coding model at a very low price. It's available through our new Meta Model API and in Meta AI.",
    "posted_at": "2026-07-09T14:00:34.000Z",
    "posted_at_human": "Thu, 09 Jul 2026 14:00:34 GMT",
    "like_count": 2766,
    "reply_count": 617,
    "repost_count": 189,
    "quote_count": 62,
    "has_media": false,
    "media_urls": [],
    "media_type": "text",
    "is_reply": false,
    "is_repost": false,
    "hashtags": [],
    "mentions": [],
    "urls": [],
    "scraped_at": "2026-07-15T04:18:09.233Z"
  },
  {
    "post_id": "3938102254417839213",
    "code": "DalR2qzoP9n",
    "url": "https://www.threads.com/@aibuilds_dev/post/DalR2qzoP9n",
    "username": "aibuilds_dev",
    "user_full_name": "AI Builds",
    "user_pic_url": "https://scontent-iad6-1.cdninstagram.com/v/t51.82787-19/412201558_17902200018103224_5921147736203352243_n.jpg...",
    "user_verified": false,
    "text": "Been testing Muse Spark 1.1 all morning, latency is noticeably better than the last release. Full notes here: https://ai-builds.dev/muse-spark-1-1 #ai #llm cc @zuck",
    "posted_at": "2026-07-09T16:42:11.000Z",
    "posted_at_human": "Thu, 09 Jul 2026 16:42:11 GMT",
    "like_count": 84,
    "reply_count": 6,
    "repost_count": 2,
    "quote_count": 0,
    "has_media": true,
    "media_urls": ["https://scontent-iad6-1.cdninstagram.com/v/t51.2885-15/442019873_17902200105103224_o.jpg..."],
    "media_type": "image",
    "is_reply": true,
    "parent_post_id": "3937491905269768921",
    "is_repost": false,
    "hashtags": ["ai", "llm"],
    "mentions": ["zuck"],
    "urls": ["https://ai-builds.dev/muse-spark-1-1"],
    "scraped_at": "2026-07-15T04:18:11.885Z"
  },
  {
    "post_id": "3938340871122098765",
    "code": "DalXk93hQmT",
    "url": "https://www.threads.com/@ml_weekly/post/DalXk93hQmT",
    "username": "ml_weekly",
    "user_full_name": "ML Weekly Digest",
    "user_pic_url": "https://scontent-iad6-1.cdninstagram.com/v/t51.82787-19/398120441_17899044518103224_2241536203352243_n.jpg...",
    "user_verified": false,
    "text": "",
    "posted_at": "2026-07-09T18:05:47.000Z",
    "posted_at_human": "Thu, 09 Jul 2026 18:05:47 GMT",
    "like_count": 31,
    "reply_count": 1,
    "repost_count": 0,
    "quote_count": 0,
    "has_media": false,
    "media_urls": [],
    "media_type": "text",
    "is_reply": false,
    "is_repost": true,
    "original_post_id": "3937491905269768921",
    "hashtags": [],
    "mentions": [],
    "urls": [],
    "scraped_at": "2026-07-15T04:18:13.401Z"
  }
]
```

A real run returns an array of records, one per post. The first record above was captured live from a real public Threads post by `@zuck`. The next two show the same schema for a reply and a repost, since a single scrape rarely returns both variants at once: notice `parent_post_id` on the reply and `original_post_id` on the repost, the two fields a single-post sample can't demonstrate.

Every post record contains these fields:

| Field | Description |
| --- | --- |
| 🆔 `post_id` | Numeric Threads post ID |
| 🔡 `code` | Post shortcode used in the URL |
| 🌐 `url` | Canonical threads.com post URL |
| 👤 `username` | Author username without the @ |
| 🏷️ `user_full_name` | Author display name |
| ✅ `user_verified` | True if the author is verified |
| 📷 `user_pic_url` | Author profile picture URL |
| 📝 `text` | Full post text |
| 🕒 `posted_at` | ISO timestamp of publication |
| 🕓 `posted_at_human` | Human readable publication timestamp |
| ❤️ `like_count` | Number of likes |
| 💬 `reply_count` | Number of replies |
| 🔁 `repost_count` | Number of reposts |
| 🗨️ `quote_count` | Number of quote posts |
| 🖼️ `media_type` | image, video, carousel, or text |
| 📎 `has_media` | True when media is attached |
| 🎞️ `media_urls[]` | Array of media file URLs |
| #️⃣ `hashtags[]` | Hashtags extracted from the text |
| @ `mentions[]` | Mentions extracted from the text |
| 🔗 `urls[]` | Plain URLs extracted from the text |
| ↩️ `is_reply` | True if this post is a reply |
| 🔂 `is_repost` | True if this post is a repost |
| 🧵 `parent_post_id` | Parent post ID for replies |
| 🧬 `original_post_id` | Original post ID for reposts |
| 🕓 `scraped_at` | ISO timestamp of when the record was captured |

Every run also ends with a final `_type: "info"` record. It is informational only and never billed.

### What are the limitations?

Worth knowing before you buy, so there are no surprises:

- **Profile mode returns only about 4 to 5 posts per username.** That is Threads' own logged-out limit, not a cap we set, verified repeatedly against live profiles. List more usernames rather than raising `maxPosts` for a bigger total.
- **Search and hashtag surfaces can intermittently come back with no post data embedded at all**, a platform-side condition on Threads' logged-out pages, not a broken query. The actor detects this after a single request and stops rather than paying for retries into the same empty response; profile and post modes are unaffected. If a search or hashtag run comes back empty, retry after a short wait before assuming your query was too narrow.
- **Many multi word phrases return nothing on Threads itself.** Logged out Threads search matches phrases closely. On 13 Oct 2026 "sangeet outfit" and "kurta styling" returned no posts on Threads while "sangeet" and "kurta" returned 20 or more. By default the actor falls back to the single words (`broadenPhrase`) and tags those rows with `matched_term`. An empty search is never charged.
- **No official Threads API exists.** This actor reads the same public HTML a logged-out browser gets, so a front-end change on Meta's side can require an update on ours. We watch for that; it is not something you need to monitor yourself.
- **Media URLs point to Meta's CDN and expire.** Download anything you need to keep promptly rather than storing the link long-term.
- **Public post data only.** No DMs, no analytics dashboard metrics, no data behind a login wall.

### 💼 What can you use it for?

**Brand and topic monitoring**
Search Threads for your brand name or a keyword and capture every public post that mentions it.
Flag spikes in conversation volume before they turn into a PR moment.

**Trend and hashtag tracking**
Pull the recent or top feed for any tag to see what is gaining traction.
Compare hashtag velocity across days to spot early breakouts.

**Competitor and creator watch**
Snapshot a rival account's latest posts with full engagement metrics.
Build a rolling dataset of which formats and hooks perform best on Threads.

**AI training and sentiment analysis**
Assemble structured Threads corpora for LLM fine-tuning or sentiment scoring.
Feed live posts into a Claude, ChatGPT, or MCP agent for summarization and classification.

### 🚀 How do I get started?

1. Open the actor and pick a Mode: Profile posts, Post detail, Search query, or Hashtag feed.
2. Fill in the matching field: usernames, post URLs, search text, or hashtag (without the #).
3. Set Max posts to cap the run size, and optionally toggle Include replies or Include reposts.
4. For search and hashtag modes, pick Result type: Recent or Top.
5. Click Save & Start, then download as JSON, CSV, or Excel, or pull via API or MCP.

### 🔁 Run on a schedule

Turn this from a one-off pull into a standing feed with Apify's built-in Schedules. No code, no cron server of your own.

1. Run the actor once with the input you want repeated, then click **Save as a task** (top of the run form). This keeps your exact input attached for every future run.
2. In the Apify Console, go to **Schedules** (left sidebar) → **Create new**.
3. Name it, set your timezone, and pick a frequency: a preset (hourly, daily or weekly) or a custom cron expression (e.g. `0 6 * * *` for daily at 6am).
4. Under **Actors or tasks to run**, add the task you saved in step 1.
5. Save. From then on it runs unattended on your schedule, billed the same pay-per-post way as a manual run. Nothing is charged just for the schedule existing.

Prefer to automate the setup itself? Same thing via the API:

```bash
curl -X POST "https://api.apify.com/v2/schedules?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "threads-scraper-daily",
    "cronExpression": "0 6 * * *",
    "isEnabled": true,
    "actions": [{ "type": "RUN_ACTOR", "actorId": "themineworks/threads-scraper" }]
  }'
```

Full options for time zones, run notifications and pausing a schedule are in Apify's [Schedules documentation](https://docs.apify.com/platform/schedules).

#### Monitor mode: pay only for NEW posts

Set `monitorMode: true` and this actor remembers what it delivered last time (keyed on
`post_id`). On the next scheduled run, only genuinely new posts are pushed and charged.
Re-running the same input daily costs you for the new posts each day plus the $0.002 run
fee. From 22 October 2026 a run with nothing new costs nothing.

```json
{ "monitorMode": true }
```

Pairs directly with **Run on a schedule** above: save a task with `monitorMode: true`,
attach it to a daily schedule, and you have a standing "what's new" feed with no
duplicate charges. The run summary reports `new_this_run` and `skipped_duplicates` so
you can see the dedup working. First run establishes the baseline (everything is "new");
every run after that is incremental.

The summary lives in the run's **OUTPUT** record (Storage → Key-value store →
`OUTPUT`), not in the dataset, so your dataset holds posts and nothing else. It
carries `posts_scraped`, `charged_for`, and the per-window fan-out yield.

### Is scraping Threads legal?

This actor reads only public Threads content, the same posts a logged-out visitor already sees on threads.com. It does not log in, does not use cookies, and does not access anything behind an authentication wall. That said, this is general information and not legal advice. Public post data can still include personal data, so you remain responsible for your own compliance with GDPR, CCPA, and any other law that applies to how you use the data.

### FAQ

**Does Threads have an official API?**
No. Meta has not released a public Threads API, which is why this scraper exists. It reads only the public data a logged-out visitor sees on threads.com.

**How am I charged?**
Pay per post, plus a $0.002 run fee. From 22 October 2026 the run fee applies only to runs that deliver at least one post, and runs that return zero posts cost nothing.

**Can I include replies and reposts?**
Yes. Both are excluded by default. Toggle Include replies and Include reposts to add them, then use the `is_reply` and `is_repost` flags to sort or filter downstream.

**Which input modes are supported?**
Four: profile (a user's recent posts, about 4 to 5 per username because that is all Threads shows a logged-out visitor, so add more usernames for a higher total), post (one or more post URLs with detail), search (posts matching a keyword, the fastest single-query route to volume), and hashtag (a tag's recent or top feed).

**Is it legal to scrape Threads?**
The actor collects only publicly available posts and never accesses private accounts. Public data can still include personal data under laws like the GDPR, so scrape only what you have a legitimate reason to use.

**Is there a Threads API in 2026?**
Meta has still not shipped a public read API for Threads. Third-party options are scrapers like this one, which read only what a logged-out visitor sees on threads.com.

**Can I monitor a keyword on Threads over time?**
Yes. Save a search-mode input and put it on a schedule (see "Run on a schedule"). Each run appends new posts to your dataset, and the in-run recency windows catch posts that appear while it runs.

**What's the cheapest way to get Threads data?**
Compare total cost, not just the headline rate. This actor charges $1.20 per 1,000 posts on Gold and above plus a $0.002 run fee, with no minimum. From 22 October 2026 that fee applies only to runs that deliver posts, so empty and failed runs cost nothing. A cheaper per-post rate with a start fee on every run usually costs more on a scheduled monitor. If a competitor is genuinely cheaper for your workload, use them.

**Can I use Threads Scraper through an MCP server?**
Yes. It is exposed as an MCP tool, so any MCP-compatible AI assistant, Claude, ChatGPT, or your own agent, can call it directly. See "Use from Claude, ChatGPT & any MCP agent" above for the paste-ready prompt and setup.

### 🤖 Use from Claude, ChatGPT & any MCP agent

Hosted MCP endpoint, no install, OAuth on first connect:

```
https://mcp.apify.com/?tools=themineworks/threads-scraper
```

Claude Desktop / Cursor config (token auth):

```json
{
  "mcpServers": {
    "threads": {
      "url": "https://mcp.apify.com/?tools=themineworks/threads-scraper",
      "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
    }
  }
}
```

Things an agent can ask for once connected:

- "Search Threads for posts about \[topic] from the last week and summarize the sentiment."
- "Pull the last 25 posts from @nasa and tell me which ones got the most engagement."
- "Track posts mentioning \[my brand] on Threads and flag anything that reads negative."

For multi-platform social research in one connection (Threads + Reddit + X + YouTube + Google Trends + news), use our [Social & Market Research MCP server](https://apify.com/themineworks/social-research-mcp?fpr=ymnoit\&utm_source=apify-readme\&utm_medium=referral), eight tools behind one endpoint.

#### Copy this into your AI assistant

Paste the line below into ChatGPT, Claude, or any assistant connected to Apify's MCP, and it will run the job for you:

> Use the Apify actor themineworks/threads-scraper to pull the last 25 posts tagged #ai on Threads, sorted by top. Return the results as a table.

Or call it programmatically with the Apify client:

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });

const run = await client.actor('themineworks/threads-scraper').call({
  mode: 'hashtag',
  hashtag: 'ai',
  maxPosts: 25,
  resultType: 'top',
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### 🛠️ Complete your social listening pipeline

Threads is one channel. Add the others in the same suite:

- **[Instagram Profile Scraper](https://apify.com/themineworks/instagram-profile-scraper?fpr=ymnoit\&utm_source=apify-readme\&utm_medium=referral)**: followers, bio, and stats for any public account. Threads and Instagram share usernames.
- **[Twitter / X Scraper](https://apify.com/themineworks/twitter-x-scraper?fpr=ymnoit\&utm_source=apify-readme\&utm_medium=referral)**: tweets by keyword or handle without a paid API key.
- **[Reddit Scraper](https://apify.com/themineworks/reddit-scraper?fpr=ymnoit\&utm_source=apify-readme\&utm_medium=referral)**: posts, comments, and subreddit search with full comment trees.

Typical flow: search Threads for a brand or keyword, cross-check the same handle on Instagram and X, then pull Reddit for longer-form conversation.

Found this useful? A quick Store review helps other buyers find it. Apify ranks Store search partly by review count, so it's one of the few levers a small team has against much bigger competitors selling the same thing.

Found a bug or have a feature request? Open an issue on the actor's Apify Console page or reach out through the Apify profile.

# Actor input Schema

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

Which type of scrape to run. IMPORTANT for volume: search and hashtag mode return posts at scale (tens to hundreds per run). Profile mode returns only the handful of recent posts Threads exposes to a logged-out visitor, typically 4 to 5 per profile, regardless of how high you set Max posts. If you need many posts about a person or topic, use search.

## `profileUsernames` (type: `array`):

Threads usernames (without @) to scrape, used when mode=profile. Example: \["zuck", "mosseri"]. Expect roughly 4 to 5 recent posts PER USERNAME: that is Threads' own logged-out limit, not a cap we set, and one profile's timeline stops returning new posts beyond it. To reach a higher maxPosts, add MORE usernames rather than raising maxPosts alone — the run pulls from each in turn and keeps going until the total is met or the list runs out. A username that returns nothing (blocked, private, wrong handle) is skipped, not fatal to the run. Verified live: 6 usernames, maxPosts=20, returned exactly 20, spread 5/4/4/4/2/1 across 5 of the 6 (one returned nothing).

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

List of full Threads post URLs to scrape. Used when mode=post. Example: \["https://www.threads.net/@zuck/post/Cuc1234"]

## `searchQuery` (type: `string`):

Search keyword. Used when mode=search. Single keywords work best: logged out Threads search matches phrases closely, so many two and three word phrases return nothing. By default the actor then falls back to the single words (broadenPhrase).

## `hashtag` (type: `string`):

Hashtag to scrape, without the leading #. Used when mode=hashtag.

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

Maximum posts to return across the whole run. This is a ceiling, not a target: the actor stops as soon as it has enough or every target is exhausted. In search and hashtag mode a high value is usually reached from one query. In profile mode, ONE username caps around 4 to 5 posts (Threads' own logged-out limit) — to actually reach a high maxPosts here, list several usernames in Profile usernames above; the run draws from each until the total is met. You are never charged for posts that do not exist.

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

If true, include reply posts in the output.

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

If true, include reposts in the output.

## `includeTagWindow` (type: `boolean`):

Search mode also reads the tag feed for the query, which can surface posts that carry the tag without spelling out the words. Posts that do not mention the tag are dropped and never charged, so this is safe to leave on. Turn it OFF if you want results only from the search windows for your exact query.

## `broadenPhrase` (type: `boolean`):

On by default. If Threads has no posts for your whole phrase, the actor searches each of its words instead (filler words like 'the' and 'for' are skipped) and returns those posts. Each such row carries matched_term (the word that found it) and searched_phrase, so you can tell them apart; they are charged like any other post. Turn it off to get only posts Threads matches to the exact phrase.

## `resultType` (type: `string`):

For search and hashtag modes: 'recent' or 'top'.

## `monitorMode` (type: `boolean`):

Run on a schedule and deliver ONLY results not seen in a previous run. You are charged per new result, not for the whole feed each time. Ideal for daily monitoring. Requires the same input across runs.

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

Apify proxy. Defaults to datacenter with a fresh rotating session per request, which clears Threads' IP throttling. Switch to residential only if you see persistent blocks.

## Actor input object example

```json
{
  "mode": "profile",
  "profileUsernames": [
    "zuck"
  ],
  "postUrls": [],
  "searchQuery": "",
  "hashtag": "",
  "maxPosts": 25,
  "includeReplies": false,
  "includeReposts": false,
  "includeTagWindow": true,
  "broadenPhrase": true,
  "resultType": "recent",
  "monitorMode": false,
  "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 = {
    "profileUsernames": [
        "zuck"
    ],
    "maxPosts": 25,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("themineworks/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 = {
    "profileUsernames": ["zuck"],
    "maxPosts": 25,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("themineworks/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 '{
  "profileUsernames": [
    "zuck"
  ],
  "maxPosts": 25,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call themineworks/threads-scraper --silent --output-dataset

```

## MCP server setup

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