# Threads Search Scraper · Language & Engagement Filters (`thequietstack/threads-search-scraper`) Actor

Threads scraper for brand monitoring and content research: search Threads by keyword and keep only the Posts you want - language per post, minimum likes, replies or reposts, date window, required and excluded words. Posts that fail a filter are never charged.

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

## Pricing

from $2.10 / 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 Search Scraper · Language & Engagement Filters

**Search Threads by keyword and keep only the posts you actually want** - in your language, with real engagement, from your date range, without the flood of one-line replies.

Threads search mixes languages, replies and zero-like posts into every keyword. In our test on 04 Oct 2026, 13 of 19 visible posts for "vibe coding" were not in English, and 11 of 17 for "AI agents" were replies. This Actor detects the language of every post, applies your engagement, date and post-type filters, and **charges only for the posts that pass**.

No login, no cookies, no account needed.

### What you get

| | This Actor | Typical Threads search scrapers |
|---|---|---|
| Language | detected per post from its text (`language`, `languageReliable`); filter with `languages: ["en"]` | whatever the search returns |
| Engagement | `minLikes`, `minReplies`, `minReposts` on the exact counts | 0-like posts included |
| Replies / reposts / quotes | dropped by default (replies, reposts) or on request (quotes); a kept reply comes with its parent post | mixed in |
| Date range | `postedAfter` / `postedBefore` on the exact publish time, also relative ("7 days") | often missing |
| Text relevance | `requireQueryInText` (all keyword words in the post), `excludeWords` | - |
| Monitoring | `onlyNewPosts`: later runs deliver only posts you have not received yet | same posts again, charged again |
| Duplicates | removed across keywords | often duplicated |
| What you pay for | only kept posts (+ a small fee per keyword searched) | every scraped row |
| Transparency | run summary: posts visible per keyword and how many each filter removed | - |

### Use cases

- **Brand and product monitoring:** your brand, product and competitor names as keywords, `languages: ["en"]`, `onlyNewPosts: true`, scheduled hourly - each run delivers only new posts, straight into Google Sheets, Slack, Make, n8n or Zapier.
- **Lead and conversation discovery:** keywords like "looking for a designer" with `requireQueryInText: true` and `excludeReplies: true`.
- **Trend research in one market:** German keywords with `languages: ["de"]` and `postedAfter: "3 days"`.
- **Content research:** what performs in your niche - `minLikes: 100` shows only posts that already got traction.
- **Datasets for research or AI:** clean JSON with language, exact engagement and timestamps.

### Input example

```json
{
    "queries": ["AI agents", "vibe coding", "#buildinpublic"],
    "languages": ["en"],
    "minLikes": 5,
    "excludeReplies": true,
    "excludeReposts": true,
    "postedAfter": "7 days",
    "onlyNewPosts": true
}
```

- `queries` - keywords, phrases or #hashtags, or a `threads.com/search?q=...` link. Up to 100 per run.
- `languages` - ISO 639-1 codes (`en`, `de`, `es`, ...) or names (`English`). `keepUncertainLanguage` keeps posts too short to detect ("Coffee?").
- `minLikes`, `minReplies`, `minReposts` - minimums on the exact counts. Hidden like counts never pass a minimum above 0.
- `postedAfter`, `postedBefore` - `2026-10-01`, an ISO timestamp, or `"24 hours"`, `"7 days"`, `"2 weeks"`.
- `excludeReplies` (default on), `excludeReposts` (default on), `excludeQuotes` (default off).
- `requireQueryInText`, `excludeWords`.
- `onlyNewPosts`, `stateStoreName`, `dedupeAcrossQueries` (default on), `maxResults`, `requestDelaySecs`.

### Output example

A real row from a test run on 04 Oct 2026 (`languages: ["en"]`, `minLikes: 5`; media URLs shortened):

```json
{
    "query": "AI agents",
    "rankInResults": 2,
    "resultsVisible": 20,
    "id": "3988374200540594977",
    "url": "https://www.threads.com/@themattberman/post/DdZjTQ-jgMh",
    "text": "jev is INSANE.\n\nin 40 seconds it broke down 724 live ads from 37 brands. ...",
    "language": "en",
    "languageReliable": true,
    "languageSource": "detected",
    "postedAt": "2026-09-17T19:00:47.000Z",
    "likes": 1349,
    "replies": 87,
    "reposts": 98,
    "quotes": 12,
    "engagement": 1546,
    "countsHidden": false,
    "username": "themattberman",
    "userFullName": "Matthew Berman",
    "userVerified": false,
    "profileUrl": "https://www.threads.com/@themattberman",
    "isReply": false,
    "parentPost": null,
    "isRepost": false,
    "isQuote": false,
    "mediaType": "video",
    "imageUrl": "https://scontent-....cdninstagram.com/...jpg",
    "videoUrl": "https://scontent-....cdninstagram.com/...mp4",
    "linkUrl": null,
    "code": "DdZjTQ-jgMh",
    "scrapedAt": "2026-10-04T18:23:38.090Z"
}
```

The `SUMMARY` record in the key-value store shows the filter funnel per keyword, for example:
`"AI agents": 20 posts visible, 2 written, dropped: minLikes 6, language 3, excludeReplies 9.`

### Honest numbers

- **Threads shows a logged-out visitor one window of about 15-20 posts per keyword** (measured 04 Oct 2026 on 8 keywords: 14-20 posts each; one German two-word keyword showed none). There is no "Recent" tab and no further pages without logging in. This Actor never logs in, so it works with exactly that window - and tells you how many posts it saw (`resultsVisible`).
- **The window rotates.** Running the same keyword again a minute later brought 7 posts that were not in the first run. For volume, run many keywords and schedule the Actor with `onlyNewPosts: true` - each run adds the new posts, and you never pay twice for the same post.
- **Language detection** uses the ELD n-gram detector on the post text (links, mentions, hashtags and emoji removed). Real sentences are detected reliably; one-word posts are marked `languageReliable: false`. Closely related languages (Malay / Indonesian) can be confused.
- The date filter works on the exact publish time of each post, but only within the visible window: it does not find older posts that Threads does not show.

### Bot protection is never bypassed

One plain page request per keyword, at a pace of one every 2 seconds, identified as this Actor. No login, no cookies, no proxies, no browser disguise. If Threads refuses (HTTP 403/429, a login or challenge page), the run stops with a clear message; posts written before stay in the dataset, and nothing is retried.

### How much does it cost?

Pay per event:

- `post` - **$0.003** per post written (= $3 per 1,000 kept posts)
- `query` - **$0.001** per keyword searched that returned posts

Posts removed by a filter, posts already delivered in an earlier run, duplicates, keywords without results and blocked or failed searches are never charged. Example: 50 keywords with strict filters that keep 5 posts each = 50 x $0.001 + 250 x $0.003 = **$0.80**. Set "Max cost per run" in the run options to cap any run.

### FAQ

**Why do I get fewer posts than I asked for?** Threads shows only about 15-20 posts per keyword to a visitor who is not logged in, and your filters remove what does not match. The summary shows exactly what each filter removed.

**Can I get "Recent" posts?** Not logged out. Schedule the Actor with `onlyNewPosts` instead - the rotating window plus the memory collect fresh posts over time.

**Is it legal?** It reads only what Threads shows publicly to any visitor. You are responsible for how you use personal data in the results (GDPR and similar laws).

**Does it need my Threads or Instagram account?** No. It never logs in and never uses cookies.

# Actor input Schema

## `queries` (type: `array`):

What you would type into the Threads search box, one per line (words, phrases or #hashtags) - or a threads.com/search?q=... link. Up to 100 per run.

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

Keep only posts written in these languages - ISO 639-1 codes like en, de, es (or names like English). The language is detected per post from its text. Empty = all languages.

## `keepUncertainLanguage` (type: `boolean`):

Very short posts ("Coffee?", emoji only) have no reliably detectable language. Off = they are dropped when a language filter is set. On = they are kept, marked with languageReliable: false.

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

Drop posts with fewer likes. Posts whose author hides like counts never pass a minimum above 0.

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

Drop posts with fewer direct replies.

## `minReposts` (type: `integer`):

Drop posts with fewer reposts.

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

Keep posts published on or after this point: a date (2026-10-01), an ISO timestamp, or relative like "7 days" or "24 hours". Uses the exact publish time of each post.

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

Keep posts published on or before this point (a bare date includes the whole day, UTC). Same formats as above.

## `excludeReplies` (type: `boolean`):

Drop posts that are replies to another post (Threads search mixes many short replies into the results). The parent post of a kept reply is included as context when this is off.

## `excludeReposts` (type: `boolean`):

Drop reposts of someone else's post.

## `excludeQuotes` (type: `boolean`):

Drop posts that quote another post.

## `requireQueryInText` (type: `boolean`):

Threads also matches posts by author name or topic. On = keep only posts whose text contains every word of the keyword (word starts: "agent" matches "agents").

## `excludeWords` (type: `array`):

Drop posts whose text contains any of these words or phrases (case-insensitive), e.g. giveaway, promo code.

## `onlyNewPosts` (type: `boolean`):

Remember every post delivered for a keyword and never deliver (or charge) it again in later runs. Schedule the Actor hourly or daily to collect fresh posts over time. A post that was filtered out earlier (e.g. below minimum likes) is delivered once it passes.

## `stateStoreName` (type: `string`):

Named key-value store that holds the onlyNewPosts memory. Use a different name per project to keep monitors apart.

## `dedupeAcrossQueries` (type: `boolean`):

A post found by two keywords is delivered (and charged) once, under the first keyword.

## `maxResults` (type: `integer`):

The run stops after this many posts are written. Your "Max cost per run" setting is respected as well.

## `requestDelaySecs` (type: `integer`):

One search page is loaded at a time, with this pause in between.

## Actor input object example

```json
{
  "queries": [
    "AI agents",
    "Kaffee"
  ],
  "languages": [
    "en"
  ],
  "keepUncertainLanguage": false,
  "minLikes": 0,
  "minReplies": 0,
  "minReposts": 0,
  "excludeReplies": true,
  "excludeReposts": true,
  "excludeQuotes": false,
  "requireQueryInText": false,
  "onlyNewPosts": false,
  "stateStoreName": "threads-search-scraper-state",
  "dedupeAcrossQueries": true,
  "maxResults": 500,
  "requestDelaySecs": 2
}
```

# Actor output Schema

## `results` (type: `string`):

One row per kept post: keyword, position in the results, text, detected language (with reliability), exact publish time, likes, replies, reposts, quotes, author, reply/repost/quote flags with the related post, media and link.

## `summary` (type: `string`):

Per keyword: status (ok, no_results, failed, blocked), posts visible, posts written and how many each filter removed; languages seen; HTTP statistics and billing. Nothing removed by a filter is charged.

# 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 = {
    "queries": [
        "AI agents"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("thequietstack/threads-search-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 = { "queries": ["AI agents"] }

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

```

## MCP server setup

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