# Threads Posts Scraper 🧵 Public Profiles, No Login (`cleanfeed/meta-threads-posts-scraper`) Actor

Scrape posts from any public Threads profile. Pass profile handles or URLs; get post text, like, reply and repost counts, author handle, publish date and a direct post URL as JSON or CSV. No login required. Since date filter makes scheduled monitoring cheap.

- **URL**: https://apify.com/cleanfeed/meta-threads-posts-scraper.md
- **Developed by:** [Yaniv van der Stigchel](https://apify.com/cleanfeed) (community)
- **Categories:** Social media, AI, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.43 / 1,000 post returneds

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?

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 Posts Scraper — monitor profiles, no login

Latest posts from any public Threads profile. Text, likes, replies, reposts,
quotes and timestamps. No login, no app password, no API key.

### What it does

- Monitor Threads profiles for new posts
- Export Threads posts to JSON or CSV
- Track Threads engagement metrics
- Watch competitor and creator accounts on Threads

Pass many profiles in one run. Every row is tagged with its source profile.

### Scope — read this before running

**Threads only exposes a recent window per profile, typically 5 to 15 posts.**
Deeper history is not available without an authenticated session, so this actor
does not offer it and does not pretend to.

That makes it a **monitoring tool, not an archive tool**. It is built for
running on a schedule across many profiles: set `since` to your last run and
each run returns only what is new. If you need a profile's full back catalogue,
this is not the right tool.

### Why it works where simpler scrapers do not

Threads serves a stripped page to standard HTTP clients — 265 KB with no post
data, versus 874 KB with it. This actor uses browser TLS fingerprinting to
receive the full payload, which is why it returns posts when plain scrapers
return nothing.

### Honest failure reporting

Unavailable profiles and misspelled handles return a row with a reason and
**cost you nothing**. You are only charged for posts actually delivered.

### Input

| Field | Required | Description |
|---|---|---|
| `profiles` | yes | Handles or threads.net URLs |
| `maxPostsPerProfile` | no | Default 25. Your cost ceiling. |
| `since` | no | ISO date. Only newer posts — ideal for scheduled runs. |
| `proxy` | no | Optional, for spreading load across many profiles |

### Example input

```json
{
  "profiles": ["@zuck", "mosseri"],
  "since": "2026-08-25"
}
```

### Output

Every row has the same fields whether it succeeded or failed, so you can
select columns without branching. Failed rows are never charged.

| Field | Type | Description |
|---|---|---|
| `success` | boolean | True when this row carries data. Failed rows are never charged. |
| `sourceProfile` | string | The profile input this row came from. |
| `postUrl` | string | Public threads.net link to the post. |
| `postId` | string | Threads' numeric post identifier. |
| `shortcode` | string | Short code used in the post URL. |
| `authorHandle` | string | Handle of the profile that posted. |
| `authorId` | string | Stable profile identifier. |
| `text` | string | The post body. |
| `publishedAt` | string | ISO 8601 timestamp of publication. |
| `likeCount` | integer | Likes at the time of the run. |
| `replyCount` | integer | Replies at the time of the run. |
| `repostCount` | integer | Reposts at the time of the run. |
| `quoteCount` | integer | Quote posts at the time of the run. |
| `hasMedia` | boolean | True when the post includes an image or video. |
| `errorCode` | string | Machine-readable failure reason. Null on success. |
| `errorMessage` | string | Human-readable explanation of the failure. Null on success. |

#### Example — success

```json
{
  "success": true,
  "sourceProfile": "@zuck",
  "postUrl": "https://www.threads.net/@zuck/post/C1aBcDeFgHi",
  "postId": "3141592653589793",
  "shortcode": "C1aBcDeFgHi",
  "authorHandle": "@zuck",
  "authorId": "314159",
  "text": "Testing a few new things in the app this week.",
  "publishedAt": "2026-08-14T09:12:04.000Z",
  "likeCount": 48210,
  "replyCount": 3112,
  "repostCount": 884,
  "quoteCount": 127,
  "hasMedia": true,
  "errorCode": null,
  "errorMessage": null
}
```

#### Example — failure

A failure carries the same fields, so nothing downstream has to branch.

```json
{
  "success": false,
  "sourceProfile": "@private_example",
  "postUrl": null,
  "postId": null,
  "shortcode": null,
  "authorHandle": null,
  "authorId": null,
  "text": null,
  "publishedAt": null,
  "likeCount": null,
  "replyCount": null,
  "repostCount": null,
  "quoteCount": null,
  "hasMedia": null,
  "errorCode": "profile-unavailable",
  "errorMessage": "No public Threads profile exists for this handle, or it is private."
}
```

#### Error codes

- `profile-unavailable`
- `unparseable-handle`
- `no-posts-found`
- `error`

### Use it for

- **Threads profile posts** — a public profile's timeline
- **Meta Threads monitoring** — scheduled runs with a since filter
- Social listening, brand and competitor tracking
- Research corpora and training datasets

### Use it from an AI agent (MCP)

This Actor is callable as a tool through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp), so Claude, ChatGPT, Cursor and VS Code can run it directly.

Add the server to your MCP client:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com",
      "headers": {
        "Authorization": "Bearer <YOUR_APIFY_TOKEN>"
      }
    }
  }
}
```

Then ask for what you want in plain language — for example *“get the recent posts from this Threads profile”* — and the agent calls `cleanfeed/meta-threads-posts-scraper` with the right input. Every output field is described in the dataset schema, so the agent knows what it is getting back before it runs anything.

### Call it from code

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")

run = client.actor("cleanfeed/meta-threads-posts-scraper").call(run_input={
    "profiles": ["https://www.threads.net/@zuck"],
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["success"]:
        print(item)
```

#### JavaScript

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });

const run = await client.actor('cleanfeed/meta-threads-posts-scraper').call({
    profiles: ["https://www.threads.net/@zuck"],
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => i.success));
```

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/cleanfeed~meta-threads-posts-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H 'Content-Type: application/json' \
  -d '{"profiles": ["https://www.threads.net/@zuck"]}'
```

### Related actors

| If you need | Use |
|---|---|
| Posts from public Bluesky accounts | [Bluesky Posts Scraper](https://apify.com/cleanfeed/bluesky-posts-extractor) |
| Messages from public Telegram channels | [Telegram Channel Scraper](https://apify.com/cleanfeed/telegram-channel-archive-scraper) |

### Limitations

- **Public profiles only.** Private profiles return `errorCode: profile-unavailable`.
- **Threads exposes only a recent window** — typically 5 to 15 posts per profile. There is no full history to page through, so `maxPostsPerProfile` above that returns what exists rather than erroring.
- Because the window is short, scheduled runs with `since` are the reliable way to build history over time.

### FAQ

#### Do I need a Threads or Instagram login?

No login and no app password. Only public profiles are read.

#### Why do I only get 5 to 15 posts?

That is the window Threads exposes publicly. There is no deeper history to page through, so asking for more returns what exists rather than erroring.

#### How do I build a longer history then?

Run on a schedule with `since`. Each run adds what is new, and over time you accumulate the history the platform will not serve in one request.

#### What profile formats work?

An `@handle` or a `threads.net` profile URL.

#### Can I read private profiles?

No. Those return `errorCode: profile-unavailable`.

### Notes

Only publicly visible posts are collected. No login, no private data.

# Actor input Schema

## `profiles` (type: `array`):

Public Threads profiles to fetch posts from: @handle or threads.net profile URLs.

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

Caps how many of the returned posts you are charged for. Threads exposes only its recent window per profile — typically 5 to 15 posts.

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

ISO date, e.g. 2026-08-01. Ideal for scheduled monitoring — only new posts are returned and charged.

## `proxy` (type: `object`):

Optional. Useful for spreading load when monitoring many profiles.

## Actor input object example

```json
{
  "profiles": [
    "@zuck"
  ],
  "maxPostsPerProfile": 25,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `posts` (type: `string`):

One row per post: text, author, likes, replies, reposts, quotes, timestamp and a direct link.

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

Profiles processed and posts delivered.

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

// Run the Actor and wait for it to finish
const run = await client.actor("cleanfeed/meta-threads-posts-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 = { "profiles": ["@zuck"] }

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cleanfeed/meta-threads-posts-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/rQLNF7LOcLgCLgT3k/builds/9HVVQilcfe7bdAKbb/openapi.json
