# Threads Scraper — Posts, Profiles, Replies, No Login (`dottti/threads-scraper`) Actor

Public Threads data without login or cookies: profiles, latest posts, single posts with replies, keyword or hashtag samples, and only-new-posts alerts. Clean JSON, CSV or Excel. $1.50 per 1,000 results.

- **URL**: https://apify.com/dottti/threads-scraper.md
- **Developed by:** [Dottti](https://apify.com/dottti) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.05 / 1,000 results

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 Scraper: Posts, Profiles, Replies, No Login

Get public Threads data from threads.com and threads.net. You get profiles, their latest posts, single posts with their replies, and a quick keyword or hashtag sample. An alert mode delivers only the posts that are new since the last run.

The actor needs no Threads account, no cookies and no browser. It reads the same public data that Threads shows to a logged-out visitor.

- **Price:** $1.50 per 1,000 results ($0.0015 each) on the Apify Free plan, less on paid plans (down to $1.05). A result is one profile row or one post row. Status rows are free. Platform usage is included.
- **First run:** the form is prefilled with two profiles (nasa, zuck) and one post. Click **Start** and you get 2 profiles and 31 posts and replies in about 5 seconds, for $0.05 (tested 4 November 2026).

### Who uses it

- **Social media managers** who track their own accounts and competitors: likes, replies, reposts and quotes per post.
- **Brand monitoring teams** who watch a list of accounts or posts on a schedule and want only the new posts and replies.
- **Researchers** who collect public posts from news outlets, public figures or agencies for analysis.
- **AI pipelines and agents** that need clean JSON: post text, author, time, counts, media links, mentions and hashtags.

### What you get

Every row has a `type` field.

- `profile`: username, full name, bio, follower count, verified flag, private flag, profile picture, bio links, profile topic tags.
- `post`: id, code, link, author, text, time posted (ISO 8601, UTC), likes, replies, reposts, quotes, reshares, media type, media links (images, videos with thumbnail, carousels), hashtags, topic tag, mentions, links, the quoted or reposted post, and who it replies to.
- `status`: one row for each input that returned nothing (missing account, deleted post, bad link, blocked). Status rows are free.

Post rows also say where they came from in `sourceType`:

- `profile`: a post from a profile you listed.
- `post`: the post link you listed.
- `author_thread`: the author's own follow-up posts under that post.
- `parent`: the posts above it, when your link is a reply.
- `reply`: a reply to your post link. `replyToPostCode` names the post.
- `search`: a result for a keyword. `searchQuery` names the keyword.

When an author writes a thread of several posts, each post is its own row. `threadRootCode` and `threadPosition` keep them in order.

### Examples from real runs (21 October 2026)

Input:

```json
{
  "profiles": ["zuck", "nasa", "natgeo", "mosseri", "skynewsarabia"],
  "postUrls": [
    "https://www.threads.com/@zuck/post/Ddt7cL5EfUG",
    "https://www.threads.com/@natgeo/post/DeujURDilYs"
  ]
}
```

Result: 132 rows (5 profiles, 127 posts and replies), 7 of 7 inputs ok, 16 requests, 8 seconds.

A profile row (image link shortened here):

```json
{
  "type": "profile",
  "id": "63269174602",
  "username": "natgeo",
  "url": "https://www.threads.com/@natgeo",
  "fullName": "National Geographic",
  "bio": "Step into wonder and find your inner explorer with National Geographic 🌎",
  "followerCount": 18394232,
  "isVerified": true,
  "isPrivate": false,
  "profilePicUrl": "https://instagram.famm3-3.fna.fbcdn.net/v/t51.82787-19/684235167_17961...",
  "bioLinks": [{ "url": "https://on.natgeo.com/instagram", "title": "" }],
  "profileTags": [],
  "input": "natgeo",
  "scrapedAt": "2026-10-21T14:27:12.162Z"
}
```

A post row (text and media links shortened here):

```json
{
  "type": "post",
  "id": "4012299642364122668",
  "code": "DeujURDilYs",
  "url": "https://www.threads.com/@natgeo/post/DeujURDilYs",
  "username": "natgeo",
  "userFullName": "National Geographic",
  "userId": "63269174602",
  "userVerified": true,
  "text": "Presented by @Rolex. Pangolins face intense pressure from illegal wildlife trafficking across Africa. ...",
  "createdAt": "2026-10-20T19:10:49.000Z",
  "likeCount": 440,
  "replyCount": 6,
  "repostCount": 24,
  "quoteCount": 2,
  "reshareCount": 2,
  "mediaType": "video",
  "media": [
    {
      "type": "video",
      "url": "https://instagram.famm3-1.fna.fbcdn.net/o1/v/t16/f2/m84/AQMclVMGauL-h2...",
      "width": 720,
      "height": 1280,
      "thumbnailUrl": "https://instagram.famm3-3.fna.fbcdn.net/v/t51.71878-15/858606769_11287..."
    }
  ],
  "hashtags": [],
  "topicTag": null,
  "mentions": ["rolex"],
  "links": [],
  "isReply": false,
  "replyToUsername": null,
  "quotedPost": null,
  "repostedPost": null,
  "isPinned": false,
  "isPaidPartnership": false,
  "isEdited": false,
  "language": null,
  "threadRootCode": "DeujURDilYs",
  "threadPosition": 1,
  "sourceType": "profile",
  "sourceInput": "natgeo",
  "scrapedAt": "2026-10-21T14:27:13.887Z"
}
```

A reply row has the same fields. These fields show the link to the post it answers:

```json
{
  "isReply": true,
  "replyToUsername": "zuck",
  "sourceType": "reply",
  "sourceInput": "https://www.threads.com/@zuck/post/Ddt7cL5EfUG",
  "replyToPostCode": "Ddt7cL5EfUG"
}
```

A status row for an account that does not exist:

```json
{
  "type": "status",
  "input": "thisuserdoesnotexist_zz9q",
  "kind": "profile",
  "status": "not_found",
  "error": "Threads shows its login page for this account: it does not exist, is private, or was removed.",
  "charged": false
}
```

### Input examples

Latest 50 posts from three accounts, without the profile row:

```json
{ "profiles": ["nasa", "@natgeo", "https://www.threads.net/@mosseri"], "maxPostsPerProfile": 50, "includeProfileDetails": false }
```

One post and up to 200 replies:

```json
{ "postUrls": ["https://www.threads.com/@zuck/post/Ddt7cL5EfUG"], "maxReplies": 200 }
```

Posts from the last 7 days only:

```json
{ "profiles": ["skynewsarabia"], "maxPostsPerProfile": 200, "postedAfter": "7 days" }
```

A quick sample for a keyword and a hashtag:

```json
{ "searchQueries": ["climate", "#nasa"] }
```

Accepted inputs: `nasa`, `@nasa`, `https://www.threads.com/@nasa`, `https://www.threads.net/@nasa`, post links like `https://www.threads.com/@nasa/post/CODE`, and short links like `https://www.threads.com/t/CODE`. Tracking parameters such as `?xmt=` are ignored.

### Alerts: only new posts

1. Turn on **Only new posts (alert mode)**.
2. Give the watchlist a name in **Alert state name**, for example `competitors`.
3. Schedule the actor, for example every hour.

The first run delivers everything and saves a baseline. Each later run delivers only posts and replies that were not there before. You pay only for the new rows. Profile rows still arrive on every run, so you can track follower counts. Turn off **Include profile details row** to skip them.

The saved state lives in the key-value store `threads-scraper-state`, under `STATE-<name>`. Use a different name for each schedule.

### Pricing

Pay per result: **$1.50 per 1,000 results** ($0.0015 per row) on the Free plan, $1.35 on Bronze, $1.20 on Silver and $1.05 on Gold. A result is one profile row or one post row. Status rows are free. Platform usage is included in the price.

You set the spending limit for each run. The actor checks it before every push. When the limit is reached, it stops and marks the remaining inputs as `skipped_cost_limit`.

What a run uses, measured on 21 October 2026:

| Run | Results | Requests | Data |
| --- | --- | --- | --- |
| 5 profiles × 20 posts, 2 post links × 20 replies | 132 | 16 | 1.6 MB |
| 1 profile × 193 posts, 1 post link × 300 replies | 495 | 26 | 1.3 MB |

That is 2.6 to 12 MB and 50 to 120 requests per 1,000 results. Short profiles cost more per result, because each profile page is about 160 KB. Runs take 2 to 8 seconds for typical inputs on 256 MB of memory.

### How it works

Threads sends logged-out visitors a page with the first posts as JSON inside the HTML. The same page names the query that loads more. The actor reads that JSON, then asks the same public endpoint for the next pages, 15 threads at a time. Pages are fetched compressed. No browser runs, so memory stays at 256 MB.

Proxy: by default the actor first connects with no proxy. If Threads blocks it, it moves to Apify datacenter proxy, then to residential proxy. You can turn off the residential step, or set your own proxy.

### Limits

These limits come from what Threads shows logged-out visitors:

- **Profiles:** about the latest 150 threads per profile. In tests this was 190 to 230 posts. Older posts need a login, which this actor does not use.
- **Replies:** Threads shows its top replies first. Some replies stay hidden from logged-out visitors. For example, a post with 6 replies showed 4.
- **Search:** only the first page of Top results, about 15 posts per keyword. The Recent tab needs a login. Use search for quick samples. For full coverage, list the accounts you care about.
- **Reposts:** the profile tab returns the account's own posts. Posts the account reposted are not in that tab.
- **View counts:** Threads does not show them to logged-out visitors, so they are not in the output.
- **Private, missing or removed accounts** all lead to the Threads login page. The status row says `not_found` and explains this.
- **Media links** are signed links from the Threads CDN. They expire after some days. Download the files soon if you need them.

This actor collects public data only. It does not log in, does not collect follower or following lists, and does not read private accounts or messages. You are responsible for using the data in line with the Threads terms and the privacy laws that apply to you.

### Run summary

Each run saves `RUN_SUMMARY` in the default key-value store. It holds the status of every input, rows by type, requests, bytes, retries and the proxy tier used.

Input statuses: `ok`, `not_found`, `invalid_input`, `no_results`, `blocked`, `api_error`, `cut_by_cost_limit`, `skipped_cost_limit`.

### Input reference

| Field | Default | What it does |
| --- | --- | --- |
| `profiles` | empty | Usernames or profile links. |
| `postUrls` | empty | Post links. Each gives the post, the author's follow-ups, parents and replies. |
| `searchQueries` | empty | Keywords or hashtags. First page of Top results only. |
| `maxPostsPerProfile` | 20 | Posts per profile. 0 = profile row only. |
| `includeProfileDetails` | true | Adds the profile row. |
| `maxReplies` | 20 | Replies per post link. 0 = none. |
| `includeParentPosts` | true | Adds the posts above a reply. |
| `maxPostsPerSearch` | 15 | Cap per keyword. |
| `postedAfter` | empty | Date or relative time, such as `2026-10-01` or `24 hours`. |
| `onlyNew` | false | Alert mode. Delivers only new posts and replies. |
| `stateName` | default | Name of the saved alert state. |
| `maxConcurrency` | 3 | Inputs fetched at once (1 to 6). |
| `proxyConfiguration` | empty | Force a proxy. Empty = automatic ladder. |
| `autoProxyFallback` | true | Move to a proxy when blocked. |
| `allowResidentialFallback` | true | Allow residential proxy as the last step. |
| `maxRetries` | 4 | Retries per request. |

With an empty input, the actor runs a small demo on the public `nasa` profile.

# Actor input Schema

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

Threads usernames or profile links. Examples: nasa, @zuck, https://www.threads.com/@natgeo, https://www.threads.net/@mosseri. You get the profile and its latest posts.

## `postUrls` (type: `array`):

Links to single Threads posts. Examples: https://www.threads.com/@zuck/post/Ddt7cL5EfUG or a short https://www.threads.com/t/CODE link. You get the post, the author's own follow-up posts, the parent posts if it is a reply, and its replies.

## `searchQueries` (type: `array`):

Optional. Keywords or hashtags such as climate or #nasa. Logged out, Threads shows only its first page of Top results (about 15 posts per keyword) and no Recent tab, so this is for quick sampling. For full coverage, list the accounts you want to follow in Profiles.

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

Newest posts to fetch per profile. A post and the author's follow-ups in the same thread each count as one post. 0 = profile details only. Logged out, Threads shows about the latest 150 threads per profile (190 to 230 posts in our tests).

## `includeProfileDetails` (type: `boolean`):

Adds one row per profile with name, bio, follower count, verified flag, bio links and profile picture.

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

Replies to fetch for each post link. 0 = no replies. Threads shows logged-out visitors the top replies first.

## `includeParentPosts` (type: `boolean`):

When a post link is itself a reply, also return the posts above it in the conversation.

## `maxPostsPerSearch` (type: `integer`):

Cap per keyword. Logged out, Threads returns about 15.

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

Optional. A date (2026-10-01) or a relative time (24 hours, 7 days, 2 weeks). Older posts are skipped and paging stops once a whole page is older.

## `onlyNew` (type: `boolean`):

Remembers what each profile, post and keyword returned last time and delivers only new posts and replies on the next run. Schedule the actor to get alerts. The first run delivers everything and saves the baseline. You pay only for new rows.

## `stateName` (type: `string`):

Name of the saved state for Only new posts. Use a different name for each watchlist or schedule.

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

How many profiles or posts to fetch at once.

## `proxyConfiguration` (type: `object`):

Leave empty for the automatic ladder: no proxy first, then Apify datacenter, then residential only if Threads blocks. Set a proxy here to force it.

## `autoProxyFallback` (type: `boolean`):

Move to a proxy when Threads blocks direct requests.

## `allowResidentialFallback` (type: `boolean`):

Residential proxy costs more platform credit. Turn off to stop at datacenter.

## `maxRetries` (type: `integer`):

Retries per request with exponential backoff when Threads answers 429 or 5xx.

## Actor input object example

```json
{
  "profiles": [
    "nasa",
    "zuck"
  ],
  "postUrls": [
    "https://www.threads.com/@zuck/post/Ddt7cL5EfUG"
  ],
  "maxPostsPerProfile": 10,
  "includeProfileDetails": true,
  "maxReplies": 10,
  "includeParentPosts": true,
  "maxPostsPerSearch": 15,
  "onlyNew": false,
  "stateName": "default",
  "maxConcurrency": 3,
  "autoProxyFallback": true,
  "allowResidentialFallback": true,
  "maxRetries": 4
}
```

# Actor output Schema

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

All rows. Filter by the type field.

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

Status per input, rows by type, requests, bytes and proxy tier used.

# 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": [
        "nasa",
        "zuck"
    ],
    "postUrls": [
        "https://www.threads.com/@zuck/post/Ddt7cL5EfUG"
    ],
    "maxPostsPerProfile": 10,
    "maxReplies": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("dottti/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 = {
    "profiles": [
        "nasa",
        "zuck",
    ],
    "postUrls": ["https://www.threads.com/@zuck/post/Ddt7cL5EfUG"],
    "maxPostsPerProfile": 10,
    "maxReplies": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("dottti/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 '{
  "profiles": [
    "nasa",
    "zuck"
  ],
  "postUrls": [
    "https://www.threads.com/@zuck/post/Ddt7cL5EfUG"
  ],
  "maxPostsPerProfile": 10,
  "maxReplies": 10
}' |
apify call dottti/threads-scraper --silent --output-dataset

```

## MCP server setup

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