# TikTok Comments Scraper (`alwaysprimedev/tiktok-comments-scraper`) Actor

Collect every public top-level comment from one or many TikTok videos as structured JSON, CSV, or Excel. Pay only for comments delivered.

- **URL**: https://apify.com/alwaysprimedev/tiktok-comments-scraper.md
- **Developed by:** [Always Prime](https://apify.com/alwaysprimedev) (community)
- **Categories:** E-commerce, Lead generation, Social media
- **Stats:** 2 total users, 1 monthly users, 33.3% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 comments

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

> Pull every public comment from any TikTok video into clean, structured data — JSON, CSV, or Excel — in seconds. ⚡

[![Built on Apify](https://img.shields.io/badge/Built%20on-Apify-blue?style=for-the-badge&logo=apify&logoColor=white)](https://apify.com)
[![Python](https://img.shields.io/badge/Python-3.11+-blue?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org)
[![JSON · CSV · Excel](https://img.shields.io/badge/Output-JSON%20%C2%B7%20CSV%20%C2%B7%20Excel-success?style=for-the-badge)](https://apify.com)

### ✨ What you get

🎯 **Every public top-level comment** — handle, nickname, comment text, likes, replies, timestamps, language, pinned status, author profile URL.
🚀 **Multiple videos in one run** — feed it a list of URLs, get one consolidated dataset back.
🧹 **Clean, flat records** — ready to drop into a sheet, a database, or a notebook. No HTML, no nested noise.
🪙 **Pay-per-result pricing** — you only pay for the comments you actually receive. Predictable, capped via `maxItems`.
🔁 **Incremental refresh mode** — re-run with `since` to pick up only the new stuff. No re-scraping the whole comment list.
📦 **Three export formats** — JSON, CSV, and Excel, all generated automatically.

### 🚀 Quick start

1. **Click "Try for free"** on the Apify Store page.
2. **Paste your TikTok video URLs** into the *Video URLs* field — one per line.
3. **Press *Start*.** Watch the live log; comments stream into the dataset as they're collected.
4. **Download** the dataset in JSON, CSV, or Excel from the run page.

That's it. No accounts, no API keys, no signing up to anything else.

### 📥 Input

| Field | Type | Default | Description |
|---|---|---|---|
| `videoUrls` | string[] | — *required* | List of TikTok video URLs or numeric video IDs. Accepts `https://www.tiktok.com/@user/video/<id>`, plain `<id>`, or any URL containing `/video/<id>`. |
| `maxCommentsPerVideo` | integer | `100` | Stop scraping each video after this many top-level comments. Set `0` for no per-video cap. |
| `maxItems` | integer | `0` | Hard cap on total comments across all videos. `0` disables the global cap. Use to control pay-per-result spend. |
| `since` | datetime | — | ISO timestamp like `2026-04-01T00:00:00Z`. Skips comments older than this. Use for incremental re-runs. |
| `concurrency` | integer | `3` | How many videos to scrape in parallel. Range 1–8. Lower if hit by rate limits, higher on premium proxies. |

### 📤 Sample output

```json
{
  "url": "https://www.tiktok.com/@nike/video/7631536776661765389",
  "id": "7631763288914133776",
  "videoId": "7631536776661765389",
  "videoUrl": "https://www.tiktok.com/@nike/video/7631536776661765389",
  "text": "Supporting that ballroom wow I love Brooks best running shoes ever!",
  "createdAt": "2026-04-21T18:39:42Z",
  "likeCount": 45,
  "replyCount": 2,
  "isPinned": false,
  "isAuthorPinned": false,
  "isLikedByAuthor": true,
  "language": "en",
  "parentCommentId": null,
  "images": [],
  "user": {
    "id": "7324198047750980613",
    "uniqueId": "jennicwo46s",
    "nickname": "JenniC",
    "secUid": "MS4wLjABAAAAO2o-FYv8q-XwUL0hQRYrbwjCm04E3mg5Tq8VQJ1SNkxJdgWefQ1dJRLg6oMH-v5D",
    "avatarUrl": "https://p16-common-sign.tiktokcdn.com/...avatar.jpg",
    "verifiedReason": null,
    "profileUrl": "https://www.tiktok.com/@jennicwo46s"
  },
  "scrapedAt": "2026-05-10T12:34:56Z"
}
````

### 💼 Use cases

| Audience | What they do with it |
|---|---|
| **🛍️ Brand & social teams** | Track sentiment on competitor posts. Catch complaints early. Find what audiences love (or hate) about your campaigns. |
| **🤝 Influencer agencies** | Vet creators by reading actual comment quality — not just like counts. Spot bot-engagement patterns. |
| **📈 Market research** | Mine emerging trends, slang, product mentions, and audience demographics from comment streams. |
| **🎯 Performance marketers** | Find high-intent buyers who actively engage with niche-relevant content; build look-alike audiences. |
| **🎓 Academic researchers** | Discourse analysis, language datasets, network science on social engagement patterns. |
| **🤖 ML/NLP teams** | Build labeled training data for sentiment, topic, hate-speech, or recommendation models. |

### 💡 Tips & tricks

- **Start with `maxItems: 100`** to see what the data looks like before doing a big run. The first record alone tells you whether the fields you need are there.
- **Use `since` for daily refreshes.** Set it to the timestamp of your last run; the scraper skips everything older. Cuts re-scraping costs to zero on stable videos.
- **For very viral videos** (10k+ comments), consider running them one-at-a-time with `concurrency: 1` — you'll get cleaner runs and avoid rate-limit retries.
- **Batch your URL list.** A single run with 100 URLs is more cost-efficient than 100 runs with 1 URL each.

### ❓ FAQ

**Q: Are reply threads included?**
A: This run captures top-level comments only. Each record carries `replyCount` so you know how many replies exist. Reply-thread collection is on the roadmap (see Changelog).

**Q: What about comments from private or restricted accounts?**
A: The scraper only sees what's publicly accessible on the TikTok web frontend. Private accounts, age-restricted videos, and geo-restricted videos are out of scope.

**Q: Can I scrape comments without a TikTok login?**
A: Yes. This actor doesn't require any TikTok login or token from you.

**Q: What if a video has 50,000 comments?**
A: Use `maxCommentsPerVideo` to cap it. Scraping the full tail of a viral video takes time and money — sample what you need.

**Q: Why is the avatar URL not loading after a day?**
A: TikTok signs CDN URLs with an `x-expires` query param. Avatars typically stop loading ~24h after the run. Re-scrape, or save your own copy at run time if you need them long-term.

**Q: Can I resume a failed run?**
A: Apify retains the dataset across run attempts. Re-run with `since` set to the last `scrapedAt` you saw, and de-dupe on `id` in your downstream pipeline.

### ⚖️ Legal

This actor scrapes only **publicly accessible** content from TikTok video pages — the same data that any unauthenticated visitor can read. You are responsible for using the resulting data in compliance with TikTok's Terms of Service, the GDPR, the CCPA, and any other laws or platform rules that apply to you. Comments are user-generated content and may contain personal data; treat them as PII when storing or processing. If you publish or republish scraped data, attribute it to TikTok and to the original commenters per the platform's content rules.

### 📜 Changelog

#### v0.1 — initial release

- Top-level comment extraction across one or many TikTok videos
- Per-video and global item caps
- Incremental mode via `since` filter
- Per-comment record with author profile, engagement, language, timestamps
- JSON, CSV, and Excel exports
- Built-in deduplication by comment id

# Actor input Schema

## `videoUrls` (type: `array`):

List of TikTok video URLs or numeric video IDs to collect comments from. Accepts the canonical https://www.tiktok.com/@user/video/<id> form, the bare video ID, or any URL containing /video/<id>.

## `maxCommentsPerVideo` (type: `integer`):

Stop scraping a single video after this many top-level comments are saved. Set to 0 for no per-video cap (collect every comment up to TikTok's pagination limits).

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

Hard cap on the total number of comments pushed to the dataset across all input videos. Set to 0 to disable the global cap. Useful for sampling runs and for keeping pay-per-result costs predictable.

## `since` (type: `string`):

Optional ISO 8601 timestamp (e.g. 2026-04-01T00:00:00Z). When set, comments older than this are skipped. Use to refresh a dataset incrementally without re-collecting everything.

## `concurrency` (type: `integer`):

How many videos to scrape in parallel. Each concurrent video runs an isolated browser context (~50 MB). Lower it if your runs hit memory limits or get rate-limited; raise it on premium proxies.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.tiktok.com/@nike/video/7629804861051161869"
  ],
  "maxCommentsPerVideo": 100,
  "maxItems": 0,
  "since": "2026-04-01T00:00:00Z",
  "concurrency": 3
}
```

# Actor output Schema

## `comments` (type: `string`):

No description

## `commentsCsv` (type: `string`):

No description

## `commentsXlsx` (type: `string`):

No description

## `consoleView` (type: `string`):

No description

# 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 = {
    "videoUrls": [
        "https://www.tiktok.com/@nike/video/7629804861051161869"
    ],
    "maxCommentsPerVideo": 100,
    "maxItems": 0,
    "concurrency": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("alwaysprimedev/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 = {
    "videoUrls": ["https://www.tiktok.com/@nike/video/7629804861051161869"],
    "maxCommentsPerVideo": 100,
    "maxItems": 0,
    "concurrency": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("alwaysprimedev/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 '{
  "videoUrls": [
    "https://www.tiktok.com/@nike/video/7629804861051161869"
  ],
  "maxCommentsPerVideo": 100,
  "maxItems": 0,
  "concurrency": 3
}' |
apify call alwaysprimedev/tiktok-comments-scraper --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "TikTok Comments Scraper",
        "description": "Collect every public top-level comment from one or many TikTok videos as structured JSON, CSV, or Excel. Pay only for comments delivered.",
        "version": "0.1",
        "x-build-id": "bncuRc4Q7rb6DVXYa"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/alwaysprimedev~tiktok-comments-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-alwaysprimedev-tiktok-comments-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/alwaysprimedev~tiktok-comments-scraper/runs": {
            "post": {
                "operationId": "runs-sync-alwaysprimedev-tiktok-comments-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/alwaysprimedev~tiktok-comments-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-alwaysprimedev-tiktok-comments-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "videoUrls"
                ],
                "properties": {
                    "videoUrls": {
                        "title": "Video URLs",
                        "type": "array",
                        "description": "List of TikTok video URLs or numeric video IDs to collect comments from. Accepts the canonical https://www.tiktok.com/@user/video/<id> form, the bare video ID, or any URL containing /video/<id>.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxCommentsPerVideo": {
                        "title": "Max comments per video",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Stop scraping a single video after this many top-level comments are saved. Set to 0 for no per-video cap (collect every comment up to TikTok's pagination limits).",
                        "default": 100
                    },
                    "maxItems": {
                        "title": "Max items (across all videos)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Hard cap on the total number of comments pushed to the dataset across all input videos. Set to 0 to disable the global cap. Useful for sampling runs and for keeping pay-per-result costs predictable.",
                        "default": 0
                    },
                    "since": {
                        "title": "Only comments newer than (incremental mode)",
                        "type": "string",
                        "description": "Optional ISO 8601 timestamp (e.g. 2026-04-01T00:00:00Z). When set, comments older than this are skipped. Use to refresh a dataset incrementally without re-collecting everything."
                    },
                    "concurrency": {
                        "title": "Concurrent videos",
                        "minimum": 1,
                        "maximum": 8,
                        "type": "integer",
                        "description": "How many videos to scrape in parallel. Each concurrent video runs an isolated browser context (~50 MB). Lower it if your runs hit memory limits or get rate-limited; raise it on premium proxies.",
                        "default": 3
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
