# Threads Scraper — Posts, Profiles, Search & Monitoring (`yearly_register/threads-scraper-v2`) Actor

Scrape public Threads posts, profiles, replies, search and media without login. Mix targets in one run, monitor new or changed posts, and pay only for delivered results.

- **URL**: https://apify.com/yearly\_register/threads-scraper-v2.md
- **Developed by:** [Automation Tech](https://apify.com/yearly_register) (community)
- **Categories:** Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 post results

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?

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, Search & Monitoring

Scrape public Threads data **without login, cookies, or a Meta API key**.

Paste usernames, post URLs, hashtags, keywords, or account searches into one run and get clean, structured profiles, posts, exposed replies, media, engagement data, and monitoring updates.

**No login · Mixed targets · Persistent monitoring · Pay only for delivered results**

### What you can do

- Track competitor and creator posts
- Monitor brands, keywords, and hashtags
- Discover Threads accounts by topic
- Export public posts, profiles, replies, repost feeds, and media metadata
- Detect new posts and engagement changes on scheduled runs
- Feed clean JSON/CSV/Excel data into dashboards, automations, AI agents, or MCP workflows

### Quick start

For most runs, this is enough:

```json
{
  "targets": ["@openai"]
}
```

Mix different target types in the same run:

```json
{
  "targets": [
    "@openai",
    "#artificialintelligence",
    "AI agents",
    "accounts:AI researchers",
    "replies:@instagram"
  ],
  "maxResults": 250
}
```

The Actor detects each target type automatically, combines the results, removes duplicates, and preserves where each record came from.

### Ready-made Tasks

The Actor includes reusable Apify Tasks for the most common workflows:

- **Profile & Recent Posts** — scrape a public profile and recent posts
- **Keyword & Hashtag Research** — research topic and hashtag results in one run
- **Account Discovery** — find public Threads accounts by topic
- **Competitor / Creator Monitor** — return new posts and engagement changes across repeated runs
- **Keyword & Hashtag Monitor** — track topic results with persistent state
- **Replies & Reposts Research** — inspect public replies and repost feeds

Each Task uses the `latest` Actor build, conservative first-run limits, a maximum charge guard, and editable example inputs. These Tasks are published as public Example tasks and have their own Store landing pages.

### What you can scrape

- Public profile metadata
- Recent public profile posts
- Single public post pages
- Replies exposed in public post/profile payloads
- Public profile replies feeds
- Public profile reposts feeds
- Keyword search
- Hashtag search
- Account/user search
- Images, videos, and carousel metadata when exposed
- Public engagement counters when exposed
- New and changed records across scheduled monitoring runs

Missing upstream values stay `null`; the Actor does not invent counts or relationship data.

### Target syntax

| Input | Meaning |
| --- | --- |
| `@openai` | Profile + recent public posts |
| `openai` | Profile |
| Threads profile URL | Profile + recent public posts |
| Public `/post/` URL | Single post page |
| `#AI` | Hashtag search |
| `AI agents` | Keyword search |
| `keyword:AI` | Explicit one-word keyword search |
| `accounts:AI researchers` | Account/user search |
| `replies:@openai` | Public profile replies feed |
| `reposts:@openai` | Public profile reposts feed |

You can provide up to 100 mixed targets plus optional account-search queries in one run.

### Turn any scrape into a monitor

Run the same targets on a schedule and return only what is new or changed:

```json
{
  "targets": ["@openai", "keyword:OpenAI"],
  "monitor": true,
  "stateKey": "openai-monitor",
  "emitMode": "new_and_changed"
}
```

The first run builds the baseline. Future runs can emit:

- `new` — posts not previously delivered
- `changed` — engagement changes on previously seen posts
- `new_and_changed` — both
- `all` — every eligible record

Monitoring state is bounded and checkpointed from delivered records, so posts dropped by the result cap are not incorrectly marked as already seen.

### Clean output for mixed workflows

Every post/reply follows the same versioned shape:

```json
{
  "recordType": "post",
  "id": "...",
  "url": "https://www.threads.com/@user/post/...",
  "text": "...",
  "publishedAt": "2026-09-23T00:00:00.000Z",
  "author": {
    "id": "...",
    "username": "user",
    "displayName": "User",
    "verified": true
  },
  "engagement": {
    "likes": 100,
    "replies": 4,
    "reposts": 2,
    "quotes": 1,
    "shares": null,
    "views": null
  },
  "media": [],
  "isReply": false,
  "isRepost": false,
  "isQuote": false,
  "sourceTypes": ["profile", "keyword"],
  "matchedQueries": ["AI"],
  "matchedProfiles": ["openai"],
  "scrapedAt": "2026-09-23T00:01:00.000Z",
  "schemaVersion": "2.0.0"
}
```

One post can be discovered through several targets. The Actor deduplicates by stable Threads ID when possible, then merges useful provenance instead of charging for intentional duplicates.

Dataset views are included for:

- Results
- Posts
- Profiles
- Replies
- Changes

Raw nested JSON remains available for API users, while the common columns are flattened for CSV/Excel-friendly views.

### Filters and sorting

Optional filters include:

- date range
- minimum/maximum likes
- minimum replies or reposts
- verified authors only
- include/exclude authors
- include/exclude keywords
- hashtags and mentions
- media type
- replies only / root posts only
- include/exclude reposts and quotes

Sort by newest, oldest, likes, replies, reposts, or combined engagement.

### Pricing

This Actor uses pay-per-event billing.

You are billed for result rows actually delivered to the dataset, not for internal HTTP pages, retries, duplicate discoveries, filtered records, or empty results.

Current event prices are shown in the Actor's **Pricing** tab.

This makes scheduled monitoring especially predictable: a run that finds very little new data also produces very little billable output.

### Reliability

The Actor is HTTP-first to keep runs lightweight:

1. Fetch public logged-out Threads pages.
2. Discover structured payloads by record shape instead of one brittle JSON path.
3. Follow public same-site continuation URLs when Threads exposes usable ones.
4. Retry transient 408/425/429/5xx failures with bounded backoff.
5. In `auto` proxy mode, escalate direct → datacenter → residential only when needed.
6. Preserve valid partial results if a later request fails.

Every run writes a machine-readable `RUN_SUMMARY` with discovery counts, duplicates, filters, result-cap drops, retries, failed/partial targets, monitoring counters, and delivered rows.

### Practical limits

Threads controls what logged-out visitors can see. This release intentionally does **not** overclaim what the public surface provides:

- Profile pages expose a recent public post window; complete profile history is not claimed.
- Post pages can expose replies, but exhaustive recursive reply-tree pagination is not claimed.
- Reply depth is limited by relationship metadata Threads exposes publicly.
- Search depth and ordering depend on the public Threads search surface; exhaustive search is not claimed.
- Some public fields may be absent and therefore remain `null`.
- Browser/login fallback is not required for the verified surfaces in this release.

If a target becomes partial or fails, the run summary reports it instead of silently pretending the scrape was complete.

### API and automation

Run the Actor through the Apify API, schedules, Tasks, integrations, or MCP-compatible workflows. Results are stored in the default dataset and can be exported as JSON, JSONL, CSV, or Excel.

For low-noise monitoring, schedule the same input with a stable `stateKey` and `emitMode: "new"` or `"new_and_changed"`.

### Development verification

Repository checks:

```bash
npm ci
npm run typecheck
npm test
THREADS_LIVE_TESTS=1 npm test
```

The normal suite is network-free. Live tests validate public Threads shapes and invariants rather than volatile hardcoded counts.

***

This is an unofficial public-data tool and is not affiliated with Meta or Threads.

# Actor input Schema

## `targets` (type: `array`):

Add usernames, profile/post URLs, #hashtags, keyword phrases, accounts:queries, replies:@username, or reposts:@username. The Actor detects the target type automatically.

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

Global cap for final unique rows delivered by the run. Start small, then increase when you know the target returns what you need.

## `accountSearches` (type: `array`):

Optional explicit user/account discovery queries. You can usually use accounts:query directly in Targets instead.

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

Stop each profile, replies, or repost feed after this many discovered post records.

## `scrapeReplies` (type: `boolean`):

Keep reply records Threads exposes for post targets.

## `includeNestedReplies` (type: `boolean`):

Keep replies below the first level when Threads exposes relationship metadata.

## `fullConversation` (type: `boolean`):

Keep the root post and publicly exposed replies available in the fetched payload, subject to reply limits. This does not claim exhaustive conversation pagination.

## `maxReplies` (type: `integer`):

Maximum reply records retained across the run.

## `maxReplyDepth` (type: `integer`):

Keep publicly exposed replies up to this relationship depth.

## `dateFrom` (type: `string`):

Inclusive ISO date/time cutoff. Posts without a usable timestamp are excluded when this filter is set.

## `dateTo` (type: `string`):

Inclusive ISO date/time cutoff. Must be on or after Posted from.

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

Keep posts with at least this many likes. Missing metrics do not satisfy a minimum.

## `maxLikes` (type: `integer`):

Keep posts with no more than this many likes. Missing metrics do not satisfy a maximum.

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

Keep posts with at least this many replies. Missing metrics do not satisfy a minimum.

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

Keep posts with at least this many reposts. Missing metrics do not satisfy a minimum.

## `verifiedOnly` (type: `boolean`):

Keep only posts whose public author metadata is explicitly verified.

## `includeAuthors` (type: `array`):

Keep posts from these exact Threads usernames only. Leading @ is optional.

## `excludeAuthors` (type: `array`):

Remove posts from these exact Threads usernames. Leading @ is optional.

## `includeKeywords` (type: `array`):

Keep posts whose returned text contains at least one term.

## `excludeKeywords` (type: `array`):

Remove posts whose returned text contains any term.

## `hashtags` (type: `array`):

Keep posts containing at least one of these normalized hashtags.

## `mentions` (type: `array`):

Keep posts mentioning at least one of these exact usernames.

## `mediaTypes` (type: `array`):

Keep posts containing at least one selected media type.

## `repliesOnly` (type: `boolean`):

Emit reply posts and exclude root posts.

## `rootPostsOnly` (type: `boolean`):

Emit root posts and exclude replies. Cannot be enabled with Replies only.

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

Include records identified as reposts.

## `includeQuotes` (type: `boolean`):

Include records identified as quote posts.

## `sortBy` (type: `string`):

Final post ordering after normalization, deduplication, reply policy, and filtering.

## `monitor` (type: `boolean`):

Turn the scrape into a persistent monitor. Future scheduled runs can return only new posts or engagement changes.

## `stateKey` (type: `string`):

Stable identifier for this independent monitor. Reuse the same value on future scheduled runs.

## `emitMode` (type: `string`):

new = unseen posts; changed = metric changes; new\_and\_changed = both; all = every eligible record.

## `changeRetentionDays` (type: `integer`):

Discard monitoring snapshots not seen within this many days.

## `maxStateRecords` (type: `integer`):

Maximum post snapshots retained for this monitor after pruning.

## `proxyMode` (type: `string`):

Auto starts with direct HTTP and escalates to Apify datacenter or residential proxies only after failure.

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

Maximum Threads targets processed concurrently.

## `requestTimeoutSecs` (type: `integer`):

Abort one HTTP attempt after this many seconds.

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

Maximum bounded retries per fetch strategy for retryable failures.

## Actor input object example

```json
{
  "targets": [
    "@openai",
    "#artificialintelligence",
    "AI agents"
  ],
  "maxResults": 250,
  "accountSearches": [
    "AI researchers",
    "NASA"
  ],
  "maxPosts": 100,
  "scrapeReplies": false,
  "includeNestedReplies": true,
  "fullConversation": false,
  "maxReplies": 100,
  "maxReplyDepth": 10,
  "verifiedOnly": false,
  "repliesOnly": false,
  "rootPostsOnly": false,
  "includeReposts": true,
  "includeQuotes": true,
  "sortBy": "default",
  "monitor": false,
  "stateKey": "default",
  "emitMode": "new",
  "changeRetentionDays": 30,
  "maxStateRecords": 50000,
  "proxyMode": "auto",
  "maxConcurrency": 5,
  "requestTimeoutSecs": 30,
  "maxRequestRetries": 4
}
```

# Actor output Schema

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

Profiles, posts, replies, search discoveries, and monitoring changes delivered by this run.

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

Discovery counts, deduplication, filters, result-cap drops, retries, failures, monitoring counters, and delivered rows.

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

// Run the Actor and wait for it to finish
const run = await client.actor("yearly_register/threads-scraper-v2").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 = { "targets": ["@openai"] }

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

```

## MCP server setup

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

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/vpoydvaXlrbyrbABZ/builds/HZvVp1mefUNk0Xb7d/openapi.json
