# Tiktok Comments Scraper (`scrapier/tiktok-comments-scraper`) Actor

Automate TikTok comment collection with ease. TikTok Comments Scraper pulls full comment threads, likes, authors, and posting times. Perfect for trend analysis, brand monitoring, sentiment tracking, and data-driven content research.

- **URL**: https://apify.com/scrapier/tiktok-comments-scraper.md
- **Developed by:** [Scrapier](https://apify.com/scrapier) (community)
- **Categories:** Automation, Lead generation, Videos
- **Stats:** 34 total users, 6 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$24.99/month + usage

To use this Actor, you pay a monthly rental fee to the developer. The rent is subtracted from your prepaid usage every month after the free trial period.You also pay for the Apify platform usage, which gets cheaper the higher Apify subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#rental-actors

## 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

### TikTok Comments Scraper — Comments With Full Nested Reply Threads

TikTok Comments Scraper pulls comments and nested reply threads from any public TikTok video, whether you hand it direct video URLs or a creator's username. Every run returns structured JSON: comment text, like counts, timestamps, commenter usernames and IDs, @mentions, and — unlike a plain top-level comment list — full reply threads paged past TikTok's 20-reply-per-page ceiling. Output ships as JSON, CSV, or Excel, ready to load into a spreadsheet, index into a vector store, or pass straight to an LLM as grounding context. No TikTok account, login, or session cookie is required to run it.

***

### What is TikTok Comments Scraper?

TikTok Comments Scraper is a query-driven Apify Actor that takes either a list of TikTok video URLs or a list of creator usernames and returns every comment TikTok will serve for those videos — including the full reply thread under each comment, not just the first page. No TikTok account, login, or session cookie is required; every endpoint it calls publicly is the same one a logged-out browser calls when you open a video.

What sets it apart from a plain top-level comment scraper: replies are paged on their own `cursor`/`has_more` sequence until your requested count is met, so a thread with hundreds of replies is retrieved past the 20-per-request limit TikTok's reply endpoint imposes on a single call — rather than being truncated at that first page the way a naive implementation would be.

Key capabilities:

- 💬 Scrape comments directly from a list of TikTok video URLs (`postURLs`)
- 🔍 Discover a creator's videos or reposts from a username, then scrape comments from each one (`profiles`)
- 🧵 Collect full nested reply threads per comment, paged past TikTok's 20-reply-per-page cap (`maxRepliesPerComment`)
- 📅 Filter which of a profile's videos get scraped by upload date (`oldestPostDateUnified`, `newestPostDate`)
- 📌 Exclude a profile's pinned videos from the scrape (`excludePinnedPosts`)
- 🔀 Sort a profile's videos by latest, most popular, or oldest before deciding which ones to scrape (`profileSorting`)

The run also escalates proxies automatically — starting with no proxy at all and stepping up to a datacenter proxy, then residential, only when TikTok actually blocks a request.

***

### What data can you get with TikTok Comments Scraper?

TikTok Comments Scraper returns two result types in every run — top-level comments and the reply threads nested inside them — plus an intermediate video-discovery step when you query by profile instead of by URL.

| Result Type | Extracted Fields | Primary Use Case |
| --- | --- | --- |
| Top-level comments | `text`, `diggCount`, `createTimeISO`, `uniqueId`, `uid`, `avatarThumbnail`, `mentions`, `isMediaOnlyComment`, `replyCommentTotal`, `likedByAuthor`, `pinnedByAuthor`, `replies` | Sentiment analysis, social listening, engagement ranking |
| Nested replies | Same row shape as comments, embedded inside the parent's `replies` array, with `repliesToId` set to the parent `cid` and `replyCommentTotal`/`pinnedByAuthor` forced to `null` because TikTok never sends those two keys on a reply object | Conversation-thread analysis, spotting creator/brand replies buried in a thread |
| Discovered videos (from `profiles`) | Resolved internally into a `videoWebUrl` for each comment row; the video itself is never pushed as its own dataset row | Turning a creator handle into a comment-ready URL list without a separate lookup step |

#### Full nested reply threads

This is the capability that separates TikTok Comments Scraper from a scraper that only grabs the first page of replies. TikTok's reply endpoint (`/api/comment/list/reply/`) caps a single request at 20 rows regardless of what you ask for — a naive integration that calls it once and stops will never return more than 20 replies per comment, no matter how deep the thread actually goes. TikTok Comments Scraper instead follows the endpoint's own `cursor` and `has_more` fields, issuing additional requests until either your `maxRepliesPerComment` target is met or TikTok reports no more pages, so a heavily-replied comment is retrieved in full rather than clipped at the first page:

```json
{
  "cid": "7578551234567890123",
  "text": "wait this is actually insane",
  "replyCommentTotal": 3,
  "replies": [
    { "cid": "7578551234567899001", "repliesToId": "7578551234567890123", "text": "fr fr" },
    { "cid": "7578551234567899002", "repliesToId": "7578551234567890123", "text": "no cap" }
  ]
}
```

Set `maxRepliesPerComment` to `0` to skip reply collection entirely — the reply endpoint is never called and the run finishes faster.

#### Profile video discovery

When you query by `profiles` instead of `postURLs`, the Actor first has to decide which of a creator's videos to scrape comments from. `resultsPerPage` sets how many videos to take per profile, `profileScrapeSections` chooses between a creator's own `videos` and their `reposts`, `profileSorting` orders the candidate videos by `latest`, `popular`, or `oldest`, and `oldestPostDateUnified`/`newestPostDate` narrow that set to a date window before `excludePinnedPosts` drops any pinned entries. Only the resulting video URLs are used — none of this discovery step's own metadata is pushed to the dataset.

***

### Why not build this yourself?

TikTok does not offer a self-serve official API for pulling comments off an arbitrary public video. Its developer platform exposes a Display API scoped to the videos of the authenticated creator's own account, and a separate Research API restricted to vetted academic institutions — neither lets a general developer query the comments of any public video by URL the way this Actor does.

Building that yourself means reverse-engineering unpublished internal endpoints (`/api/comment/list/`, `/api/comment/list/reply/`, `/api/post/item_list/`) that TikTok can reshape without notice, and correctly telling apart three outcomes that all arrive as an ordinary HTTP 200: a real (possibly empty) comment page, a WAF challenge page of a few hundred to a few thousand bytes, and a signed-endpoint refusal that comes back as a 0-byte body. Getting that classification wrong means a blocked run silently reports "0 comments, success" instead of failing loudly. It also means building and maintaining a proxy escalation strategy, because a fixed proxy tier either wastes money on traffic that didn't need it or gets blocked on traffic that did.

TikTok Comments Scraper handles all of that: response-size-based verdict classification (`ok`, `blocked`, `challenge`, `refusal`, `error`), a proxy ladder that starts with no proxy and only escalates to datacenter and then residential when TikTok actually blocks a request, and comment/reply pagination that follows TikTok's own cursors instead of assuming a fixed page stride.

***

### What is the difference between a comment scraper and a reply-thread scraper?

A comment scraper returns the list of top-level comments TikTok shows under a video; a reply-thread scraper additionally follows each comment's own reply thread past whatever single-page limit the reply endpoint imposes. The distinction matters because TikTok's most useful conversational context — a creator replying to a specific comment, a correction, a running joke, a customer-service response — usually lives inside the replies, not in the top-level list. A tool that stops at the first 20 replies per comment silently drops exactly the part of the thread that a brand-monitoring or moderation workflow is most likely to need.

TikTok Comments Scraper returns both in a single run: top-level comments as the primary pushed rows, each carrying a nested `replies` array built from the same row structure, paged past the reply endpoint's 20-per-request cap up to your `maxRepliesPerComment` value. There is no separate mode to switch on — every run that sets `maxRepliesPerComment` above `0` collects full threads by default.

***

### How to scrape TikTok comments with TikTok Comments Scraper?

1. Open **TikTok Comments Scraper** on the Apify Store and click **Try for free**, or find it under your existing Actors in the Apify Console.
2. Enter your query: paste one or more TikTok video URLs into `postURLs`, or switch to the **Comments from TikTok profiles** section and enter one or more usernames into `profiles`.
3. Set the real query controls you need — `commentsPerPost` and `maxRepliesPerComment` for volume, and for profile queries: `resultsPerPage`, `profileScrapeSections`, `profileSorting`, `oldestPostDateUnified`/`newestPostDate`, and `excludePinnedPosts`.
4. Click **Start** and let the run finish. Progress and any proxy-escalation or blocking events are logged live.
5. Open the **Output** tab and download the results as JSON, CSV, or Excel — or pull them programmatically with the Apify API or `apify-client`.

```json
{
  "postURLs": ["https://www.tiktok.com/@mrbeast/video/7578547467189374239"],
  "commentsPerPost": 50,
  "maxRepliesPerComment": 10
}
```

#### How to run multiple queries in one job

Add every URL you want to `postURLs`, or every username you want to `profiles` — both fields accept an array, and the Input tab's **Bulk edit** mode lets you paste a prepared list instead of adding items one at a time. A single run processes every URL you provide; internally, videos are worked on concurrently rather than one at a time, so a batch of URLs does not run strictly sequentially.

***

### ⬇️ Input

TikTok Comments Scraper takes either a list of video URLs or a list of usernames — never both at once, since `postURLs` wins whenever it is non-empty and `profiles` is ignored in that case. Everything else is optional and has a schema default.

| Parameter | Type | Required | Default | Constraints | Description |
| --- | --- | --- | --- | --- | --- |
| `postURLs` | array of strings | No | — | `stringList` editor | TikTok video URLs to extract comments from. Add them one by one or paste a list in Bulk edit. Tracking parameters such as `?is_from_webapp=1` are stripped automatically. |
| `commentsPerPost` | integer | No | `10` | `minimum: 1` | Top-level comments to collect per video. TikTok serves roughly 47–50 comments per API page, so the final count can land slightly under a request that is not a multiple of the page size. |
| `maxRepliesPerComment` | integer | No | `3` | `minimum: 0` | Replies to collect from each comment that has any. The reply endpoint is paged 20 at a time until this number is met, so values above 20 are honoured. Set to `0` to skip replies entirely and run faster. |
| `profiles` | array of strings | No | — | `stringList` editor. Ignored when `postURLs` is filled in. | One or more TikTok usernames, with or without the leading `@`. Use Bulk edit to paste a prepared list. |
| `resultsPerPage` | integer | No | `10` | `minimum: 1`, `maximum: 1000000` | Videos to take from each profile. Values above 1,000 are coerced down to 1,000 at runtime — the schema maximum stays at 1,000,000 so this coercion never rejects an input outright, but the effective ceiling is 1,000 and the coercion is logged. |
| `profileScrapeSections` | array of strings | No | `["videos"]` | `minItems: 1`, `uniqueItems: true`, enum: `videos`, `reposts` | Which of a profile's sections to scrape. `videos` uses a plain HTTP request. `reposts` has no HTTP equivalent and starts a headless browser, so it is noticeably slower. |
| `profileSorting` | string | No | `"latest"` | enum: `latest`, `popular`, `oldest` | `latest` reads the profile feed in order. `popular` and `oldest` collect a wider window of recent videos — up to 5× your requested count, capped at 200 — and sort within that window, so they approximate rather than search a creator's whole history. |
| `oldestPostDateUnified` | string | No | — | `datepicker` editor, absolute (`YYYY-MM-DD`) or relative (`5 days`, `2 weeks`) | Only videos uploaded on or after this date are used. Filters videos, not comments. |
| `newestPostDate` | string | No | — | Same format as above | Only videos uploaded on or before this date are used. Filters videos, not comments. |
| `excludePinnedPosts` | boolean | No | `false` | — | Skip a profile's pinned videos, which are usually the first ones shown on the profile page. Applies to the `videos` section. On the `reposts` section the pinned check currently has nothing to check against — a repost item never carries pinned status — so a pinned repost is not filtered out even with this set to `true`. |
| `proxyConfiguration` | object | No | `{"useApifyProxy": false}` | `proxy` editor | Proxy settings. The run starts with no proxy, which is normally enough. If TikTok blocks a request it escalates to a datacenter proxy and then to residential with 3 retries. |

No field in this schema is a credential or secret — the Actor does not accept a TikTok login, API key, or cookie as input at all.

#### Example JSON input

```json
{
  "postURLs": [
    "https://www.tiktok.com/@mrbeast/video/7578547467189374239"
  ],
  "commentsPerPost": 50,
  "maxRepliesPerComment": 10,
  "profiles": [],
  "resultsPerPage": 10,
  "profileScrapeSections": ["videos"],
  "profileSorting": "latest",
  "excludePinnedPosts": false,
  "proxyConfiguration": { "useApifyProxy": false }
}
```

**Common pitfall:** filling in both `postURLs` and `profiles` in the same run. `postURLs` always wins — `profiles` is silently ignored, not merged. If you want both a fixed URL list and profile-based discovery in one job, run them as two separate Actor runs.

***

### ⬆️ Output

Every comment and every reply is pushed as a typed, normalized JSON row with a consistent schema across runs. Export it from the **Output** tab as JSON, CSV, or Excel, or pull it with the Apify API. The full array from a run is also written to the key-value store as `output.json` for a single-file download alongside the dataset.

Only top-level comments are pushed as their own dataset rows; their replies live nested inside that row's `replies` array rather than as separate top-level rows.

| Field | Type | Description |
| --- | --- | --- |
| `videoWebUrl` | string | Canonical URL of the video the comment belongs to, with tracking parameters and any `vm`/`vt` short-link redirect already resolved. |
| `submittedVideoUrl` | string | The same resolved canonical URL. |
| `input` | string | The URL exactly as it was processed for this row — useful for tracing a row back to a specific `postURLs` entry. |
| `cid` | string | TikTok's comment ID. Unique per row. |
| `createTime` | integer | Unix timestamp, in seconds, of when the comment was posted. |
| `createTimeISO` | string | The same timestamp as an ISO 8601 UTC string. |
| `text` | string | The comment text. Empty when the comment is an image or sticker with no text — see `isMediaOnlyComment`. |
| `diggCount` | integer | Number of likes on the comment. |
| `likedByAuthor` | boolean or `null` | Whether the video's author liked this comment. `null` if TikTok did not send the underlying `is_author_digged` field on this row. |
| `pinnedByAuthor` | boolean or `null` | Whether the author pinned this comment. TikTok only ever sends this field on top-level comments — it is always `null` on a reply row. |
| `repliesToId` | string or `null` | The parent comment's `cid`. `null` on a top-level comment, populated on every reply. |
| `replyCommentTotal` | integer or `null` | How many replies TikTok reports for this comment. TikTok only sends this on top-level comments — it is always `null` on a reply row. |
| `uid` | string | Commenter's numeric TikTok user ID. |
| `uniqueId` | string | Commenter's `@username`. |
| `avatarThumbnail` | string | URL of the commenter's avatar image. |
| `mentions` | array of strings | `@handles` parsed out of the comment text. |
| `isMediaOnlyComment` | boolean | `true` when the comment has no text because it consists of an image or sticker instead. |
| `replies` | array of objects | Nested reply rows, each built from this same field set (minus `replies`, since TikTok's reply threads are one level deep). Present on top-level comment rows only — a reply object never has its own `replies` key. |

The default dataset view in the Apify Console shows a 10-column subset of this — `text`, `isMediaOnlyComment`, `diggCount`, `replyCommentTotal`, `createTimeISO`, `uniqueId`, `videoWebUrl`, `uid`, `cid`, and `avatarThumbnail` — for readability. The full row, including `submittedVideoUrl`, `input`, `createTime`, `likedByAuthor`, `pinnedByAuthor`, `repliesToId`, `mentions`, and `replies`, is present in every export and in the API response; switch to **All fields** in the Output tab, or query the dataset directly, to see it.

#### Scraped results

```json
[
  {
    "videoWebUrl": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
    "submittedVideoUrl": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
    "input": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
    "cid": "7578551234567890123",
    "createTime": 1750000000,
    "createTimeISO": "2025-06-15T09:26:40.000Z",
    "text": "if @chandlerhallow doesn't get the car I'm rioting",
    "diggCount": 4213,
    "likedByAuthor": true,
    "pinnedByAuthor": false,
    "repliesToId": null,
    "replyCommentTotal": 2,
    "uid": "6812345678901234567",
    "uniqueId": "viewer_jay",
    "avatarThumbnail": "https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0068/1a2b3c4d~c5_100x100.jpeg",
    "mentions": ["chandlerhallow"],
    "isMediaOnlyComment": false,
    "replies": [
      {
        "videoWebUrl": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
        "submittedVideoUrl": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
        "input": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
        "cid": "7578551234567899001",
        "createTime": 1750000450,
        "createTimeISO": "2025-06-15T09:34:10.000Z",
        "text": "right?? he never gets it",
        "diggCount": 88,
        "likedByAuthor": null,
        "pinnedByAuthor": null,
        "repliesToId": "7578551234567890123",
        "replyCommentTotal": null,
        "uid": "6890001112223334445",
        "uniqueId": "commenter_two",
        "avatarThumbnail": "https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0068/9f8e7d6c~c5_100x100.jpeg",
        "mentions": [],
        "isMediaOnlyComment": false
      },
      {
        "videoWebUrl": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
        "submittedVideoUrl": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
        "input": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
        "cid": "7578551234567899002",
        "createTime": 1750000600,
        "createTimeISO": "2025-06-15T09:36:40.000Z",
        "text": "lmaooo facts",
        "diggCount": 12,
        "likedByAuthor": null,
        "pinnedByAuthor": null,
        "repliesToId": "7578551234567890123",
        "replyCommentTotal": null,
        "uid": "6890002223334445556",
        "uniqueId": "commenter_three",
        "avatarThumbnail": "https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0068/2b3c4d5e~c5_100x100.jpeg",
        "mentions": [],
        "isMediaOnlyComment": false
      }
    ]
  },
  {
    "videoWebUrl": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
    "submittedVideoUrl": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
    "input": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
    "cid": "7578551234567890555",
    "createTime": 1750001200,
    "createTimeISO": "2025-06-15T09:46:40.000Z",
    "text": "",
    "diggCount": 340,
    "likedByAuthor": false,
    "pinnedByAuthor": false,
    "repliesToId": null,
    "replyCommentTotal": 0,
    "uid": "6899998887776665554",
    "uniqueId": "sticker_fan",
    "avatarThumbnail": "https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0068/5d6e7f8a~c5_100x100.jpeg",
    "mentions": [],
    "isMediaOnlyComment": true,
    "replies": []
  },
  {
    "videoWebUrl": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
    "submittedVideoUrl": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
    "input": "https://www.tiktok.com/@mrbeast/video/7578547467189374239",
    "cid": "7578551234567891777",
    "createTime": 1750002000,
    "createTimeISO": "2025-06-15T10:00:00.000Z",
    "text": "day 47 of asking for a part 2",
    "diggCount": 1027,
    "likedByAuthor": false,
    "pinnedByAuthor": true,
    "repliesToId": null,
    "replyCommentTotal": 0,
    "uid": "6877776665554443332",
    "uniqueId": "loyal_watcher",
    "avatarThumbnail": "https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0068/7f8a9b0c~c5_100x100.jpeg",
    "mentions": [],
    "isMediaOnlyComment": false,
    "replies": []
  }
]
```

***

### How can I use the data extracted with TikTok Comments Scraper?

- **📢 Social listening and brand monitoring teams:** pull every comment on videos that mention your brand or product, filter by `diggCount` to surface the most-liked reactions, and route the rest into a sentiment pipeline using the raw `text` field.
- **🤖 AI engineers and LLM developers:** have an agent call the Actor with a video URL, receive structured JSON back, and pass the comment and reply text straight into a model as grounding context for a summarization or moderation task — no HTML parsing required.
- **📊 Market researchers:** compare `diggCount` and `replyCommentTotal` distributions across competitors' videos over the same period to gauge relative share of engagement, and use `createTimeISO` to see how a conversation evolved after a launch.
- **🧭 Product teams:** mine `text` and nested `replies` for repeated feature requests or complaints — replies are frequently where a viewer clarifies or argues a point the top-level comment only hints at.

***

### 📈 How do you monitor comment activity over time?

Comment activity on a video is not static — `diggCount` climbs, new top-level comments appear, and `replyCommentTotal` grows as a thread keeps attracting responses days or weeks after the video was posted. Monitoring that activity means running the same `postURLs` (or the same `profiles` query) on a schedule and comparing each run's output against the previous one.

The fields worth diffing between runs are `cid` (to detect genuinely new comments, since it is unique per comment), `diggCount` (to track which comments are gaining traction), and `replyCommentTotal` (to catch a thread that has grown since the last run — which is also your signal to raise `maxRepliesPerComment` on the next pass if you want the newly-added replies too). A comment whose `cid` you have not seen before is new; a comment whose `diggCount` jumped sharply between two runs is the one worth surfacing to a human.

A practical setup: schedule a run against a fixed list of `postURLs` once a day using an [Apify Schedule](https://docs.apify.com/platform/schedules), export each day's dataset, and diff it against the previous day's export on `cid` and `diggCount`. Alert when a new top-level comment crosses a like threshold, or when `replyCommentTotal` on a tracked comment jumps — that is usually the moment a reply thread turned into something worth reading, not just counting.

***

### Integrate TikTok Comments Scraper and automate your workflow

TikTok Comments Scraper works with any language or tool that can send an HTTP request, through the standard Apify API.

#### REST API with Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")

run_input = {
    "postURLs": ["https://www.tiktok.com/@mrbeast/video/7578547467189374239"],
    "commentsPerPost": 50,
    "maxRepliesPerComment": 10,
}

run = client.actor("<YOUR_USERNAME>/tiktok-comments-scraper").call(run_input=run_input)

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["uniqueId"], "->", item["text"])
```

Query in, structured comment (and nested reply) JSON out — the Apify API token is the only credential this call needs; no TikTok credential is ever passed.

#### Scheduled monitoring and delivery

TikTok Comments Scraper has no built-in webhook or scheduling logic of its own — it uses the Apify platform's. Attach an [Apify Schedule](https://docs.apify.com/platform/schedules) to run it automatically on a cron interval, and configure a [webhook](https://docs.apify.com/platform/integrations/webhooks) on the run to notify your own endpoint or trigger a downstream job the moment a run finishes.

***

### Is it legal to scrape TikTok comments?

Scraping publicly visible TikTok comments is generally permissible: the comments returned by this Actor are the same ones any logged-out visitor can already see on the video page, and no login-gated or private content is accessed. Courts have distinguished between accessing public web content and content shielded by a login wall — see *hiQ Labs, Inc. v. LinkedIn Corp.*, 938 F.3d 985 (9th Cir. 2019), which held that scraping publicly accessible data does not, on its own, violate the U.S. Computer Fraud and Abuse Act.

Comment text, usernames, user IDs, and avatar URLs are personal data under GDPR (EU) and CCPA (California) once they are linked to an identifiable individual, even though TikTok displays them publicly. Having a legitimate basis for collecting and storing that data — and honoring deletion or access requests where applicable — remains your responsibility as the data controller. Scraping for one-off research carries a different risk profile than scraping to train a model or build a permanent database of individuals' comment history. Consult your legal team for commercial use cases involving bulk data storage.

***

### ❓ Frequently asked questions

#### Do I need a TikTok account, login, or cookies to use TikTok Comments Scraper?

No. Every endpoint this Actor calls is publicly reachable without authentication, and the input schema has no login, API key, or cookie field. An optional session cookie can be supplied at the deployment level purely to help the `reposts` browser path reach a profile's Reposts tab when it is otherwise hard to load — it is not something you configure through the Actor's input.

#### How does TikTok Comments Scraper handle TikTok's anti-bot measures?

It reads response size, not just HTTP status, because TikTok returns HTTP 200 for both a WAF challenge page and a signed-endpoint refusal (a 0-byte body) — treating either as "0 comments, success" would be wrong. On a block, it escalates from no proxy to a datacenter proxy, then to a residential proxy with retries, and sticks with whichever tier last succeeded for the rest of that request.

#### Does TikTok Comments Scraper extract full nested reply threads?

Yes. Each top-level comment row carries a `replies` array built by paging the reply endpoint on its own cursor until your `maxRepliesPerComment` value is met or TikTok reports no more pages — not just the first 20 replies a single request returns. Set `maxRepliesPerComment` to `0` to disable reply collection for that run.

#### How many comments and replies does TikTok Comments Scraper return per query?

As many as `commentsPerPost` and `maxRepliesPerComment` request, up to what the video actually has and what TikTok's endpoints will serve. There is no schema-level maximum on either field — only a `minimum` (`1` for comments, `0` for replies) — but TikTok's comment endpoint caps a single page at roughly 47–50 rows regardless of what is requested, so an odd `commentsPerPost` value can land slightly under target at the last page boundary.

#### Can I scrape comments from a creator's profile instead of individual video URLs?

Yes, using `profiles`. Set `resultsPerPage` for how many videos to pull per profile (up to a runtime ceiling of 1,000, even though the schema allows requesting as high as 1,000,000), `profileScrapeSections` to choose between a creator's `videos` and `reposts`, and `profileSorting` to order the candidate set by `latest`, `popular`, or `oldest` before the top `resultsPerPage` are kept.

#### What happens if I fill in both `postURLs` and `profiles`?

`postURLs` wins. `profiles` is silently ignored for that run rather than merged with it — if you need both a fixed URL list and a profile-based discovery run, submit them as two separate runs.

#### How do I use TikTok Comments Scraper to monitor comment activity over time?

Schedule the same `postURLs` (or `profiles` query) to run on a recurring interval using an Apify Schedule, then diff each run's export against the previous one on `cid` (to catch new comments) and on `diggCount`/`replyCommentTotal` (to catch comments and threads that are still gaining traction).

#### Does TikTok Comments Scraper work with Claude, ChatGPT, and AI agent frameworks?

It has no dedicated MCP server. It is callable as a standard HTTP endpoint through the Apify API by any agent framework that can make a REST call — an agent issues a run with a video URL or username, receives structured comment JSON back, and can pass it directly to a model as grounding context.

#### How does TikTok Comments Scraper compare to other TikTok comment scrapers?

Compared to `clockworks/tiktok-comments-scraper`, whose listing documents the same core comment fields but does not describe reply-thread depth beyond "number of replies and replies content" (checked on the Apify Store, 2026-07-25), TikTok Comments Scraper explicitly pages replies past the 20-per-request limit and flags image/sticker-only comments with `isMediaOnlyComment` rather than leaving them as blank text rows. Compared to `epctex/tiktok-comment-scraper`, whose listing prices a separate "Reply Query" charge per comment thread with only the first roughly 15 replies free (checked on the Apify Store, 2026-07-25), TikTok Comments Scraper charges a single `row_result` event per top-level comment regardless of how many replies are nested inside it. `apidojo/tiktok-comments-scraper`'s listing advertises up to 10,000 comments per minute and a 99% success rate (checked on the Apify Store, 2026-07-25) — figures from that listing, not measured here.

#### Can I use TikTok Comments Scraper without managing proxies or TikTok credentials?

Yes. No TikTok credential is ever required, and proxy handling is automatic: the run starts with no proxy and only escalates to a datacenter, then residential, proxy when TikTok actually blocks a request. You can still supply your own `proxyConfiguration` if you want to control the proxy group used at the datacenter stage.

***

### 💬 Your feedback

Found a bug, or a field missing something you need? Open an issue from the Actor's **Issues** tab in Apify Console — that's the fastest way to reach the maintainer, and it keeps the report attached to the exact Actor version you ran.

# Actor input Schema

## `postURLs` (type: `array`):

The video URLs to extract comments from. Add them one by one or paste a list in Bulk edit. Tracking parameters such as ?is\_from\_webapp=1 are fine and are stripped automatically.

## `commentsPerPost` (type: `integer`):

How many top-level comments to collect per video. TikTok serves roughly 47-50 comments per API page, so the final count can land slightly under a request that is not a multiple of the page size.

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

How many replies to collect from each comment that has any. The reply endpoint is paged 20 at a time until this number is met, so values above 20 are honoured. Set to 0 to skip replies entirely and run faster.

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

One or more TikTok usernames, with or without the leading @. Use Bulk edit to paste a prepared list.

## `resultsPerPage` (type: `integer`):

How many videos to take from each profile. Values above 1000 are coerced down to 1000 at runtime and the coercion is logged.

## `profileScrapeSections` (type: `array`):

Videos uses a plain HTTP request. Reposts has no HTTP equivalent and starts a headless browser, so it is noticeably slower.

## `profileSorting` (type: `string`):

Latest reads the profile feed in order. Popular and Oldest collect a wider window of recent videos (up to 5x your requested count, capped at 200) and sort within that window, so they approximate rather than search a creator's whole history.

## `oldestPostDateUnified` (type: `string`):

Optional. Only videos uploaded on or after this date are used. Absolute (YYYY-MM-DD) or relative (for example: 5 days, 2 weeks). Filters videos, not comments.

## `newestPostDate` (type: `string`):

Optional. Only videos uploaded on or before this date are used. Absolute (YYYY-MM-DD) or relative (for example: 5 days, 2 weeks). Filters videos, not comments.

## `excludePinnedPosts` (type: `boolean`):

Skip a profile's pinned videos, which are usually the first ones shown on the profile page.

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

The run starts with no proxy, which is normally enough. If TikTok blocks a request it escalates to a datacenter proxy and then to residential with 3 retries.

## Actor input object example

```json
{
  "postURLs": [
    "https://www.tiktok.com/@mrbeast/video/7578547467189374239"
  ],
  "commentsPerPost": 10,
  "maxRepliesPerComment": 3,
  "profiles": [
    "mrbeast"
  ],
  "resultsPerPage": 10,
  "profileScrapeSections": [
    "videos"
  ],
  "profileSorting": "latest",
  "excludePinnedPosts": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# 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/@mrbeast/video/7578547467189374239"
    ],
    "profiles": [
        "mrbeast"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapier/tiktok-comments-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 = {
    "postURLs": ["https://www.tiktok.com/@mrbeast/video/7578547467189374239"],
    "profiles": ["mrbeast"],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapier/tiktok-comments-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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/@mrbeast/video/7578547467189374239"
  ],
  "profiles": [
    "mrbeast"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call scrapier/tiktok-comments-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=scrapier/tiktok-comments-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/VlysSRXiQVArrblZT/builds/X88OVMn7YoM2xO6Ie/openapi.json
