# TikTok Scraper: Profiles, Videos, Hashtags, Search & Comments (`memo23/tiktok-scraper`) Actor

One actor for TikTok instead of seven. Pick a mode and pass a list: accounts and their posts, a single post in full detail, a hashtag's video feed, keyword search, a video's comments with replies, or account discovery by keyword. Video rows share one shape across every mode. No login, no cookies.

- **URL**: https://apify.com/memo23/tiktok-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Social media, AI, Agents
- **Stats:** 21 total users, 18 monthly users, 98.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 result rows

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

## TikTok Scraper: Profiles, Videos, Hashtags, Search & Comments

<p align="center"><img src="https://api.apify.com/v2/key-value-stores/hLquVQ6atnAyyJaLg/records/logo.png" alt="TikTok Scraper" width="140"></p>

One actor for TikTok instead of seven. Pick a mode, pass a list, and the rows land in a single dataset you can sort.

Accounts and their posts, one post in full detail, a hashtag's video feed, keyword search, a video's comments with replies, or account discovery by keyword.

### Why Use This Scraper

- **Seven jobs, one actor** and one input shape to learn
- **A video row is the same row** whether it came from a profile, a hashtag or a search
- **Exact limits.** Ask for 37 and you get 37, not the 50 a page happens to hold
- **Duplicates removed** by id, which matters because TikTok's cursors overlap
- **A backup source** takes over when the primary runs dry mid-keyword
- **Partial beats dead.** A run near its timeout returns what it has instead of failing
- **No login, no cookies, no proxy configuration**

### Overview

This is for people who already work with TikTok data and are tired of wiring up a different actor, a different input shape and a different output schema for every question. Researchers, agencies, creator-marketing teams and anyone feeding a dashboard.

Every row carries `rowType` and `source`, so a mixed run stays parseable and you can branch without guessing where a row came from.

Output is a flat dataset you can export as JSON, CSV or Excel.

### Supported Inputs

The `input` list means whatever the mode says it means.

| Mode | What to pass | Example |
|---|---|---|
| `profile` | usernames or profile URLs | `@gymshark` |
| `videos` | usernames or profile URLs | `https://www.tiktok.com/@khaby.lame` |
| `post` | post URLs or bare IDs | `7660964717631999254` |
| `hashtag` | hashtags or tag URLs | `#coffee` |
| `search` | plain keywords | `latte art` |
| `comments` | video URLs or bare IDs | `https://www.tiktok.com/@gymshark/video/7660964717631999254` |
| `users` | plain keywords | `fitness coach` |

Short `vm.tiktok.com` and `vt.tiktok.com` share links are resolved for you in `post` and `comments` modes.

#### Unsupported inputs

- **Live streams.** Not covered by any mode.
- **Private accounts.** A public card is returned where one exists, but private posts are not public data and are reported as such.
- **Anything behind a login.** This actor never authenticates.

### Use Cases

| Audience | What they use it for |
|---|---|
| Creator marketing | Find accounts by keyword, then pull their posts and engagement in the same run |
| Brand monitoring | Watch a hashtag feed and read what commenters actually say |
| Agencies | One input shape across every client request instead of seven actors to explain |
| Researchers | Keyword search with exact limits, so a sample size is a sample size |
| Dashboards | One row shape means one schema to maintain downstream |

### How It Works

<p align="center"><img src="https://api.apify.com/v2/key-value-stores/hLquVQ6atnAyyJaLg/records/how-it-works.png" alt="How the TikTok Scraper works" width="900"></p>

1. You pick a mode and pass a list of things to scrape.
2. The actor reads each entry, resolving share links and profile URLs as needed.
3. Results are collected page by page, deduplicated by id, and stopped at exactly the count you asked for.
4. If the primary source runs dry mid-keyword, a second source continues rather than reporting a thin result as the whole of TikTok.
5. Rows are written with `rowType` and `source` attached, so a mixed run stays sortable.

### Input Configuration

| Field | Type | Default | Applies to |
|---|---|---|---|
| `mode` | string | `profile` | required |
| `input` | array | required | all modes |
| `maxResults` | integer | `100` | hashtag, search, users, comments |
| `includeVideos` | boolean | `false` | profile |
| `maxVideosPerProfile` | integer | `100` | profile, videos |
| `strictHashtagMatch` | boolean | `false` | hashtag |
| `minFollowers` | integer | `0` | users |
| `maxFollowers` | integer | none | users |
| `verifiedOnly` | boolean | `false` | users |
| `includeReplies` | boolean | `false` | comments |
| `maxRepliesPerComment` | integer | `50` | comments |
| `includeMediaUrls` | boolean | `true` | post |
| `includeAuthorStats` | boolean | `true` | post |
| `includeHashtagsMentions` | boolean | `true` | post |

#### Example: an account and its recent posts

```json
{ "mode": "profile", "input": ["@gymshark"], "includeVideos": true, "maxVideosPerProfile": 30 }
```

#### Example: what a hashtag is actually tagged with

```json
{ "mode": "hashtag", "input": ["#coffee"], "maxResults": 200, "strictHashtagMatch": true }
```

#### Example: mid-tier creators in a niche

```json
{ "mode": "users", "input": ["fitness coach"], "minFollowers": 10000, "maxFollowers": 200000 }
```

### Output Overview

Three row types, told apart by `rowType`.

**`video`** comes from profile, videos, hashtag and search. The same 21 core fields in every case, even though those modes read two different TikTok payloads behind the scenes. Hashtag rows carry a few extra fields the web feed provides.

**`account`** comes from profile and users.

**`comment`** comes from comments mode. Replies carry `isReply: true` and the `parentCommentId` they belong to.

**`post`** is its own richer type, from post mode only: slideshow images, repost count, ad and AI-generated flags, duet and stitch settings, media URLs.

### Output Sample

A video row, from a `search` run on "latte art":

```json
{
  "rowType": "video",
  "source": "latte art",
  "postId": "7112843091046010118",
  "postUrl": "https://www.tiktok.com/@tannercolson/video/7112843091046010118",
  "caption": "How to Pour Latte Art - Milk Steaming Basics",
  "createdAt": "2026-06-14T18:22:11.000Z",
  "durationSeconds": 43,
  "isPinned": false,
  "playCount": 412254,
  "likeCount": 28911,
  "commentCount": 402,
  "shareCount": 1873,
  "saveCount": 9204,
  "authorId": "6749884166599225349",
  "authorUsername": "tannercolson",
  "authorNickname": "Tanner Colson",
  "authorFollowerCount": 288400,
  "musicId": "7112843060234",
  "musicTitle": "original sound",
  "coverUrl": "https://p16-sign.tiktokcdn-us.com/...",
  "scrapedAt": "2026-08-22T21:14:03.221Z"
}
```

An account row, from `users`:

```json
{
  "rowType": "account",
  "source": "fitness coach",
  "username": "stephycrismatt",
  "nickname": "Stephy Crismatt",
  "userId": "6721944883108135942",
  "profileUrl": "https://www.tiktok.com/@stephycrismatt",
  "bio": "Coach · Programs below",
  "isVerified": false,
  "verificationSource": "search",
  "followerCount": 425074,
  "followingCount": 812,
  "likeCount": 9440221,
  "videoCount": 1204,
  "region": "US",
  "scrapedAt": "2026-08-22T21:15:40.118Z"
}
```

### Key Output Fields

**Every row**: `rowType`, `source`, `scrapedAt`

**Video**: `postId`, `postUrl`, `caption`, `createdAt`, `durationSeconds`, `isPinned`, `playCount`, `likeCount`, `commentCount`, `shareCount`, `saveCount`, `authorId`, `authorUsername`, `authorNickname`, `authorFollowerCount`, `musicId`, `musicTitle`, `coverUrl`

**Account**: `username`, `nickname`, `userId`, `secUid`, `profileUrl`, `bio`, `isVerified`, `followerCount`, `followingCount`, `likeCount`, `videoCount`, `region`

**Comment**: `commentId`, `text`, `likeCount`, `replyCount`, `isReply`, `parentCommentId`, `commenterUsername`, `commenterVerified`, `isPinnedByAuthor`, `isLikedByAuthor`

**Post**: everything a video row has, plus `postType`, `repostCount`, `isAd`, `isAigc`, `duetEnabled`, `stitchEnabled`, `hashtags`, `mentions`, `videoUrl`, `imageUrls`

### FAQ

**Why one actor instead of seven separate ones?**
Because one input shape and one output schema is less work than seven. The specialist actors remain published for anyone who prefers them.

**Are video rows really the same across modes?**
Yes, and it took work. Profile, videos and search read TikTok's mobile payload while the hashtag feed reads its web payload, with different field names for the same things. Both are normalised to one shape here. Hashtag rows carry a few extra fields the web payload genuinely provides.

**Why does a hashtag run return posts without that hashtag?**
TikTok's hashtag feed is a related-content feed, not a tag index. On a live #coffee feed, 23 of 37 posts actually carried the tag. Set `strictHashtagMatch` to drop the rest.

**I asked for 40 results and got 40. Is that normal?**
It is here. Limits are honoured exactly rather than rounded up to whatever a page holds.

**Why is `verificationSource` on account rows?**
Because account search is a weaker source for the verified badge than a profile lookup. The field tells you which one you are reading, so you know how much to trust it. Run `profile` mode when the badge has to be authoritative.

**What happens if a run hits its time limit?**
It stops cleanly and returns what it collected, with a warning naming the count. A partial result beats a dead run.

**Do I need a proxy or a TikTok account?**
No. No login, no cookies, no proxy configuration, and nothing to solve.

### Support

Found a bug or missing a field? Open an issue on the actor's Issues tab.

### Additional Services

Need a mode that is not here, a scheduled feed into your own warehouse, or a custom field set? Describe it on the Issues tab.

### Explore More Scrapers

The specialist TikTok actors behind this bundle stay published, along with actors for TikTok Shop, Instagram, YouTube, LinkedIn and more. Browse the publisher profile.

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/tiktok-scraper`).

**Purpose:** one TikTok actor with seven modes over accounts, posts, hashtags, keyword search, comments and account discovery.

**Minimal input:**

```json
{ "mode": "profile", "input": ["@gymshark"] }
```

**Output:** one row per item, tagged by `rowType` — `video`, `account`, `comment` or `post`. Video rows: rowType, source, postId, postUrl, caption, createdAt, durationSeconds, isPinned, playCount, likeCount, commentCount, shareCount, saveCount, authorId, authorUsername, authorNickname, authorFollowerCount, musicId, musicTitle, coverUrl, scrapedAt.

**Behaviors an agent should know:**

- `mode` decides what `input` means. Passing usernames with mode `search` will search for them as keywords.
- Always set `maxResults` (hashtag, search, users, comments) or `maxVideosPerProfile` (profile, videos). Uncapped runs on a busy hashtag are large.
- Limits are exact, so plan cost from the number you ask for.
- Branch on `rowType`, not on the mode you requested: `profile` with `includeVideos` returns both `profile` and `video` rows.
- A target that cannot be delivered yields one row carrying `error` and `message`; the run still ends SUCCEEDED. Check for `error` before treating a row as data.
- Billing: every delivered row is charged. Error rows are charged too, so filter inputs before large runs.

### ⚠️ Disclaimer

This Actor accesses publicly available data on TikTok for legitimate research and business-analysis purposes. It does not log in, bypass authentication, or access private content. Use of this Actor must comply with TikTok's Terms of Service and all applicable laws, including data-protection regulations (GDPR, CCPA, etc.). The authors are not responsible for any misuse. Users must:

- Respect rate limits and avoid overloading TikTok's infrastructure
- Not use scraped data to violate user privacy or platform terms
- Process any personal data only with a lawful basis and in compliance with their jurisdiction
- Not republish scraped content in violation of copyright

We do not store scraped data; the Actor returns it directly to your Apify dataset for your authorized use. TikTok is a trademark of ByteDance Ltd.; this Actor is not affiliated with or endorsed by TikTok or ByteDance.

### SEO Keywords

tiktok scraper, tiktok api, tiktok profile scraper, tiktok video scraper, tiktok hashtag scraper, tiktok search scraper, tiktok comments scraper, tiktok user search, tiktok data extraction, tiktok analytics tool, tiktok creator discovery, tiktok engagement data, scrape tiktok without login, tiktok influencer research, social media scraper, tiktok content research, tiktok post metrics, tiktok bulk scraper, tiktok no proxy scraper, tiktok mcp server

# Actor input Schema

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

Which job to run. This decides what the "input" list below means. profile: usernames in, account details out. videos: usernames in, that account's posts out. post: post URLs or IDs in, one detailed row each. hashtag: hashtags in, the videos in each feed out. search: keywords in, matching videos out. comments: video URLs or IDs in, their comments out. users: keywords in, matching accounts out.

## `input` (type: `array`):

What to scrape. The meaning follows the mode you picked. For profile and videos, usernames or profile URLs such as "@gymshark" or "https://www.tiktok.com/@gymshark". For post and comments, post URLs or bare IDs such as "https://www.tiktok.com/@gymshark/video/7660964717631999254" or "7660964717631999254"; short vm.tiktok.com links are resolved for you. For hashtag, tags such as "#coffee" or "coffee". For search and users, plain keywords such as "latte art".

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

How many rows to collect for each entry in the list above. Applies to hashtag, search, users and comments. The exact number is honoured rather than rounded up to a page boundary, so asking for 37 returns 37. Default 100.

## `includeVideos` (type: `boolean`):

Profile mode only. Adds that account's posts after its profile row. Leave off for a fast account lookup. Default false.

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

How many posts to collect per account in videos mode, or in profile mode when "Also fetch posts" is on. Up to 33 comes from a single cheap request; anything above that pages a second, slower source. Default 100.

## `strictHashtagMatch` (type: `boolean`):

Hashtag mode only. TikTok's hashtag feed is a related-content feed, not a tag index: a #coffee run also returns posts tagged only #coffeetok. Measured on a live feed, 23 of 37 posts carried the requested tag. Turn this on to drop the rest, which matters for tag-share analysis. Default false.

## `minFollowers` (type: `integer`):

Users mode only. Skip accounts below this follower count. Combine with the maximum to target a size band, for example 10000 to 100000 for mid-tier creators. Default 0, meaning no lower limit.

## `maxFollowers` (type: `integer`):

Users mode only. Skip accounts above this follower count. Useful for finding smaller creators rather than the household names that dominate search results. Leave empty for no upper limit.

## `verifiedOnly` (type: `boolean`):

Users mode only. Return just accounts carrying TikTok's verification badge. Default false.

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

Comments mode only. Adds reply rows under each top-level comment that has any. Reply rows carry isReply true and the parentCommentId they belong to. Default false.

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

Comments mode only, and only when replies are on. Caps how many replies to collect under each comment. Default 50.

## `includeMediaUrls` (type: `boolean`):

Post mode only. Adds the video file URL, cover image, music URL, and for slideshows the list of image URLs. Default true.

## `includeAuthorStats` (type: `boolean`):

Post mode only. Adds the author's bio, avatar, follower, following, like and video counts to each post row. Default true.

## `includeHashtagsMentions` (type: `boolean`):

Post mode only. Adds the post's hashtags and @-mentions, read from TikTok's structured data and falling back to parsing the caption when that data is missing. Default true.

## Actor input object example

```json
{
  "mode": "profile",
  "input": [
    "@gymshark",
    "@khaby.lame"
  ],
  "maxResults": 100,
  "includeVideos": false,
  "maxVideosPerProfile": 100,
  "strictHashtagMatch": false,
  "minFollowers": 0,
  "verifiedOnly": false,
  "includeReplies": false,
  "maxRepliesPerComment": 50,
  "includeMediaUrls": true,
  "includeAuthorStats": true,
  "includeHashtagsMentions": true
}
```

# Actor output Schema

# 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 = {
    "input": [
        "@gymshark"
    ]
};

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

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

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "input": [
    "@gymshark"
  ]
}' |
apify call memo23/tiktok-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/tiktok-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/YK9EsWtqBMkqDk6qP/builds/45ybInw7cISDChEAc/openapi.json
