# TikTok Comment & Reply Thread Extractor (`vexaloom-labs/tiktok-comment-and-reply-thread-extractor`) Actor

Pull comments and reply threads from TikTok videos by URL or post ID, with pinned/hearted-by-author flags, commenter details, and like counts. No TikTok account or cookies required.

- **URL**: https://apify.com/vexaloom-labs/tiktok-comment-and-reply-thread-extractor.md
- **Developed by:** [vexaloom-labs](https://apify.com/vexaloom-labs) (community)
- **Categories:** Automation, Social media, Videos
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

## TikTok Comment & Reply Thread Extractor

Pull the comment section of any public TikTok video — including the full reply thread under each comment — with no TikTok account, no cookies and no proxy setup. Every comment and reply becomes one dataset row, with its author, like count, timestamps and creator-engagement flags.

### What this actor does

- Accepts TikTok video URLs, numeric video IDs, and/or profile names (scrapes the newest videos of each profile)
- Walks the comment section as deep as TikTok serves it, recovering far past the first few hundred comments on huge videos
- Fetches the reply thread under every root comment — replies are paged past TikTok's first reply batch, not truncated
- Emits each root comment followed immediately by its own replies, linked by `replyToCommentId` (and `replyToReplyId` for nested answers)
- Flags pinned comments, creator-hearted comments, and translation availability
- Captures commenter handle, display name, numeric user ID, secUid and avatar, plus image and sticker comments
- Optionally attaches the source video's caption and stats (`includeVideoMetadata`)
- Optionally adds each commenter's region code and bio (`includeCommenterDetails`) and exact follower/following/heart/video counts (`includeCommenterStats`)
- `deepWalk` mode recovers far more comments on videos where TikTok's default list stops early, restoring image/sticker data from the web payload per comment ID
- Reports videos that are deleted, private or nonexistent as "unavailable" instead of silently empty
- Filters by minimum likes (`minLikes`) and caps rows per video (`maxCommentsPerPost`) or per run (`maxItems`)
- Omits empty fields everywhere — no nulls, no placeholders

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `postUrls` | array | — | TikTok video/photo URLs to scrape comments from |
| `postIds` | array | — | Numeric video IDs (15–20 digits), alternative to URLs |
| `profiles` | array | — | Usernames or profile URLs — comments on their newest videos |
| `maxVideosPerProfile` | integer | `10` | How many of each profile's newest videos to scrape |
| `maxCommentsPerPost` | integer | – (100 if not set) | Maximum rows per video (replies included) |
| `includeReplies` | boolean | `true` | Fetch and emit reply threads |
| `maxRepliesPerComment` | integer | `0` | Cap replies per root comment; `0` = all |
| `maxItems` | integer | `0` | Global row cap for the run; `0` = no cap |
| `minLikes` | integer | `0` | Keep only comments/replies with at least this many likes |
| `includeVideoMetadata` | boolean | `false` | Attach a `video` object (caption, author, stats) to every row |
| `includeCommenterDetails` | boolean | `false` | Add `user.region` and `user.bio` to every row |
| `includeCommenterStats` | boolean | `false` | Add exact `user.followerCount` / `followingCount` / `heartCount` / `videoCount` |
| `maxCommenterLookups` | integer | `100` | Cap on how many commenters are enriched by `includeCommenterStats`; `0` = no cap |
| `deepWalk` | boolean | `false` | Read TikTok's app-style view of the comment list (much higher coverage on many videos) and restore image/sticker fields per comment; off keeps the standard walk |

#### Example: comments and replies for one video

```json
{
  "postUrls": ["https://www.tiktok.com/@khaby.lame/video/7574844184952081686"],
  "maxCommentsPerPost": 500,
  "includeReplies": true
}
```

#### Example: top comments only, no replies

```json
{
  "postUrls": ["https://www.tiktok.com/@natgeo/video/7640880319159094559"],
  "maxCommentsPerPost": 200,
  "includeReplies": false
}
```

#### Example: comments from a profile's newest videos

```json
{
  "profiles": ["mrbeast"],
  "maxVideosPerProfile": 3,
  "maxCommentsPerPost": 200,
  "maxItems": 1000
}
```

#### Example: only highly-liked comments, with video metadata

```json
{
  "postIds": ["7574844184952081686", "7640880319159094559"],
  "minLikes": 100,
  "includeVideoMetadata": true
}
```

### Output per comment or reply

- `rowType` — `comment` for top-level comments, `reply` for replies
- `commentId` — unique TikTok comment ID
- `postId` — ID of the video the comment belongs to
- `postUrl` — URL of the video
- `text` — comment text (absent on image/sticker-only comments)
- `createTime` / `createdAt` — Unix and ISO 8601 creation time
- `likeCount` — likes on the comment
- `replyCount` — replies TikTok reports for this comment (comments only)
- `isPinnedByAuthor` — pinned by the video creator
- `isLikedByAuthor` — hearted by the video creator
- `isReply` — true on reply rows
- `replyToCommentId` — root comment ID (reply rows)
- `replyToReplyId` — the reply being answered (nested replies)
- `replyToUsername` / `replyToNickname` / `replyToUserId` — who the reply answers
- `threadId` — TikTok's thread identifier for the reply chain
- `language` — detected comment language
- `isTranslatable` — TikTok offers a translation
- `shareUrl` — share link that opens the comment
- `user.id` / `user.secUid` / `user.username` / `user.displayName` / `user.avatarUrl` — commenter details
- `user.verified` — present (true) when the commenter carries a verification badge
- `user.region` / `user.bio` — commenter country/region code and profile bio (only with `includeCommenterDetails`)
- `user.followerCount` / `user.followingCount` / `user.heartCount` / `user.videoCount` — exact commenter stats (only with `includeCommenterStats`)
- `images` — raw image list for image comments
- `stickers` — raw sticker data for sticker comments
- `labels` / `textExtra` — raw TikTok annotations (mentions, hashtags, labels)
- `video` — caption and stats of the source video (only with `includeVideoMetadata`)
- `scrapedAt` — ISO 8601 collection time

### Use cases

- **Sentiment & topic analysis** — build comment corpora per video, per creator or per campaign
- **Creator engagement audits** — pinned and creator-hearted comments surface the conversations a creator endorses
- **Reply-thread research** — reconstruct who answered whom with `replyToCommentId` / `replyToReplyId`
- **Brand monitoring** — sweep a profile's newest videos and catch complaints or questions early
- **Moderation exports** — pull full threads into CSV/JSON for review tooling
- **Academic research** — comment and reply networks with exact timestamps and stable IDs

### FAQ

**Do I need a TikTok account, cookies or a proxy?**
No. The actor reads TikTok's public comment data anonymously — no login, no cookies, no proxy setup. Pacing and retries are handled automatically.

**How many comments can I get from one video?**
As many as TikTok serves. The actor keeps walking the comment list past the point where a basic scraper stops. With `deepWalk` it reads TikTok's app-style view of the list, which exposes far more comments on many videos (for one video: ~42 via the standard walk, 131–160 via the app view), and restores image and sticker data from the standard view for each comment. Coverage still varies by network exit and by how many comments TikTok filters (spam, deleted, region-restricted) or withholds from anonymous readers, and the count shown in the app is live and can change during a run. The actor never invents rows.

**How complete are reply threads?**
Replies are fetched per comment with their own cursor walk and capped only by your inputs. A thread with thousands of replies is collected across pages until it ends or your cap is reached.

**What happens with deleted, private or comment-disabled videos?**
Deleted, private and nonexistent videos are detected from the video page and reported as **unavailable** in the status message; videos that exist but have no (or disabled) comments are reported as empty. Either way the actor emits no rows for that video and moves on.

**Can I sort or filter comments?**
Sorting and date filters are not available: TikTok returns comments in its own engagement-based ranking order and ignores sort/date parameters, so the actor offers none instead of silently dropping rows. You can trim by likes with `minLikes` — comments and replies below the threshold are left out; keyword filtering is best done in your own pipeline on the exported text.

**How do I get commenter details and follower counts?**
Two optional toggles. `includeCommenterDetails` adds the commenter's region code and bio (`user.region`, `user.bio`). `includeCommenterStats` adds exact follower, following, heart and video counts (`user.followerCount`, …) by looking up each unique commenter's public profile once; private or banned accounts are omitted, never guessed, and `maxCommenterLookups` caps how many commenters are enriched per run.

**Can comments be translated?**
No. TikTok's payload carries no translated text and the translation endpoint requires signed requests; the actor exports the original text plus `language` and `isTranslatable` for downstream translation.

**Why do some comments have no `text`, and do media URLs expire?**
Image-only and sticker-only comments have no text; their attachments are in `images` / `stickers`. Avatar and sticker URLs are signed TikTok CDN links that work when exported but expire after roughly a day or two — use `commentId`, `user.username`, `shareUrl` and `postUrl` as the durable keys.

**Is this affiliated with TikTok?**
No. This is an independent, third-party actor that reads TikTok's publicly available comment data; it is not affiliated with, endorsed by, or operated by TikTok.

# Actor input Schema

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

TikTok video or photo URLs to scrape comments from (e.g. https://www.tiktok.com/@user/video/123...).

## `postIds` (type: `array`):

Numeric TikTok video IDs (15-20 digits) as an alternative to URLs.

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

TikTok usernames or profile URLs. The actor scrapes comments from their newest videos, up to 'Max videos per profile'.

## `maxVideosPerProfile` (type: `integer`):

How many of each profile's newest videos to scrape comments from.

## `maxCommentsPerPost` (type: `integer`):

Maximum number of rows per video, counting replies when 'Include replies' is on. The actor walks the comment section as deep as TikTok serves it.

## `includeReplies` (type: `boolean`):

Fetch the reply thread under each root comment and emit each reply as its own row right after its parent.

## `maxRepliesPerComment` (type: `integer`):

Cap the replies fetched for any single root comment. Set to 0 for all replies (still bounded by 'Max comment rows per video').

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

Global cap on the number of rows in the run (comments + replies). Set to 0 for no global cap.

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

Only keep comments and replies with at least this many likes. Comments below the threshold are skipped before their replies are fetched.

## `includeVideoMetadata` (type: `boolean`):

Add a 'video' object (caption, author, view/like/comment counts) to every row.

## `includeCommenterDetails` (type: `boolean`):

Add the commenter's region code and bio to user.region / user.bio, matched by comment ID from TikTok's app-style view of the page.

## `includeCommenterStats` (type: `boolean`):

Add exact follower, following, heart and video counts to user.\* for every commenter. Each commenter's public profile is looked up once; private or banned accounts are omitted.

## `maxCommenterLookups` (type: `integer`):

Cap on how many commenters are enriched when 'Include commenter follower counts' is on. Set to 0 for no cap.

## `deepWalk` (type: `boolean`):

Read TikTok's app-style view of the comment list, which reaches far more comments on many videos, and restore image/sticker data from the standard view per comment. Off keeps the standard walk.

## Actor input object example

```json
{
  "postUrls": [
    "https://www.tiktok.com/@khaby.lame/video/7574844184952081686"
  ],
  "maxVideosPerProfile": 5,
  "maxCommentsPerPost": 5,
  "includeReplies": true,
  "maxRepliesPerComment": 5,
  "maxItems": 0,
  "minLikes": 0,
  "includeVideoMetadata": false,
  "includeCommenterDetails": false,
  "includeCommenterStats": false,
  "maxCommenterLookups": 100,
  "deepWalk": false
}
```

# Actor output Schema

## `commentId` (type: `string`):

Unique numeric ID of the comment or reply.

## `postId` (type: `string`):

Numeric ID of the TikTok video this comment belongs to.

## `postUrl` (type: `string`):

URL of the TikTok video.

## `rowType` (type: `string`):

'comment' for top-level comments, 'reply' for replies.

## `text` (type: `string`):

The text content of the comment or reply.

## `createdAt` (type: `string`):

ISO 8601 timestamp when the comment was posted.

## `likeCount` (type: `string`):

Number of likes on the comment.

## `replyCount` (type: `string`):

Total number of direct replies to this comment.

## `isPinnedByAuthor` (type: `string`):

True if the comment is pinned by the video author.

## `isLikedByAuthor` (type: `string`):

True if the video author has liked this comment.

## `isReply` (type: `string`):

True when this row is a reply to another comment rather than a top-level comment.

## `replyToCommentId` (type: `string`):

ID of the root comment this row replies to. Present only on reply rows.

## `replyToReplyId` (type: `string`):

For nested replies, the ID of the reply this row answers.

## `replyToUsername` (type: `string`):

Handle of the comment/reply being answered (reply rows).

## `replyToNickname` (type: `string`):

Display name of the comment/reply being answered (reply rows).

## `replyToUserId` (type: `string`):

Numeric user ID of the comment/reply being answered (reply rows).

## `threadId` (type: `string`):

ID grouping a root comment with its replies, when TikTok provides one.

## `language` (type: `string`):

Detected language code of the comment (e.g. 'en', 'es'). May be absent.

## `isTranslatable` (type: `string`):

Whether TikTok offers a translation for this comment.

## `shareUrl` (type: `string`):

TikTok share link that opens this comment.

## `scrapedAt` (type: `string`):

ISO 8601 timestamp when the row was collected.

## `video` (type: `string`):

Caption, author and stats of the source video (only when 'Include video metadata' is on).

# 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 = {
    "postUrls": [
        "https://www.tiktok.com/@khaby.lame/video/7574844184952081686"
    ],
    "maxVideosPerProfile": 5,
    "maxCommentsPerPost": 5,
    "includeReplies": true,
    "maxRepliesPerComment": 5,
    "maxItems": 0,
    "minLikes": 0,
    "includeVideoMetadata": false,
    "includeCommenterDetails": false,
    "includeCommenterStats": false,
    "maxCommenterLookups": 100,
    "deepWalk": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("vexaloom-labs/tiktok-comment-and-reply-thread-extractor").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 = {
    "postUrls": ["https://www.tiktok.com/@khaby.lame/video/7574844184952081686"],
    "maxVideosPerProfile": 5,
    "maxCommentsPerPost": 5,
    "includeReplies": True,
    "maxRepliesPerComment": 5,
    "maxItems": 0,
    "minLikes": 0,
    "includeVideoMetadata": False,
    "includeCommenterDetails": False,
    "includeCommenterStats": False,
    "maxCommenterLookups": 100,
    "deepWalk": False,
}

# Run the Actor and wait for it to finish
run = client.actor("vexaloom-labs/tiktok-comment-and-reply-thread-extractor").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 '{
  "postUrls": [
    "https://www.tiktok.com/@khaby.lame/video/7574844184952081686"
  ],
  "maxVideosPerProfile": 5,
  "maxCommentsPerPost": 5,
  "includeReplies": true,
  "maxRepliesPerComment": 5,
  "maxItems": 0,
  "minLikes": 0,
  "includeVideoMetadata": false,
  "includeCommenterDetails": false,
  "includeCommenterStats": false,
  "maxCommenterLookups": 100,
  "deepWalk": false
}' |
apify call vexaloom-labs/tiktok-comment-and-reply-thread-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vexaloom-labs/tiktok-comment-and-reply-thread-extractor"
        }
    }
}
```

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/vHUfV4HYEI8naCh9G/builds/5xR7gMTgKlauNkvWC/openapi.json
