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

Scrape public Threads profiles, posts, searches, and monitoring results with batch inputs, smart filters, deduplication, and no login, cookies, or browser automation.

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

## Pricing

from $1.80 / 1,000 search 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?

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 Scraper – Profiles, Posts, Search & Monitoring

Collect structured public Threads data without login, cookies, or browser automation. Scrape profiles, posts, keyword-search results, and monitoring feeds with batch inputs, advanced filters, deduplication, and provenance tracking.

**Built for:** brand monitoring, competitor research, creator tracking, social listening, scheduled workflows, and public Threads datasets.

> **Unofficial tool.** Not affiliated with or endorsed by Meta or Threads.

### What it does

Threads Scraper turns publicly available Threads pages into clean, structured dataset rows. Use it to retrieve profile information and recent public posts, inspect one public post, search public posts by keyword, or combine profiles and keywords in a single stateless monitoring run.

It works with public HTTP responses only. It does not require a Threads login, cookies, credentials, browser automation, or private APIs. The Actor normalizes available source data while leaving unavailable fields as `null` rather than guessing values.

### Key features

| Feature | What you get |
| --- | --- |
| **4 scraping modes** | Profiles, posts, search, and monitoring. |
| **Batch inputs** | Up to 50 profiles or 20 search queries per run. |
| **Advanced filters** | Time, likes, verified authors, keywords, and usernames. |
| **Smart deduplication** | Merge duplicate posts across queries and sources. |
| **Monitoring workflows** | Combine profiles and keyword search with `since` support. |
| **Rich structured output** | Canonical URLs, hashtags, mentions, links, and provenance. |
| **HTTP-only** | No login, cookies, credentials, or browser automation. |
| **Pay per result** | Result events are emitted only for delivered final results. |

### Main use cases

- Brand monitoring for mentions, campaigns, and relevant public conversations.
- Competitor monitoring across a selected list of public profiles and keywords.
- Creator monitoring for recent posts, engagement signals, and links shared publicly.
- Social listening and trend research around topics, products, or events.
- Public-content research where a clean dataset is more useful than manually browsing pages.
- Scheduled monitoring workflows that need a fresh, deduplicated stream on each run.
- Building structured public Threads datasets for analysis, dashboards, or downstream automation.

### Input modes

Choose one explicit `mode` per run. Input is validated before a public request is made.

#### Profile

Scrape one public profile and its available recent posts. A leading `@` is accepted.

```json
{
  "mode": "profile",
  "username": "nasa",
  "maxPostsPerProfile": 5
}
```

For several targets, use `usernames` instead. Up to 50 unique usernames are processed sequentially. Each successful target produces one dataset row with `profile`, `recentPosts`, and `scrapedAt`.

```json
{
  "mode": "profile",
  "usernames": ["nasa", "instagram"],
  "sortBy": "newest"
}
```

#### Post

Scrape one public Threads post URL and return a normalized post record.

```json
{
  "mode": "post",
  "url": "https://www.threads.com/@username/post/SHORTCODE"
}
```

#### Search

Retrieve results from Threads' publicly available keyword-search surface for a keyword or phrase. `maxResults` applies after normalization, deduplication, filtering, and sorting.

```json
{
  "mode": "search",
  "query": "artificial intelligence",
  "maxResults": 25,
  "sortBy": "mostLiked"
}
```

For multiple searches, use `queries`. Up to 20 unique queries are processed sequentially and combined into one final unique result set. Each row includes `matchedQueries`, so you can see why a post appeared. Search relevance and available result volume depend on what Threads exposes publicly for the supplied query.

```json
{
  "mode": "search",
  "queries": ["AI", "machine learning"],
  "maxResults": 50
}
```

#### Monitor

Monitor combines available recent posts from public profiles with public keyword-search results. The two sources are merged into one globally deduplicated stream while retaining source provenance. It is suitable for scheduled workflows and stateless: the caller supplies or updates `since` for each run, and the Actor does not save cursors, history, or prior-run state.

```json
{
  "mode": "monitor",
  "usernames": ["openai", "nasa"],
  "queries": ["space", "artificial intelligence"],
  "since": "2026-08-29T00:00:00Z",
  "maxResults": 100
}
```

At least one of `usernames` or `queries` is required. Monitor rows retain `matchedProfiles`, `matchedQueries`, and `sourceTypes`, then merge duplicate posts into one record.

### Pay per result

This Actor uses Pay-Per-Event pricing. A result event is emitted only when a final dataset row is delivered:

- `profile-result` — one delivered profile result, including its nested recent posts.
- `post-result` — one delivered public post.
- `search-result` — one final unique search result.
- `monitor-result` — one final unique monitoring result.

Duplicate, filtered-out, skipped, failed, and empty results do not generate result events.

### Filters and sorting

Profile recent posts, search results, and monitor results support the same local filters:

| Input | Behavior |
| --- | --- |
| `postedWithin` | `24h`, `7d`, `30d`, or `any`. |
| `postedAfter` | Keeps posts published at or after an ISO timestamp. |
| `since` | Additional ISO freshness cutoff for monitor mode. |
| `minLikes` | Requires at least the specified public like count. |
| `verifiedOnly` | Keeps posts whose available public author metadata is verified. |
| `includeKeywords` | Keeps posts matching at least one case-insensitive text term. |
| `excludeKeywords` | Rejects posts matching any supplied text term. |
| `includeAuthors` | Keeps exact public username matches; `@` is optional. |
| `excludeAuthors` | Rejects exact public username matches; `@` is optional. |
| `sortBy` | `default`, `newest`, `oldest`, or `mostLiked`. |

When more than one time cutoff is supplied, the most restrictive (latest) cutoff applies. Exclusions take precedence over inclusions. The processing order is: normalize, deduplicate, filter, sort, then truncate. `maxPostsPerProfile` applies per profile; `maxResults` applies to the final search or monitor result set.

### Output data

Post-like rows can include:

- Stable identifiers: `id`, `shortcode`, and a canonical public `url` when available.
- Content and timing: `text` and `publishedAt`.
- Public engagement: `likeCount`, `replyCount`, `repostCount`, and `quoteCount`.
- Author data: `author` with available profile fields.
- Derived text metadata: `hashtags`, `mentions`, and `externalLinks`. Hashtags and mentions omit their leading `#` and `@`.
- Provenance: `matchedQueries`, `matchedProfiles`, and `sourceTypes` where the mode exposes them.
- `scrapedAt` on every delivered result row.

Profile rows contain a `profile` object plus a nested `recentPosts` array. Any source field that is unavailable remains `null`; this Actor does not infer missing values.

The default dataset **Results** view is designed for mixed output and shows useful fields for both profile and post-like rows. **Posts** and **Profiles** remain available as focused dataset views.

### Scope and responsible use

This Actor is limited to public Threads data and uses conservative sequential requests for batch work. It does not use login, cookies, browser automation, private cursor pagination, authenticated APIs, comments or replies, media downloading, or persistent internal state. Some public fields may be `null`, and upstream rate limits can occasionally affect requests.

Users are responsible for complying with applicable laws, platform terms, privacy obligations, content rights, and internal policies. Do not use the Actor for harassment, stalking, spam, fraud, discrimination, abuse, or to bypass access controls. This documentation is informational and not legal advice.

### Practical API examples

The scripts in `examples/` call the published Actor through the Apify REST API and print the default Dataset. They use Node’s built-in `fetch`, so they add no production dependency.

- `examples/keyword-search.mjs` — return a bounded set of public keyword-search matches.
- `examples/monitor-public-threads.mjs` — combine one public profile with a public keyword search in a stateless monitoring run.

Run an example with an environment variable; never hard-code an Apify token:

```bash
`APIFY_TOKEN=... node examples/keyword-search.mjs "product analytics"`
`APIFY_TOKEN=... node examples/monitor-public-threads.mjs`
```

The monitoring example calculates a seven-day `since` cutoff for demonstration. In a real scheduler, persist the last successful cutoff in your workflow and pass that value on the next run.

The examples call the published [Apify Actor](https://apify.com/yearly_register/threads-scraper). A matching public Example Task link will be added here only after its URL has been verified from Apify Console.

# Actor input Schema

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

profile returns profile results; post returns one post; search returns post results; monitor combines profiles and searches.

## `username` (type: `string`):

Use for one profile target. A leading @ is accepted.

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

Use for up to 50 unique profile or monitor targets.

## `url` (type: `string`):

Use for one public Threads post target.

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

Use for one keyword search.

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

Use for up to 20 unique search or monitor queries.

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

Total final unique search or monitor results. Default is 10 for search and 100 for monitor.

## `maxPostsPerProfile` (type: `integer`):

Optional post cap for each profile result after filtering and sorting.

## `postedWithin` (type: `string`):

Keep posts published within this window.

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

Keep posts published at or after this ISO timestamp.

## `since` (type: `string`):

Monitor freshness cutoff as an ISO timestamp.

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

Keep posts with at least this many public likes.

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

Keep only posts whose public author metadata is verified.

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

Keep posts containing at least one keyword.

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

Reject posts containing any keyword.

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

Keep exact matching public usernames.

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

Reject exact matching public usernames.

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

Apply after filtering and deduplication.

## Actor input object example

```json
{
  "mode": "profile",
  "username": "nasa",
  "maxPostsPerProfile": 3,
  "postedWithin": "any",
  "verifiedOnly": false,
  "sortBy": "default"
}
```

# Actor output Schema

## `items` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "profile",
    "username": "nasa",
    "maxPostsPerProfile": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("yearly_register/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 = {
    "mode": "profile",
    "username": "nasa",
    "maxPostsPerProfile": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("yearly_register/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 '{
  "mode": "profile",
  "username": "nasa",
  "maxPostsPerProfile": 3
}' |
apify call yearly_register/threads-scraper --silent --output-dataset

```

## MCP server setup

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