# Threads Relevant Search (`titan_coder/threads-relevant-search`) Actor

Strict text-match search for relevant posts on Threads with the direct replies embedded in each post's page. Pay only for genuinely relevant, delivered posts; irrelevant hits, empty results and errors are free.

- **URL**: https://apify.com/titan\_coder/threads-relevant-search.md
- **Developed by:** [Radu Furtuna](https://apify.com/titan_coder) (community)
- **Categories:** Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 1,000 relevant post delivereds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Threads Relevant Search 🧵

**Strict text-match search on Threads — every delivered post comes with the direct replies embedded in its own page.**

Most Threads search tools bill you for every raw hit, even the ones that just happen to contain your keyword buried in noise. This one checks relevance with a deterministic word-boundary match before charging — and never bills the same post twice under the same search.

### Why marketers/analysts/community managers use this

- **No noise billing.** A post is only delivered (and charged) if your query matches as a whole phrase or as complete words in its text — not a random substring inside an unrelated word.
- **Replies included.** Every delivered post comes with the direct replies embedded in its own page — no separate scrape needed.
- **No duplicate charges, ever.** Re-run the same search (same `searchId`) as many times as you like — a post you've already been billed for is never billed again.
- **No login, no browser.** Public page access only — no Threads/Instagram account of ours or yours at risk.

### How it works

1. Give it a `searchId` (names this search's durable history) and a `query`.
2. It searches Threads' public top results for that query, keeps only the posts that pass a strict text-match relevance check, and fetches each one's own page for its direct replies.
3. You get one dataset row per delivered relevant post — author, text, date, engagement counts, and its replies.

### What you pay for

**One event only — `relevant-post-delivered`.** Billed once per genuinely relevant post actually delivered to your dataset — and only once, ever, per `searchId`, no matter how many times you re-run the same search. The same real post found under a *different* `searchId` is billed again there — that's a separate order, not a duplicate. Irrelevant hits, empty results, and errors cost nothing.

### Input

| Field | What it does |
|---|---|
| `searchId` | Names this search's durable history — required. The query and relevance rule are locked to this ID on first run: changing the query under the same `searchId` later fails loudly (`search_id_query_mismatch`) instead of silently changing what counts as "already seen". |
| `query` | Keyword or phrase to search, 1–200 characters |
| `maxResults` | How many relevant posts to attempt delivering this run (1–30, default 20) |
| `notifyOn` | `new_results` (default) fires the webhook only when something new was delivered; `always`/`never` for the rest |
| `webhookUrl` | HTTPS only; delivers a digest as JSON — without the reply trees (see Limits) |

### Output

- **Dataset** — one row per delivered relevant post: author, text, date, like count, the platform's own reply count, the replies we actually fetched (`repliesReturnedCount`, `repliesTruncated` if capped), and the replies themselves.
- **Key-value store** — a short digest and a coverage summary.
- **Run status** — a clear reason on bad input, a locked search identity mismatch, or a run that ran out of time — reported honestly, not silently truncated as a success.

### What "relevant" means here

A post counts as relevant only if, after normalizing case and whitespace, your query appears in the post's text either as the exact phrase (with word boundaries on both ends) or as every one of its individual words (also with word boundaries). This is a **strict text match**, not a semantic/meaning-based match — we don't promise the model understands intent, only that "ai" won't match inside "paid" and "open ai" won't match inside "open air".

### Limits (v1)

- Up to 30 relevant posts delivered per run, from Threads' fixed top-results batch for your query — there is no further pagination beyond that batch in v1.
- Replies are the direct (top-level) replies embedded in the post's own page — not a full recursive thread, and capped at 100 per post.
- This is a fixed attempt-set: if fetching one candidate's page fails, we don't substitute another candidate in its place — you may get fewer than `maxResults` on a rough run, honestly reported as partial.

### A note on the source

Data comes from Threads' own public pages via the same request pattern a logged-out browser makes — no login, no account of ours or yours at risk. This is not an official API; Meta's rendering could change or be blocked at any time — use this at your own discretion.

# Actor input Schema

## `searchId` (type: `string`):

Name of this search's durable history (a-z, 0-9, dash; up to 40 chars). The query and relevance rule are locked to this ID on first run — changing the query under the same searchId later fails loudly instead of silently hiding results.

## `query` (type: `string`):

Keyword or phrase to search on Threads. 1-200 characters.

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

Maximum number of relevant posts to deliver this run (1-30). Threads returns a fixed top-set per search — this does not paginate beyond it.

## `notifyOn` (type: `string`):

new\_results — post the webhook only when new relevant posts were found; always — post it every run; never — do not call webhookUrl at all.

## `webhookUrl` (type: `string`):

Optional. Receives a digest of delivered posts as JSON (POST) — without the reply trees (see README). HTTPS only; private/loopback addresses are rejected; redirects are not followed.

## Actor input object example

```json
{
  "searchId": "my-search",
  "query": "apify",
  "maxResults": 20,
  "notifyOn": "new_results"
}
```

# Actor output Schema

# 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 = {
    "searchId": "my-search",
    "query": "apify"
};

// Run the Actor and wait for it to finish
const run = await client.actor("titan_coder/threads-relevant-search").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 = {
    "searchId": "my-search",
    "query": "apify",
}

# Run the Actor and wait for it to finish
run = client.actor("titan_coder/threads-relevant-search").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 '{
  "searchId": "my-search",
  "query": "apify"
}' |
apify call titan_coder/threads-relevant-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,titan_coder/threads-relevant-search"
        }
    }
}

```

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/tY4TE2mwMjGXr1Ckq/builds/JDaHQ06d2XrEqFGXb/openapi.json
