# X Twitter Post Replies Scraper (`automation-lab/x-post-replies-conversation-threads`) Actor

Export direct and nested public X replies with authors, text, engagement, media, timestamps, and parent-child conversation relationships.

- **URL**: https://apify.com/automation-lab/x-post-replies-conversation-threads.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Social media
- **Stats:** 3 total users, 0 monthly users, 93.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.14 / 1,000 item extracteds

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

## X Twitter Post Replies Scraper

Export the public replies X surfaces for supplied post URLs as clean, relationship-ready records.

**X Twitter Post Replies Scraper** collects direct and nested X Twitter post replies with author details, text, timestamps, engagement counts, media, parent post IDs, conversation IDs, and reply depth. Use it for conversation analysis, community research, engagement review, and repeatable data exports without maintaining a browser workflow.

The Actor accepts public `x.com` or `twitter.com` status URLs. It follows the reply records exposed to logged-out visitors and recursively opens surfaced child replies up to your chosen depth.

### What does this X replies scraper do?

For each supplied post URL, the Actor:

1. validates and canonicalizes the X URL;
2. reads X's public logged-out conversation data;
3. identifies replies whose parent is the current focal post;
4. emits a typed record for each unique reply;
5. follows surfaced child replies when `maxDepth` is greater than 1;
6. stops at `maxItems`, `maxDepth`, or the end of surfaced data.

The default dataset contains reply records only. The supplied focal post is represented by `sourcePostId` and `sourcePostUrl`, not billed as an item.

### Who is it for?

- **Social media analysts** mapping how a public discussion branches.
- **Brand and community teams** reviewing visible responses to announcements.
- **Researchers** creating reply-author and engagement datasets.
- **Data engineers** loading normalized X reply records into warehouses or notebooks.
- **Developers** who need parent-child IDs without writing a fragile page parser.

Choose this Actor when you already know the post URLs. It does not search X by keyword or discover posts from profiles.

### Why use this Actor?

- No X login or user cookie is required.
- Both direct and nested reply relationships are represented.
- Records include useful author, media, timestamp, and engagement fields.
- Invalid, private, deleted, or unsupported pages fail visibly instead of returning misleading empty success.
- Limits, depth, retries, and request delay are bounded.
- The default dataset works with Apify exports, webhooks, integrations, and API clients.

### What X reply data can I extract?

| Field | Meaning |
| --- | --- |
| `postId` | Stable X ID of the reply |
| `postUrl` | Canonical public URL of the reply |
| `sourcePostId` | ID from the supplied source URL |
| `sourcePostUrl` | Canonical supplied source URL |
| `conversationId` | Highest conversation ancestor exposed on the source page |
| `parentPostId` | ID of the post this record replies to |
| `depth` | Relationship depth from the supplied source post |
| `isDirectReply` | `true` when `depth` is 1 |
| `text` | Public reply text |
| `createdAt` | Reply timestamp when exposed |
| `authorId` | Public X author ID when exposed |
| `authorUsername` | Author handle |
| `authorName` | Display name when exposed |
| `authorProfileUrl` | Canonical author profile URL |
| `authorAvatarUrl` | Public avatar URL when exposed |
| `authorFollowers` | Visible follower count when exposed |
| `authorVerified` | Visible X verification state when exposed |
| `replyCount` | Visible reply count |
| `repostCount` | Visible repost count |
| `likeCount` | Visible like count |
| `quoteCount` | Visible quote count |
| `viewCount` | Visible view count |
| `bookmarkCount` | Visible bookmark count |
| `mediaUrls` | Public image or best available video URLs |
| `mediaTypes` | Media types aligned with `mediaUrls` |
| `scrapedAt` | UTC extraction timestamp |

Fields that X does not expose for a particular record are omitted rather than invented.

### How to get started

1. Open the Actor input page.
2. Paste one or more public X post URLs into **X post URLs**.
3. Set **Maximum replies** for the total output cap.
4. Use depth `1` for direct replies or `2`–`5` for nested relationships.
5. Start the run.
6. Open the **Reply conversation** dataset view.
7. Export JSON, CSV, Excel, XML, or RSS, or consume the dataset through the API.

A small direct-replies input is:

```json
{
  "startUrls": [
    { "url": "https://x.com/elonmusk/status/1675187969420828672" }
  ],
  "maxItems": 2,
  "maxDepth": 1,
  "requestDelayMs": 250
}
```

### Input parameters

#### `startUrls`

Required array of public `x.com` or `twitter.com` post URLs. URL fragments and alternate Twitter hostnames are normalized. Profile URLs, searches, lists, and non-X URLs are rejected.

#### `maxItems`

Maximum unique reply records across all supplied URLs. Default: `50`. Range: `1`–`500`.

#### `maxDepth`

Number of parent-child levels to follow from every supplied post. Default: `2`. Range: `1`–`5`.

Depth `1` returns only replies whose parent is the supplied post. Higher values request surfaced child pages and can produce nested records.

#### `requestDelayMs`

Delay between public X page requests. Default: `250` milliseconds. Range: `0`–`10000`. Increase it when running a larger nested crawl repeatedly.

The Actor does not enable a paid proxy fallback. Direct public requests currently provide the product data at lower cost.

### Output example

A current reply record has this shape:

```json
{
  "recordType": "reply",
  "postId": "1675189603911827457",
  "postUrl": "https://x.com/losxbt/status/1675189603911827457",
  "sourcePostId": "1675187969420828672",
  "sourcePostUrl": "https://x.com/elonmusk/status/1675187969420828672",
  "conversationId": "1675187969420828672",
  "parentPostId": "1675187969420828672",
  "depth": 1,
  "isDirectReply": true,
  "text": "@elonmusk Bro wtf is this 💀 https://t.co/UNmXAKbmst",
  "createdAt": "2023-07-01T17:08:19.000Z",
  "authorUsername": "losxbt",
  "authorProfileUrl": "https://x.com/losxbt",
  "replyCount": 216,
  "likeCount": 96265,
  "viewCount": 5387003,
  "mediaUrls": ["https://pbs.twimg.com/media/Fz94ENmWIAAt3Z9.jpg"],
  "mediaTypes": ["photo"],
  "scrapedAt": "2026-08-30T20:10:00.000Z"
}
```

Engagement values can change after extraction. Use `scrapedAt` to identify the observation time.

### How much does it cost to extract X post replies?

Pricing uses one start event plus one `item` event per reply saved to the default dataset. The focal source post is not charged as an item.

Apify shows the applicable price before every run. The launch pricing contract is:

- **Start:** $0.001 per run.
- **BRONZE item:** $0.00024 per delivered reply.
- Lower per-item tiers apply on SILVER through DIAMOND plans.

Example Actor charges at the BRONZE rate are approximately:

| Delivered replies | Actor charge |
| ---: | ---: |
| 1 | $0.00124 |
| 10 | $0.0034 |
| 100 | $0.025 |

Apify displays the applicable plan tier and maximum charge controls. These prices were checked against measured current-build runtime and exact X reply competitors.

### Direct replies and nested thread mapping

Use `maxDepth: 1` when the job is a flat engagement export around one source post.

Use a higher depth when you need:

- parent-child edges for a conversation graph;
- direct versus nested classification;
- branch-level author or engagement analysis;
- downstream thread reconstruction.

Sort by `depth` and join `parentPostId` to `postId` to rebuild the surfaced tree. Keep `sourcePostId` in the join key when one dataset contains several source URLs.

### Integrations and workflow ideas

- Send completed datasets to Google Sheets with an Apify integration.
- Trigger a webhook and load records into BigQuery or Snowflake.
- Schedule recurring runs and compare reply IDs in your own storage.
- Build a parent-child graph in NetworkX, Neo4j, or a BI tool.
- Run language or sentiment classification downstream.
- Join reply authors with another authorized public-profile dataset.

The Actor returns current observations. It does not maintain a change-history database or send alerts by itself.

### API usage

Replace `<APIFY_TOKEN>` with your Apify API token.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~x-post-replies-conversation-threads/runs?token=<APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"startUrls":[{"url":"https://x.com/elonmusk/status/1675187969420828672"}],"maxItems":6,"maxDepth":2}'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/x-post-replies-conversation-threads').call({
  startUrls: [{ url: 'https://x.com/elonmusk/status/1675187969420828672' }],
  maxItems: 6,
  maxDepth: 2,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("automation-lab/x-post-replies-conversation-threads").call(
    run_input={
        "startUrls": [{"url": "https://x.com/elonmusk/status/1675187969420828672"}],
        "maxItems": 6,
        "maxDepth": 2,
    }
)
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

For synchronous API calls, use the Actor run-sync endpoint and keep the input small enough for the client timeout.

### Use with Apify MCP

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/x-post-replies-conversation-threads"
```

#### Claude Desktop, Cursor, and VS Code MCP setup

Use this equivalent JSON configuration in Claude Desktop, Cursor, or VS Code:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/x-post-replies-conversation-threads"
    }
  }
}
```

Example prompts:

- “Run the X replies Actor on this public post and return direct replies only.”
- “Export up to 50 surfaced replies with depth 3, then group authors by branch.”
- “Map each reply ID to its parent post ID from this run.”

### Coverage and limitations

X controls which replies are visible to logged-out visitors. The Actor exports that ranked, surfaced subset; it does **not** promise every reply from conversations with hundreds or thousands of responses.

Other limitations:

- Private, deleted, suspended, age-restricted, or login-only posts are unsupported.
- Reply ranking and visible engagement can change between runs.
- A page can expose fewer records than `maxItems`.
- The Actor does not search by keyword, scrape profiles, or authenticate to X.
- The Actor does not bypass access controls or CAPTCHA challenges.
- Changes to X's public Relay data shape can temporarily cause explicit run failures.
- Deep crawls make more source requests and take longer than direct-only runs.

A successful run with fewer rows than requested means X exhausted the surfaced branch, not that the Actor silently invented pagination.

### Reliability tips

- Start with depth `1` or `2` and a small item cap.
- Keep the default request delay for recurring jobs.
- Increase `requestDelayMs` if X responds with temporary rate limits.
- Use multiple runs rather than one unbounded input batch.
- Inspect the failed URL in logs when a post was deleted or made private.
- Store `postId` and `scrapedAt` for reliable downstream deduplication.

Transient network, HTTP 429, and temporary 5xx failures receive a bounded retry. Stable invalid-input and unavailable-page failures do not loop indefinitely.

### Troubleshooting

#### The run returned fewer replies than `maxItems`

`maxItems` is a cap, not a promise. X may surface only a small ranked set for that post or branch. Increase depth only when nested replies are useful; it does not create unavailable rows.

#### The run says the page has no public conversation data

Confirm that the URL opens publicly while logged out, contains `/status/<numeric-id>`, and is not deleted, private, suspended, or age-restricted. Retry later only when the page is temporarily unavailable.

#### A URL is rejected before extraction

Use a complete URL such as `https://x.com/username/status/1234567890`. Search URLs, profile URLs, and shortened links are intentionally rejected.

#### Engagement differs from a prior export

Counts are current source observations and can change. Compare records using `postId` and observation time using `scrapedAt`.

### Responsible use and legality

Use this Actor only for public information you are authorized to collect and process.

You are responsible for:

- complying with applicable laws and contractual obligations;
- respecting privacy, data-protection, intellectual-property, and platform rules;
- minimizing personal data collection;
- securing exports and controlling retention;
- avoiding harassment, surveillance, discrimination, spam, or harmful profiling.

The Actor does not authenticate as a user or access private posts. Public availability does not remove your responsibility to use the data lawfully.

### Related Automation Lab Actors

- [X Communities Members & Posts](https://apify.com/automation-lab/x-communities-members-posts) for public Community metadata, surfaced members, and posts.
- [Twitter/X Lists Scraper](https://apify.com/automation-lab/twitter-lists-scraper) for public List metadata, members, and posts.

These products solve different discovery jobs. Use this Actor when the starting point is a known post URL and reply relationships are the required output.

### FAQ

#### Does it require an X account or cookies?

No. The Actor uses data X currently exposes to logged-out visitors.

#### Does it return every reply?

No. It returns the direct and nested replies surfaced on public logged-out pages. Large conversations are commonly partial.

#### Can it collect only direct replies?

Yes. Set `maxDepth` to `1`.

#### Can it process several post URLs?

Yes. Add several entries to `startUrls`; `maxItems` applies across the full run.

#### Are nested relationships preserved?

Yes. Use `parentPostId`, `depth`, `isDirectReply`, and `conversationId`.

#### Are media files downloaded?

No. Public media URLs and types are included. The Actor does not download or store the media bytes.

#### Can I schedule monitoring?

You can schedule recurring Actor runs, but comparison, alerts, and historical retention belong in your downstream workflow.

#### Is the source post included as a charged dataset item?

No. The default dataset contains reply records. The source is referenced by `sourcePostId` and `sourcePostUrl` on every row.

# Actor input Schema

## `startUrls` (type: `array`):

Public x.com or twitter.com post URLs. Each URL is treated as the source post whose surfaced reply tree will be followed.

## `maxItems` (type: `integer`):

Stop after this many unique reply records across all source posts.

## `maxDepth` (type: `integer`):

How many parent-child levels to follow from each supplied post. Use 1 for direct replies only; up to 5 for nested conversations.

## `requestDelayMs` (type: `integer`):

Polite delay between public post-page requests. Increase this if X temporarily rate-limits repeated runs.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://x.com/elonmusk/status/1675187969420828672"
    }
  ],
  "maxItems": 20,
  "maxDepth": 2,
  "requestDelayMs": 250
}
```

# Actor output Schema

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

Default dataset containing all delivered reply records

# 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 = {
    "startUrls": [
        {
            "url": "https://x.com/elonmusk/status/1675187969420828672"
        }
    ],
    "maxItems": 20,
    "maxDepth": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/x-post-replies-conversation-threads").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 = {
    "startUrls": [{ "url": "https://x.com/elonmusk/status/1675187969420828672" }],
    "maxItems": 20,
    "maxDepth": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/x-post-replies-conversation-threads").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 '{
  "startUrls": [
    {
      "url": "https://x.com/elonmusk/status/1675187969420828672"
    }
  ],
  "maxItems": 20,
  "maxDepth": 2
}' |
apify call automation-lab/x-post-replies-conversation-threads --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/x-post-replies-conversation-threads"
        }
    }
}

```

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/VEochvBSDk660TCur/builds/tU0vF5Dbg3ar9u9wY/openapi.json
