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

Threads scraper for Meta Threads: posts, replies, reposts, profiles and keyword or hashtag search with no login and no cookies. The free plan gets the full result. Flat rows with engagement, views, media, links and bio contacts. Monitoring returns only new posts. Export, API, schedules, AI agents.

- **URL**: https://apify.com/lergassy/threads-scraper.md
- **Developed by:** [Matvey](https://apify.com/lergassy) (community)
- **Categories:** Social media, Lead generation, Agents
- **Features:** Limited permissions
- **Stats:** 497 total users, 282 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: 4.93 out of 5 stars

## Pricing

from $1.40 / 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** turns Meta's Threads into clean rows without a login: **keyword and hashtag search with language, engagement and date filters**, an account's **posts, replies and reposts** as far back as you want, **whole threads with their replies**, **profiles** with follower count, bio links and contacts, and **account discovery** by keyword. Every post carries text, time, author, **views**, likes, replies, reposts, quotes, shares, media URLs, links, mentions, hashtags, the quoted or reposted post and the reply context. **One post is billed once, error rows are free, normal runs have no start fee** — $2 per 1,000 posts, and the number of rows you asked for is the number you get. **The Apify free plan gets the full result too**: no cookies, no paid-only login pool — on 8 November 2026 the keyword `ai agents` returned 75 posts here, while another popular Threads scraper returned 5 on the free plan.

![One run of Threads Scraper: an account's posts with text, engagement and dates, no login.](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/threads-scraper/threads-scraper-output-table.png)

![Watch the demo: 633 Threads posts scraped in 55 seconds, no login](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/threads-scraper/threads-scraper-video.png)

*One run, start to finish: three accounts in, 633 rows out in 55 seconds for $1.27. [Watch the 47-second demo](https://youtu.be/E20gP9vhT5Q).*

Paste usernames, keywords or URLs, click Start, and download JSON, CSV or Excel — or pull the rows through the Apify API into Google Sheets, n8n, Make, Zapier, a database or an AI agent.

### Who this is not for

Said up front so you do not pay to find out:

- **Private accounts.** They return a free error row, not data. Only what Threads shows a logged-out visitor is available.
- **Followers and following lists.** Threads does not make them public, so we do not offer them — and do not guess them.
- **Posting, replying, liking or messaging.** This reads Threads; it never writes to it and never touches your account.
- **Anything behind a login.** No cookies, no account, no session of yours is used. That is why a run can never get your account restricted — and also why logged-in-only data is out of reach.

If what you need is a public account's posts or a public search, with engagement counts and dates — that is exactly what this is.

### Tested head-to-head — 26 September 2026

![Threads Scraper tested against the six most-used Threads scrapers: 20 of 20 posts returned, 20 of 20 search results mention the keyword, $2 per 1,000 posts](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/benchmarks/threads-tested-2026-09-26.png)

The same requests went to this Actor and to the six most-used Threads scrapers on the Apify Store, from an ordinary paying account: the 20 latest posts of @zuck, and 20 posts for the keyword `coffee`.

| | This Actor | The six others |
|---|---|---|
| Posts of @zuck returned (20 asked) | **20** | 5–20 — four of six returned 8 or fewer |
| Search: posts that actually mention "coffee" | **20 of 20** | 11–17 of 20 (three tested) |
| A post with its replies (20 asked) | **20 replies + the post** | — |
| Start fee per run | **None** (Monitoring runs: $0.005) | most charge one |
| Cost per 1,000 posts delivered | **$2.00** | $2.10–7.86 |

### What is Threads Scraper?

Threads has an official API only for your own account. This Actor is a **Threads API alternative** for everyone who needs the network as data rather than as a feed: social listening and brand monitoring, creator and competitor research, lead generation from bios, trend and sentiment analysis, archiving, academic research, and AI agents asked "what is this account posting" or "what are people saying about X". It reads the public pages Threads serves to any visitor, so it works for any public account and any public post, needs **no account, no cookies and no proxy of your own**, and can never get an account of yours blocked.

Seven modes, one row shape:

| Mode | You give | You get |
|---|---|---|
| **Search** | keywords, #hashtags | public posts from the top, recent and tag result pages, expanded across wordings and deduplicated, then filtered by keyword, language, likes, replies, views and date |
| **Posts** | usernames | the account's own posts, paged through the whole profile (hundreds or thousands), optionally within a date window |
| **Replies** | usernames | replies the account wrote, each with the post it answered |
| **Reposts** | usernames | posts the account reposted |
| **Thread** | post URLs | the post with its self-thread and its replies, each reply linked to the parent |
| **Profile** | usernames | followers, full name, bio, bio links, e-mails and phones found in the bio, verification, latest post |
| **Accounts** | keywords | people and brands matching a keyword, optionally with full profile details |

### What other Threads scrapers get wrong — and what this one does instead

The reviews and issue trackers of the popular Threads scrapers on Apify repeat the same seven complaints. Each one is a design decision here.

| Complaint on other actors | Threads Scraper |
|---|---|
| "Search returns a ton of non-English content with 0 engagement — fairly useless" | `languages`, `minLikes`, `minReplies`, `minViews` filters; every row carries `languageGuess` and `containsKeyword` |
| "Many posts don't contain the keyword I entered" | `strictKeywordMatch` keeps only posts whose text or hashtags contain your keyword |
| "Duplicated results", "same post returned and billed multiple times" | Posts are deduplicated by id inside a run; Monitoring mode deduplicates across runs. One post, one charge |
| "Only got 25 results", "maxItems not honored" | Query expansion collects several result pages per keyword (3–4× more unique posts, measured); `maxPosts` and `maxItems` are hard caps |
| "No date range filter", "so much duplication to deal with on repeated runs" | `postedAfter` / `postedBefore` window, plus Monitoring mode that returns only posts never delivered before |
| "view_count, reply_count, repost_count always 0", "is_reply always false", "no link to the thread" | `url`, stable numeric `userId`, `isReply`, `replyToUserId`, `parentPostId`, `replyCount`, `repostCount`, `quoteCount`, `shareCount` and `viewCount` on every row |
| "150 failed runs at $0.02 each for profiles that don't exist", "expensive", start fees | No start fee. A profile that does not exist is a free error row. $2 per 1,000 posts against $2.50–5 plus $0.02–0.05 per run elsewhere |

### What data does Threads Scraper extract?

#### Post rows

| Field | Example |
|---|---|
| `id`, `code`, `url` | 3977518562965884838 · Dcy_A8pGo-m · https://www.threads.net/@zuck/post/Dcy_A8pGo-m |
| `text`, `language`, `languageGuess` | "Muse Spark 1.3 is rolling out today…" · en · en |
| `createdAt`, `timestamp` | 2026-09-02T19:26:23.000Z · 1788377183 |
| `username`, `userId`, `fullName`, `isVerified`, `profilePicUrl`, `profileUrl` | zuck · 63055343223 · Mark Zuckerberg · true · … |
| `likeCount`, `replyCount`, `repostCount`, `quoteCount`, `shareCount`, `viewCount` | 1453 · 196 · 148 · 28 · 78 · 160646 |
| `mediaType`, `imageUrl`, `videoUrl`, `images[]`, `videos[]`, `hasAudio`, `accessibilityCaption` | carousel · … · \[{url,width,height,alt}] · \[{url,thumbnail,hasAudio}] |
| `hashtags`, `mentions`, `urls`, `linkPreview` | \["ai"] · \["natgeo"] · \["https://…"] · {url, displayUrl, title, imageUrl, faviconUrl} |
| `isReply`, `replyToUsername`, `replyToUserId`, `rootPostUsername`, `replyControl` | true · zuck · 63055343223 · everyone |
| `isQuote`, `quotedPost`, `isRepost`, `repostedPost`, `repostedBy` | {code, username, url, text, likeCount} |
| `isPinned`, `isEdited`, `isPaidPartnership`, `isSpoiler`, `likesHidden`, `location` | booleans · place name |
| `threadId`, `positionInThread`, `threadLength` | a multi-post thread keeps its order |
| `containsKeyword`, `searchVariant`, `query`, `source` | true · #aiagents · AI agents · search-top / search-recent / search-tag / posts / thread / reply / replies / reply-context / reposts |
| `sourceUsername`, `parentPostId`, `parentPostUrl` | which input produced the row; the post a reply answers |

#### Profile and account rows

`username`, `userId`, `fullName`, `biography`, `bioLinks`, `websiteUrl`, `emails`, `phones` (extracted from the bio), `followerCount`, `isVerified`, `isPrivate`, `profilePicUrl` (HD), `profileUrl`, `instagramUrl`, `latestPostAt`, `latestPostUrl`, `latestPostText` (Profile mode), `query` (Accounts mode).

Every row has `type` (`post`, `profile`, `user` or `error`) and `scrapedAt`. An account or post that cannot be read arrives as an `error` row with the reason — `profile not found`, `post not found or unavailable`, `rate limit` — never as a silently missing item, and never on the bill.

![Every field Threads Scraper returns for a post: text, author, timestamps, likes, replies, reposts, quotes, shares, media type and URLs](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/threads-scraper/threads-scraper-posts-all-fields.png)

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

Pay per row, no subscription, no start fee on normal runs, no charge for errors:

| Row | Price | For |
|---|---|---|
| **Post** | **$0.002** — 1,000 posts for $2 | every post row in every mode, replies included |
| **Profile** | $0.004 | Profile mode, `includeProfile`, Accounts mode with details |
| **Account match** | $0.002 | Accounts mode without details |
| **Monitoring run** | $0.005 per run (from 23 November 2026) | once per run with `monitoringStoreName` set, also when nothing new is found — an hourly watch is about $3.60 a month before rows |

There is no browser in this Actor, so platform usage is close to zero: an account's last 1,000 posts cost $2 and about 40 requests; 359 filtered search posts cost $0.72 and 55 seconds. On the **Apify free plan ($5 of credit a month)** that is about 2,500 posts every month for free. Paid Apify plans get 10–30 % off the row price.

For comparison, on 14 September 2026 the five most-used Threads scrapers on Apify charged $2.50, $5.00, $20.00, $5.00 and $2.50 per 1,000 posts — four of them with a $0.02 fee per run on top, so a scheduled hourly watch pays the fee 720 times a month before it collects a single post. Set **Maximum items** and a run can never cost more than that number × $0.002.

#### Bulk export: what 50,000 posts actually cost

This Actor is built for bulk jobs — put hundreds of inputs into one run, or call it from the API on
a schedule. There is **no fee per normal run, no fee per page and no proxy charge** (only Monitoring runs
carry $0.005 each): you pay for the rows you keep, and error rows are free. That is what decides the bill once you pull a whole market
rather than a single account.

| Job | This Actor | Most-used Actor in this category |
|---|---|---|
| 50,000 posts across 100 runs | **$100** | $133 |

Checked on the Apify Store on 24 September 2026 against the Actor with the most monthly users in
this category: $2.50 per 1,000 posts plus a $0.02 start fee for every GB of run memory — $0.08 a run
at its default 4 GB. Its per-run fee is counted in, as ours would be if we charged one. Some Actors here
ask less per row — this table compares against the one buyers actually use most.

### Switching from another Threads Actor

Measured on 9 October 2026 from Apify's own public Store data. The comparison is with the most-used Threads Actor on the Store (most users in the last 30 days); it is not named here on purpose — the numbers are public and checkable, the name adds nothing.

| | The most-used Threads Actor | This Actor |
|---|---|---|
| Price per 1,000 posts, free plan | $2.50 | **$2.00** |
| Price per 1,000 posts, Gold plan | $2.50 | **$1.40** |
| Start fee per run | $0.02 per GB of run memory — $0.08 at its default 4 GB (paid plans $0.005 per GB) | **none** (Monitoring runs: $0.005) |
| Runs in the last 30 days that ended SUCCEEDED | 98.1% of 77,711 | **99.7%** of 7,924 |

Read the last row honestly in both directions: both are Apify's public 30-day counts and include runs that failed because of the caller's own input, and their sample is ten times larger than ours. The other Actor also has fields we do not have — poll options and votes, music on a post, sticker data — so if you rely on those, check our output table before you switch.

#### Input mapping

| Their input | This Actor | Note |
|---|---|---|
| `mode`: `user` | `mode`: `posts` | |
| `mode`: `user_replies` / `user_reposts` | `mode`: `replies` / `reposts` | |
| `mode`: `search` | `mode`: `search` | |
| `mode`: `profiles` | `mode`: `users` | up to 60 accounts per keyword (`maxUsersPerKeyword`) |
| `usernames` | `usernames` | bare name, `@name` or the profile URL |
| `keywords` | `keywords` | |
| `max_posts` | `maxPosts` | a cap per username or keyword in both; ours starts at 1, not 10 |
| `search_filter`: `top` / `recent` | `searchTypes`: `["top"]` / `["recent"]` | ours combines `top`, `recent` and `tags` by default, duplicates removed |
| `search_language` | `languages` | ours filters by ISO code: `en`, `es`, `ja`… |
| `start_date` / `end_date` | `postedAfter` / `postedBefore` | `YYYY-MM-DD` only, no "7 days"; ours applies to Posts mode too, not just search |

#### Output mapping

| Their field | This Actor |
|---|---|
| `username`, `user_id`, `display_name` | `username`, `userId`, `fullName` |
| `post_url`, `post_code` | `url`, `code` |
| `text_content` | `text` |
| `created_at` / `created_at_timestamp` | `createdAt` / `timestamp` |
| `like_count`, `reply_count`, `repost_count`, `quote_count`, `share_count`, `view_count` | `likeCount`, `replyCount`, `repostCount`, `quoteCount`, `shareCount`, `viewCount` |
| `media_type`, `media_urls` | `mediaType`, `images` / `videos` |
| `hashtags`, `mentions`, `urls` | `hashtags`, `mentions`, `urls` |
| `is_pinned`, `is_edited`, `is_paid_partnership`, `is_spoiler` | `isPinned`, `isEdited`, `isPaidPartnership`, `isSpoiler` |
| `is_reply`, `reply_to_username`, `is_quote_post`, `is_repost` | `isReply`, `replyToUsername`, `isQuote`, `isRepost` |
| `is_verified`, `profile_url`, `profile_pic_url` | `isVerified`, `profileUrl`, `profilePicUrl` |
| `followers_count`, `bio`, `bio_links`, `emails`, `phones` | `followerCount`, `biography`, `bioLinks`, `emails`, `phones` (profile rows) |
| `search_keyword` | `query` |
| `scraped_at` | `scrapedAt` |

Their field names are taken from their published dataset schema (build 0.0.403, 9 October 2026), not from a run of theirs.

### How to scrape Threads with Threads Scraper

1. **Pick a mode** — Search for keywords and hashtags; Posts, Replies, Reposts or Profile for accounts; Thread for a post URL; Accounts to find people.
2. **Paste inputs** — usernames (`zuck`, `@natgeo` or the profile URL), keywords (`AI agents`, `#nba`), or post URLs. Any Threads URL also works in **Start URLs** and is routed to the right mode.
3. **Set the cap** — `maxPosts` per username or keyword, `maxItems` for the whole run. Both are exact.
4. **Add filters if you need them** — languages, minimum likes / replies / views, a date window, `strictKeywordMatch` for search.
5. **Start.** Rows appear in the dataset as they are collected; download them as JSON, CSV, Excel or HTML, or read them through the API.

![Threads Scraper input form: mode, usernames, keywords, post URLs, filters and caps.](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/threads-scraper/threads-scraper-input-form.png)

### ⬇️ Input

The last 200 posts of two accounts, with their profiles:

```json
{
    "mode": "posts",
    "usernames": ["zuck", "natgeo"],
    "maxPosts": 200,
    "includeProfile": true
}
```

Brand monitoring done properly — English posts that actually mention the brand and have at least one like, several pages per keyword:

```json
{
    "mode": "search",
    "keywords": ["Notion", "Obsidian app"],
    "maxPosts": 300,
    "strictKeywordMatch": true,
    "languages": ["en"],
    "minLikes": 1,
    "expandQueries": true
}
```

The same watch on a daily schedule, returning only what is new since the last run:

```json
{
    "mode": "search",
    "keywords": ["AI agents"],
    "maxPosts": 300,
    "languages": ["en"],
    "strictKeywordMatch": true,
    "monitoringStoreName": "ai-agents-watch"
}
```

A post with its replies:

```json
{
    "mode": "thread",
    "postUrls": ["https://www.threads.net/@zuck/post/Dcy_A8pGo-m"],
    "maxRepliesPerPost": 100
}
```

Posts from a quarter, about one topic:

```json
{
    "mode": "posts",
    "usernames": ["zuck"],
    "postedAfter": "2026-06-01",
    "postedBefore": "2026-08-31",
    "filterKeywords": ["AI", "Llama"]
}
```

Find accounts and their contacts:

```json
{
    "mode": "users",
    "keywords": ["coffee roaster brooklyn"],
    "maxUsersPerKeyword": 30,
    "includeProfileDetails": true
}
```

#### Every input field

| Field | Type | What it does |
|---|---|---|
| `mode` | `posts` · `replies` · `reposts` · `profile` · `search` · `thread` · `users` | Which job to run |
| `usernames` | list | Accounts for Posts, Replies, Reposts and Profile — bare name, with @, or profile URL |
| `keywords` | list | Words or #hashtags for Search and Accounts |
| `postUrls` | list | Post URLs for Thread mode (`/post/…`, `/post/…/media` and share links `threads.com/share/…` all work) |
| `startUrls` | list | Any Threads URLs — profile, post, search or tag — routed automatically |
| `maxPosts` | number | Cap per username or keyword |
| `maxRepliesPerPost` | number | Thread mode: replies per post |
| `searchTypes` | `top` · `recent` · `tags` | Which result pages to combine |
| `expandQueries` | boolean | Also search singular/plural, with and without the hash, and the joined hashtag form (default on) |
| `postedAfter`, `postedBefore` | `YYYY-MM-DD` | Date window; Posts mode stops paging once it passes the older bound |
| `filterKeywords` | list | Keep a post only when it contains any of these words |
| `strictKeywordMatch` | boolean | Search: keep only posts that contain the keyword (or every word of it) |
| `languages` | list | ISO codes: `en`, `es`, `ja`, `zh`, `ko`, `ru`… Threads' own label, else a guess from the script of the text |
| `minLikes`, `minReplies`, `minViews` | number | Engagement floors |
| `includeViews` | boolean | View count on every post, read from the post's own page (default on; off = fastest bulk runs) |
| `includeProfile` | boolean | Posts / Replies / Reposts: also save the account's profile row |
| `includeProfileDetails` | boolean | Accounts: open each profile for followers, bio links and contacts |
| `maxUsersPerKeyword` | number | Accounts mode cap |
| `maxItems` | number | Hard cap on rows for the whole run — your cost cap |
| `monitoringStoreName` | text | Name a watch; later runs with the same name return only posts never delivered before |
| `concurrency` | number | Accounts or keywords processed at once |
| `proxyConfiguration` | proxy | Apify datacenter proxy by default; switch to Residential if you ever see rate-limit error rows |

### ⬆️ Output

```json
{
    "type": "post",
    "id": "3977518562965884838",
    "code": "Dcy_A8pGo-m",
    "url": "https://www.threads.net/@zuck/post/Dcy_A8pGo-m",
    "text": "Muse Spark 1.3 is rolling out today with frontier performance almost too cheap to meter…",
    "createdAt": "2026-09-02T19:26:23.000Z",
    "timestamp": 1788377183,
    "username": "zuck",
    "userId": "63055343223",
    "fullName": "Mark Zuckerberg",
    "isVerified": true,
    "likeCount": 1453,
    "replyCount": 196,
    "repostCount": 148,
    "quoteCount": 28,
    "shareCount": 78,
    "viewCount": 160646,
    "mediaType": "image",
    "imageUrl": "https://instagram.fdps8-1.fna.fbcdn.net/v/t51.82787-15/….webp",
    "images": [{ "url": "…", "width": 1080, "height": 970, "alt": null }],
    "videos": [],
    "hashtags": [],
    "mentions": [],
    "urls": [],
    "linkPreview": null,
    "language": "en",
    "languageGuess": "en",
    "isReply": false,
    "isQuote": false,
    "isRepost": false,
    "isPinned": false,
    "threadId": "3977518562965884838",
    "positionInThread": 0,
    "threadLength": 1,
    "source": "posts",
    "sourceUsername": "zuck",
    "scrapedAt": "2026-09-14T02:48:12.000Z"
}
```

A search row adds `query` (`"AI agents"`), `searchVariant` (`"#aiagents"`), `containsKeyword` (`true`) and `source` (`"search-recent"`). A reply from Thread mode adds `parentPostId` and `parentPostUrl`. A profile row:

```json
{
    "type": "profile",
    "userId": "63055343223",
    "username": "zuck",
    "fullName": "Mark Zuckerberg",
    "biography": "…",
    "bioLinks": ["https://www.meta.com"],
    "emails": [],
    "phones": [],
    "followerCount": 7010000,
    "isVerified": true,
    "isPrivate": false,
    "profileUrl": "https://www.threads.net/@zuck",
    "instagramUrl": "https://www.instagram.com/zuck/",
    "latestPostAt": "2026-09-13T18:02:10.000Z",
    "latestPostUrl": "https://www.threads.net/@zuck/post/…"
}
```

![Posts mode output: six posts from @zuck with like, reply, repost, quote and share counts, media type and post URL](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/threads-scraper/threads-scraper-posts.png)

![The same posts as raw JSON — the exact shape an API client or AI agent receives](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/threads-scraper/threads-scraper-posts-json.png)

### Search on Threads: what a visitor can see, and how the Actor gets more

Threads shows a logged-out visitor exactly **one page of 20–25 posts per query and result type** — no cursor, no "load more", no date operators (`since:`, `until:` and `lang:` return nothing; checked 14 September 2026). Scrapers that promise thousands of search results either log in with an account — which is how accounts get blocked — or quietly return the same page again.

What Threads does allow is asking in more ways: "AI agents", "AI agent", "aiagents" and "#aiagents" return almost disjoint sets. With **Expand each keyword** on, every keyword is searched as singular and plural, with and without the hash and as a joined hashtag, across the top, recent and tag pages, and the results are deduplicated by post id. Measured on 14 September 2026: two keywords went from 106 posts (one page each) to **359 unique posts**. `searchVariant` tells you which wording found each post.

**Phrases of several words.** For most multi-word phrases — "web marketing jobs", "マーケ 転職", "кофе москва" — Threads shows a logged-out visitor an empty page, while each word alone fills one (checked 4 October 2026). When a phrase comes back empty, the Actor searches each of its words on its own and keeps only the posts that contain every word, so you get the posts that match the phrase instead of an empty run. `searchVariant` then shows the word that found the post. Fewer rows are possible this way than for a single word — shorter phrases or single keywords give more.

What it still cannot do: reach a specific week of last year for a keyword. For that, use Posts mode on the accounts that matter with a date window, or run a Monitoring watch from now on.

![Search mode: eight posts for the keyword “ai agents” with engagement counts, media and the query that found them](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/threads-scraper/threads-scraper-search.png)

![Media view of a search run: image and video URLs extracted from Threads posts, ready to download](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/threads-scraper/threads-scraper-search-media.png)

### Monitoring mode: a clean feed of new posts

Name a watch in `monitoringStoreName` and schedule the run. The first run delivers everything it finds; every later run with the same name **returns only posts never delivered before** — the ids are remembered in a named key-value store on your account. In a test, a second run five minutes after the first returned 3 new posts and skipped 30 already delivered. Nothing is repeated, nothing is billed twice, and a daily run becomes a change feed you can push into Slack, a sheet or a CRM. From 23 November 2026 each Monitoring run costs $0.005 on top of the rows it delivers — $0.15 a month for a daily watch, about $3.60 for an hourly one.

A ready schedule: create an Apify Schedule with the cron `0 * * * *` (every hour) and this input — every run is only the new posts:

```json
{
    "mode": "search",
    "keywords": ["your brand", "your competitor"],
    "strictKeywordMatch": true,
    "minLikes": 1,
    "monitoringStoreName": "brand-watch"
}
```

> **Want alerts, not a spreadsheet?** [Threads Monitor](https://apify.com/lergassy/threads-monitor) is built just for watching: it returns only new posts, sends each one straight to **Telegram, Slack, Discord or your webhook**, and you pay only for what it finds.

![Replies mode: what an account answered, with the post it replied to](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/threads-scraper/threads-scraper-replies.png)

![Reposts mode: what an account boosted, kept separate from its own posts](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/threads-scraper/threads-scraper-reposts.png)

### Use cases for Threads data

#### Brand and competitor monitoring

Search your brand, your competitors and your category terms with `strictKeywordMatch`, `languages: ["en"]` and `minLikes: 1`, on a daily schedule with Monitoring mode. Each run is the list of new, relevant, engaged mentions — ready for a sentiment pass or a Slack digest.

#### Creator and influencer research

Posts mode on a list of creators gives their whole back catalogue with engagement per post; Profile mode adds follower counts and bio links. Sort by `likeCount / followerCount` to find the accounts whose audience actually reacts.

#### Lead generation

Accounts mode finds people and businesses by keyword; with `includeProfileDetails` each row carries bio links, e-mails and phones found in the bio, and the Instagram profile URL. Feed the domains into a contact scraper for the rest.

#### Trend and sentiment analysis

Hashtag search with expansion, `languages` for the market you care about, and the `text`, `hashtags`, `likeCount` and `replyCount` columns give a dataset an LLM can summarise; `linkPreview` shows which articles the network is sharing.

#### Reply and conversation analysis

Thread mode returns a post's replies with `parentPostId`, `replyToUsername` and `positionInThread`, so a conversation tree is one join away. Replies mode shows how an account talks to others.

#### AI agents and RAG

Every row is flat and carries `url`, `userId` and `summary`-ready text, and the Actor is available through the Apify MCP server, so an agent can search a topic, read the posts and follow up on the authors in one conversation.

![Profile mode: follower counts, verified badge, biography, website, contacts and latest post date for @zuck and @mosseri](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/threads-scraper/threads-scraper-profile.png)

![Accounts mode: profiles found by the keyword “coffee roaster”, with followers, bio and contacts for influencer discovery](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/threads-scraper/threads-scraper-users.png)

### Integrations and Threads API

- **API** — `POST https://api.apify.com/v2/acts/lergassy~threads-scraper/run-sync-get-dataset-items?token=…` with the input JSON returns the rows directly; for longer runs start the run, wait for `SUCCEEDED`, then read `datasets/{id}/items?format=csv`.
- **Python / JavaScript** — `apify-client`: `client.actor("lergassy/threads-scraper").call(run_input={...})`, then `client.dataset(run["defaultDatasetId"]).list_items()`.
- **Google Sheets** — the Apify Google Sheets integration or the CSV export link; the rows are flat, and the few nested fields (`images`, `videos`, `quotedPost`, `linkPreview`) arrive as JSON text.
- **n8n, Make, Zapier** — use the Apify node or app, pick this Actor, paste an input JSON from above, map the dataset items downstream. Set `maxItems` so a big watch never overflows a scenario.
- **Webhooks and schedules** — an `ACTOR.RUN.SUCCEEDED` webhook posts the dataset link to Slack or your endpoint; Apify Schedules run the same input hourly or daily, and with Monitoring mode every run is only the new posts.
- **MCP** — connect the Apify MCP server to Claude, Cursor or any MCP client and ask for Threads data in plain language; the input fields are described for agents.

### 🤖 For AI agents and LLM apps

The Actor is tool-friendly on purpose: every mode takes plain strings, every row is flat with a stable `id` and `url`, error rows say why, and caps are exact so an agent can budget. Typical agent calls:

- "What has @zuck posted about AI in the last month?" → `mode: posts`, `usernames: ["zuck"]`, `postedAfter`, `filterKeywords: ["AI"]`.
- "What are people saying about Notion this week, English only, ignore junk?" → `mode: search`, `strictKeywordMatch`, `languages: ["en"]`, `minLikes: 1`.
- "Who are the coffee roasters in Brooklyn on Threads and how do I reach them?" → `mode: users`, `includeProfileDetails: true`.
- "Summarise the replies to this post." → `mode: thread`, `postUrls`.

### Limitations, stated plainly

- **View counts** are read from each post's own page — one extra request per post, but only the first ~20% of that page is downloaded (the part that holds the count), so it adds roughly a second per batch of posts rather than a full page each. Turn `includeViews` off for the fastest bulk runs. A post only seconds old has no view count yet; then `viewCount` is `null`, never a guess.
- **Search depth.** One page per query and result type is what Threads serves a visitor; expansion multiplies it 3–4×, not 100×. See the Search section.
- **Replies per post.** A post page shows the first ~25 replies to a visitor; `maxRepliesPerPost` above that returns what Threads shows.
- **Private accounts** return an error row. **Followers and following lists** are not public and are not offered.
- **Rate limits.** Threads throttles addresses that hammer it; the Actor rotates proxy sessions and retries, and a request that is still refused gets one more try through a residential address at our cost. If Threads keeps refusing most of a run, the Actor pauses once, then stops instead of grinding: the targets it did not get to are listed in the `NOT_ATTEMPTED` record and in the status message (not charged — run them again later). Refused targets are free `error` rows, never pretended data.
- **An empty run explains itself.** If nothing comes back, the status message says why: which of your filters removed the posts Threads returned (with counts), that a monitoring run found nothing new, or that Threads shows no posts for that account or search to a logged-out visitor. Empty runs cost nothing (a Monitoring run pays its $0.005 run fee).

### ❓ FAQ

#### Do I need a Threads account, cookies or a login?

No. Nothing to paste, nothing to log into, and no account of yours can be blocked because none is used. Actors that ask for `threadsCookies` or run "our accounts" are the ones behind the "let my Threads account be blocked" reviews.

#### Why do other scrapers return posts that don't contain my keyword — and does this one?

Threads pads keyword search with loosely related posts. This Actor marks every search row with `containsKeyword`, and `strictKeywordMatch: true` drops the rest before you pay for them.

#### Does search support Threads' Top and Recent sort?

Yes — `searchTypes` picks top, recent and tag pages; the `source` column says which page a post came from.

#### Can I get only English (or Japanese, Spanish…) posts?

`languages: ["en"]`. Threads labels only some posts with a language; for the rest the Actor reads the script of the text, and Latin-script posts count as `en`. `languageGuess` shows the value used.

#### Why did I get fewer search results than `maxPosts`?

Because Threads shows a visitor one page per query wording and result type. Turn on `expandQueries` (default), add related keywords (a phrase of several words usually yields fewer posts than its words searched separately — see the Search section), and use Monitoring mode on a schedule to accumulate over time.

#### Are results deduplicated? Will I be billed twice for one post?

Inside a run every post id is delivered once. Across runs, Monitoring mode remembers what was delivered and skips it. One post, one charge.

#### Can I filter by date?

`postedAfter` and `postedBefore` on every mode. Posts mode stops paging once it passes the older bound, so a narrow window is also cheaper.

#### How many posts can I get from one account?

As many as the profile holds — Posts mode pages through the whole history, about 25 posts per request. If Threads rotates its pagination query, the Actor re-discovers it from the site's bundles automatically.

#### Are `replyCount`, `repostCount` and `isReply` real?

Yes — read from the same data the Threads page renders. `replyToUserId`, `parentPostId` and `positionInThread` let you rebuild a thread.

#### What happens with a username that doesn't exist?

A free `error` row: `account not found`. No start fee, no charge, no 150 failed runs at $0.02 each.

#### Is it legal to scrape Threads?

The Actor reads only what Threads shows to any visitor without logging in — public posts and public profiles — and stores no private data. Scraping public data is generally lawful, but how you use it is your responsibility: respect Meta's terms where they apply to you, privacy law in your jurisdiction, and people's expectations; consult a lawyer for commercial use.

#### Can I use it with the Apify API, Python, n8n or Make? Through an MCP server?

Yes to all — see Integrations. It follows the standard run → dataset flow, and it is listed for the Apify MCP server and agentic payments.

#### What happens when Threads changes its page?

The Actor is tested automatically every day. Known query ids are re-discovered from Threads' own bundles when they rotate; a real layout change shows up as `error` rows with a reason, not as empty data, and the fix usually ships the same day. Report anything odd in the Issues tab.

### Run health

When a run ends with many error rows, the Actor sends its developer the counts (rows delivered, error types, run ID) so the cause gets fixed fast; your input and results are never sent.

### Your feedback

Something missing, a field you need, an account that misbehaves? Open an issue on the **Issues** tab — every report gets an answer, and most fixes ship within a day. If the Actor saves you time, a short review helps other buyers find it.

### You might also like

| Actor | What it does |
|---|---|
| [Threads Monitor](https://apify.com/lergassy/threads-monitor) | Only new posts from accounts and keywords you watch, with Telegram, Slack and Discord alerts |
| [Website Contact Scraper](https://apify.com/lergassy/website-contacts-api) | E-mails, phones and social profiles from the websites you found in bios |
| [Email & Phone Verifier](https://apify.com/lergassy/email-phone-verifier) | Check the contacts before the mail merge |
| [YouTube Comments Scraper](https://apify.com/lergassy/youtube-comments-scraper) | The same kind of listening on YouTube |
| [Telegram Channel Analytics](https://apify.com/lergassy/telegram-channel-analytics) | Reach and engagement of Telegram channels |
| [Trustpilot Reviews API](https://apify.com/lergassy/trustpilot-reviews-api) | What customers say about a brand, with filters |

# Changelog

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

# Actor input Schema

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

<b>Posts</b> — an account's own posts, as many as you ask for. <b>Replies</b> / <b>Reposts</b> — what an account answered or boosted. <b>Profile</b> — followers, bio, links and contacts. <b>Search</b> — public posts for keywords and hashtags. <b>Thread</b> — one post with its replies. <b>Accounts</b> — people and brands matching a keyword.

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

For Posts, Replies, Reposts and Profile. Bare name, with @, or the profile URL: <code>zuck</code>, <code>@natgeo</code>, <code>https://www.threads.net/@nasa</code>.

## `keywords` (type: `array`):

For Search and Accounts. <code>AI agents</code>, <code>#nba</code>, <code>coffee roaster brooklyn</code>. The # is optional.

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

For Thread mode: <code>https://www.threads.net/@zuck/post/Dcy_A8pGo-m</code>. Share links from the app (<code>https://www.threads.com/share/CODE</code>) work too. In Posts mode these are fetched too.

## `startUrls` (type: `array`):

Profile, post, search or tag URLs — each is routed to the matching job automatically.

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

Cap per username or keyword, not per run. Posts mode pages through the whole profile; Search returns what Threads shows a logged-out visitor (usually 20–60 posts per keyword across the top, recent and tag pages).

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

Threads shows a logged-out visitor the first ~25 replies of a post.

## `searchTypes` (type: `array`):

Search mode: which result pages to combine. Duplicates are removed.

## `expandQueries` (type: `boolean`):

Threads shows a visitor one page (20–25 posts) per query and result type, and different wordings return different posts. On, every keyword is also searched as singular/plural, with and without the hash and as a joined hashtag, in top, recent and tag order — several times more unique posts per keyword. Off searches the exact text only.

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

<code>YYYY-MM-DD</code>. Older posts are dropped and Posts mode stops paging once it reaches them.

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

<code>YYYY-MM-DD</code>. Newer posts are dropped. Together with <b>Posted after</b> this gives a date window, so a scheduled run never re-collects yesterday's posts.

## `filterKeywords` (type: `array`):

Case-insensitive words or phrases; a post is kept when it contains any of them. Empty = keep everything.

## `strictKeywordMatch` (type: `boolean`):

Search mode: keep a post only when your keyword (or every word of it) actually appears in the text or hashtags. Threads pads keyword results with loosely related posts — this switch removes them. Every row also carries <code>containsKeyword</code> so you can filter later instead.

## `languages` (type: `array`):

Keep only posts in these languages, ISO codes: <code>en</code>, <code>es</code>, <code>ja</code>, <code>zh</code>, <code>ko</code>, <code>ru</code>… Threads labels only some posts; for the rest the script of the text is used (Latin letters count as <code>en</code>). Empty = all languages.

## `minLikes` (type: `integer`):

Drop posts with fewer likes. <code>1</code> removes the zero-engagement filler that keyword search returns.

## `minReplies` (type: `integer`):

Drop posts with fewer replies.

## `minViews` (type: `integer`):

Keep only posts with at least this many views. Threads publishes a view count for very few posts when read without a login, so posts with no count are kept, not dropped. For a dependable threshold use Min likes or Min replies.

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

Posts, Replies and Reposts modes: also save the account's profile (followers, bio, links, contacts).

## `includeViews` (type: `boolean`):

Fill viewCount on every post (the number Threads shows under a post, e.g. "134K views"). Read from each post's own page: one extra request per post, no extra charge. Turn off for the fastest possible bulk runs. <b>Costs time:</b> Threads prints the view count only on the post's own page, and only to a residential visitor — so each post needs one extra page fetch of about 180 KB. Measured 28.10.2026: 20 posts take 5 s without view counts and about 33 s with them. Turn this off when you do not need views and the run is roughly seven times faster.

## `includeProfileDetails` (type: `boolean`):

Accounts mode: open each account's page for follower count, bio links and contacts (one extra request per account). <b>It also sets the price of the row:</b> with details on you pay the profile rate, with it off you pay the cheaper account rate — the row still arrives as type <code>profile</code> either way.

## `maxUsersPerKeyword` (type: `integer`):

Accounts mode.

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

Stop after this many rows in the run.

## `monitoringStoreName` (type: `string`):

Give the run a name (e.g. <code>brand-watch</code>) and every later run with the same name returns <b>only posts it has not delivered before</b>. Schedule it hourly or daily to get a clean feed of new posts with nothing repeated and nothing billed twice. From 23 November 2026 a monitoring run costs $0.005 per run on top of the rows. Empty = normal run (no run fee).

## `concurrency` (type: `integer`):

How many accounts or keywords are processed at once.

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

Apify datacenter proxies work for Threads. If runs end with rate-limit error rows, switch to Residential.

## `includeRunSummary` (type: `boolean`):

One extra free row with the run's totals: rows delivered, free rows, what was charged and how much in US dollars. It carries type "notice", so a filter on your data rows skips it.

## Actor input object example

```json
{
  "mode": "posts",
  "usernames": [
    "zuck"
  ],
  "maxPosts": 100,
  "maxRepliesPerPost": 100,
  "searchTypes": [
    "top",
    "recent",
    "tags"
  ],
  "expandQueries": true,
  "strictKeywordMatch": false,
  "languages": [],
  "minLikes": 0,
  "minReplies": 0,
  "minViews": 0,
  "includeProfile": false,
  "includeViews": true,
  "includeProfileDetails": true,
  "maxUsersPerKeyword": 20,
  "maxItems": 10000,
  "concurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "includeRunSummary": true
}
```

# Actor output Schema

## `rows` (type: `string`):

One row per post (text, author, time, likes, replies, reposts, quotes, shares, media URLs, links, mentions, hashtags, quoted or reposted post, reply context) or per profile / account (followers, bio, links, emails, phones).

## `runSummary` (type: `string`):

JSON counters for the run: rows delivered, filtered out, errors and request statistics.

## `notAttempted` (type: `string`):

JSON list of inputs the run did not reach (time limit or "Max cost per run"). Not charged — pass them as input to a new run to continue. Present only when the run stopped early.

# 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("lergassy/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("lergassy/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 lergassy/threads-scraper --silent --output-dataset

```

## MCP server setup

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