# Threads Scraper API (`cleanscrape/threads-scraper`) Actor

Collect public Threads posts from accounts or post links. Export original text, likes, replies, images, videos and shared links, with a report showing what was collected. No Threads login required. Maintained by CleanScrape.

- **URL**: https://apify.com/cleanscrape/threads-scraper.md
- **Developed by:** [CleanScrape](https://apify.com/cleanscrape) (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.19 / 1,000 post saveds

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 API

Collect public Threads posts from accounts or specific post links. Export the original text, available engagement counts, images, videos and shared links. No Threads login or account cookies are needed.

Use it to review an account's public posts, compare a shortlist of posts, or collect examples for content research. A separate run report explains what was collected and where the source stopped.

**From $0.0298 for 20 saved posts.** Pay for posts saved, with no startup fee. Bronze, Silver and Gold discounts are available.

### Watch the walkthrough

Let's use NASA as an example: collect 20 public posts, check what was collected, and export a shortlist to CSV or Excel. Use your own public account or specific post links for another project.

https://www.youtube.com/watch?v=BV8qxrID4\_0

### Start with a small example

[Open the 20-post example](https://apify.com/cleanscrape/threads-scraper/examples/threads-account-posts-example), or enter the same settings yourself:

1. Select **Account posts**.
2. In **Accounts or post links**, enter `nasa`. Replace it with your own example when you are ready.
3. Set **Maximum posts** to `20`, then start the run. The date filter is optional.

Open **Posts** for the results. To check coverage, open the **Posts** menu in **Output**, select **Run report**, then open **REPORT**. The maximum applies to the whole run, not separately to each account. It is a limit, not a guaranteed result count.

The same example as JSON input:

```json
{
  "mode": "accounts",
  "targets": ["nasa"],
  "maxResults": 20,
  "timeRange": "any"
}
```

### Choose what to collect

| Selection | What to enter | What it collects |
| --- | --- | --- |
| Account posts | Handles such as `nasa`, `@nasa`, or full profile links | Account posts available through the public source |
| Specific posts | Full Threads post links | The posts you selected, without collecting an account feed |
| Replies (experimental) | Full links to posts whose replies you want | Replies available from the public conversation source |

Enter one item per line. When switching from Account posts to a link-based mode, replace the handles in the same list with full post links. Both `threads.com` and older `threads.net` links are accepted.

For example, collect one selected post:

[Try the post-links example](https://apify.com/cleanscrape/threads-scraper/examples/threads-post-links-example) to compare selected posts without reading an entire account feed.

```json
{
  "mode": "posts",
  "targets": ["https://www.threads.com/@nasa/post/DZceA72Drjf"],
  "maxResults": 5
}
```

This requests one post, even though the maximum is five. For replies to that post, change `mode` to `replies`. The source may return fewer replies than the count shown on Threads.

This Actor does not search Threads by keyword or export follower lists. Replies are experimental: some replies may be missing, and the output does not reconstruct who replied to whom.

#### Collect older account posts

Keep **Account posts** and **Any time** selected, then increase **Maximum posts**. The Actor follows the public feed backwards until the source ends, your limit is reached, or a safety stop is needed. The maximum is 10,000 saved posts per run; 20 remains the default for a quick first try.

[Try the older-posts example](https://apify.com/cleanscrape/threads-scraper/examples/threads-account-history-example) with a 200-post limit. Replace the example account with your own starting point.

For a longer export, allow up to 60 minutes under Apify's run options and set a spending limit you are comfortable with. The Actor saves its report as it works. A larger maximum does not guarantee that more posts are available.

**Entire available feed is not the same as guaranteed entire history.** Threads can omit old posts or return incomplete pages without a login. The report distinguishes reaching the available feed's end from stopping early. Use it before treating an export as a complete archive.

### Optional date filter

Leave **Any time** selected for a standard run, or choose **Last 7 days**, **Last 30 days**, or **Choose dates**. Custom dates use calendar controls. Dates left in those controls are ignored when you switch to another option.

For JSON input, use `timeRange: "custom"` with `startDate` and `endDate` in `YYYY-MM-DD` format. The end date includes the whole UTC calendar day.

Filtering keeps matching posts from what the source supplies; it does not unlock older or unavailable posts. Pinned posts and source ordering can make dates appear out of order. Sort by **Published (UTC)** when needed.

### Read the results

| Posts column | Meaning |
| --- | --- |
| Author | The account that wrote the post |
| Post text | Original text, without rewriting or generated summaries |
| Published (UTC) | When the post was published |
| Likes, Replies, Reposts, Quotes | Counts supplied by Threads at collection time |
| Post link | A link back to the source post |

**Images and videos** separates media attachments for inspection. **Shared and quoted content** shows link previews and quoted posts. Full JSON retains IDs and nested objects for programmatic use.

A blank count or `null` means the source did not provide that value, not zero. A captionless post can still contain useful media, a shared link or a quote. Link titles remain in `linkPreview`; they do not replace the author's words.

If Threads explicitly marks a quoted post unavailable, the Actor can still save the readable original post. Its unavailable quote stays blank, and the report notes this. Deleted or inaccessible quoted content is not reconstructed.

Media URLs may expire. Download files you need while the links work. This is not a permanent media archive, and the Actor does not visit shared external websites.

Replies include the requested `rootPostId`. The immediate `parentPostId` stays `null` because that relationship has not been established reliably; the output is not a reconstructed reply tree.

### Export or automate

Use Apify's dataset export controls for JSON, CSV or Excel, choosing the view you need. **Spreadsheet export** provides a separate, compact CSV with one row per post and protection against spreadsheet formulas in collected text. Original JSON values stay unchanged. Keep long post IDs as text in spreadsheets to avoid rounding.

The Actor's **API** tab provides request examples for automation. Send the same input fields shown above and retrieve the run's default dataset. Keep your Apify token in an environment variable or credential manager, not in a public URL or shared workflow.

Each new run is a separate snapshot. Match `postId` across runs and use `observedAt` for collection time. New-only monitoring and cross-run duplicate removal are not built in.

### Check coverage

**Run report** lists each account or link, saved results, filtered or excluded records, and stopping reasons. **Coverage JSON** contains the machine-readable version.

**Checked** means the available source ended and queued records were processed, not that complete history was recovered. **Partial** means collection stopped early, a record was excluded, or a source warning prevents confirming coverage. Read the stopping reason and any coverage notes together. An empty dataset after a failure is not evidence that an account has no posts.

If Threads returns an incomplete page, the Actor can check individual posts before saving them. The report still warns that other posts may be missing. Access blocks, rate limits and unexpected source errors stop collection rather than being hidden.

The limit is shared across accounts. Each active account is checked in turn, so results can be interleaved. Use separate runs if you need a fixed sample from each account.

The Actor does not bypass login requirements or access blocks. It can try another public page when a post response is incomplete, but it does not retry indefinitely.

If an interrupted write cannot be confirmed in the dataset, it is preserved under **Recovery records** rather than blindly written again. A resumed run reconciles its existing storage. Starting a new run creates a new snapshot, not a continuation.

### Pricing

The base price is **$1.49 per 1,000 saved posts**. Each post saved to the default dataset is one event, including a reply in reply mode. Images, videos and quoted content attached to that post are not extra results. There is no startup fee or separate platform-usage charge for customers.

| Saved posts | Base cost |
| --- | --- |
| 20 | $0.0298 |
| 100 | $0.149 |
| 1,000 | $1.49 |

Bronze receives **10% off**, Silver **15% off**, and Gold, Platinum and Diamond **20% off** the base event price. Apify applies the eligible tier automatically; check the Pricing tab for your rate.

Reports, filtered-out posts and separate recovery records are not additional billed results. If a run stops early, posts already saved are still billable. Set **Maximum posts** and Apify's **Maximum cost per run** to control your job. New runs are new snapshots and can return previously collected posts again.

### Questions or a problem with a run?

Maintained by CleanScrape. Open an Actor issue or email <contact.cleanscrape@gmail.com> with the run ID and a non-sensitive example of what you expected. Please do not send API tokens, cookies or private exports.

If you have used the Actor, an honest review or a note about a step that was unclear helps us improve it.

**Disclaimer:** This independent tool is not affiliated with, endorsed by or sponsored by Meta. Threads and all other trademarks belong to their respective owners. Collect and use public data responsibly and in accordance with applicable requirements.

# Actor input Schema

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

Account posts collects an account's public posts. Specific posts collects only the links you enter. Replies collects available replies; some may be missing.

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

For Account posts, use nasa, @nasa or a profile link. For Specific posts or Replies, use a full post link, such as https://www.threads.com/@nasa/post/DZceA72Drjf. Add one per line.

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

Total across all accounts or links. Increase it for older account posts. This is a limit, not a guaranteed result count.

## `timeRange` (type: `string`):

Keep posts published during this period. Older or unavailable posts may still be missing.

## `startDate` (type: `string`):

Used only with Choose dates. Select the first included date; dates use UTC.

## `endDate` (type: `string`):

Used only with Choose dates. The whole final UTC date is included.

## Actor input object example

```json
{
  "mode": "accounts",
  "targets": [
    "nasa"
  ],
  "maxResults": 20,
  "timeRange": "any"
}
```

# Actor output Schema

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

No description

## `report` (type: `string`):

No description

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

No description

## `export` (type: `string`):

No description

## `recovery` (type: `string`):

Only populated when a restart preserved a delivery with an uncertain outcome.

# 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": [
        "nasa"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cleanscrape/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 = { "targets": ["nasa"] }

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

```

## MCP server setup

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