# TikTok Scraper (`calm_builder/tiktok-scraper`) Actor

Scrape TikTok videos, profiles, hashtags, search results, sounds and playlists in one Actor. Get views, likes, shares, saves, captions, subtitles, music and author data, plus comments with replies and follower lists. Date and popularity filters included free. Fast runs, pay only per result.

- **URL**: https://apify.com/calm\_builder/tiktok-scraper.md
- **Developed by:** [Coder](https://apify.com/calm_builder) (community)
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 video scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## TikTok Scraper

Collect TikTok videos, profiles, hashtags, search results, sounds, playlists, comments and follower lists in one Actor, as clean, structured data.

Add usernames, hashtags, keywords or any TikTok link, choose how many videos you need, and export the results as JSON, CSV or Excel, or pull them through the Apify API. Results appear in your dataset while the run is still going.

### What This Actor Does

- Scrapes **profiles** — account details plus their videos, reposts, public liked videos, playlists and active stories
- Scrapes **hashtags**, **sounds** and **playlists**
- Searches TikTok for **videos**, **photo posts**, **creators** or the **Top** tab
- Accepts **any TikTok link** — videos, photo posts, profiles, hashtags, sounds, playlists and short `vm.tiktok.com` / `vt.tiktok.com` links
- Returns views, likes, comments, shares, saves, captions, hashtags, mentions, music, cover images and photo-post images
- Optionally adds **comments with their replies**, nested inside each video
- Optionally adds the **full author profile** to every video
- Optionally adds **subtitles** as plain text, in every language TikTok provides
- Optionally collects **followers** and **following** lists
- **Date filters, view and like filters and sorting are included at no extra cost**

### Best For

- Influencer discovery and creator vetting
- Trend, hashtag and sound research
- Competitor and brand monitoring
- Comment and sentiment analysis
- Building datasets for dashboards, BI, NLP or AI workflows

### Input

Fill in at least one of **Profiles**, **Hashtags**, **Search terms** or **TikTok links**. You can combine them freely in one run.

#### Main input fields

| Field                                                              | What it does                                                                                             |
| ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
| `profiles`                                                         | Usernames or profile links, e.g. `khaby.lame`, `@khaby.lame` or `https://www.tiktok.com/@khaby.lame`     |
| `hashtags`                                                         | Hashtags with or without `#`, e.g. `fitness`                                                             |
| `searchQueries`                                                    | Keywords, exactly as you would type them into TikTok search                                              |
| `startUrls`                                                        | Any TikTok links — the link type is detected automatically                                               |
| `resultsPerPage`                                                   | How many videos to collect from each profile, hashtag, search term, sound or playlist (default 10)       |
| `searchSection`                                                    | Which search tab to use: `videos`, `photos`, `creators` or `top`                                         |
| `profileSections`                                                  | Which parts of a profile to collect: `videos`, `reposts`, `liked`, `playlists`, `stories`                |
| `newerThan` / `olderThan`                                          | Only videos from a date range. Accepts a date (`2025-06-01`) or a relative period (`7 days`, `3 months`) |
| `minViews` / `minLikes`                                            | Only videos above a popularity threshold                                                                 |
| `sortBy`                                                           | `default`, `newest`, `oldest`, `mostViewed`, `mostLiked` or `mostShared`                                 |
| `includeComments` / `maxCommentsPerVideo`                          | Add comments to each video                                                                               |
| `includeReplies` / `maxRepliesPerComment`                          | Add replies under each comment                                                                           |
| `includeAuthorProfiles`                                            | Add the author's full profile to each video                                                              |
| `includeSubtitles`                                                 | Add subtitle text to each video                                                                          |
| `includeFollowers` / `includeFollowing` / `maxFollowersPerProfile` | Collect follower and following lists (up to 1,000 per list)                                              |
| `country`                                                          | The country TikTok sees your requests coming from (default United States)                                |

#### Example input

```json
{
  "profiles": ["khaby.lame"],
  "hashtags": ["booktok"],
  "searchQueries": ["healthy breakfast"],
  "resultsPerPage": 20,
  "newerThan": "30 days",
  "sortBy": "mostViewed",
  "includeComments": true,
  "maxCommentsPerVideo": 20
}
```

### Output

Results are split into three tables, each shown on the run's **Output** tab and each with its own export:

- **Videos** — one row per video or photo post (the default dataset)
- **Profiles** — one row per account, from profiles and creator searches
- **Followers & following** — one row per account in a follower or following list, with `profileUsername` showing whose list it is

Videos collected from a profile link back to that profile through `profileId`, so the profile's details are stored once instead of being repeated on every video. Comments and their replies are nested inside each video, so a video and its conversation stay together in one row.

Every field is included when you export or use the API. The **Export** button on the run page exports videos; export profiles and follower lists from the **Output** or **Storage** tab.

#### Video fields

| Field                                                                              | Description                                                                     |
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `id`, `url`                                                                        | Video ID and link                                                               |
| `text`                                                                             | Caption                                                                         |
| `createdAt`                                                                        | Upload time (ISO 8601)                                                          |
| `viewCount`, `likeCount`, `commentCount`, `shareCount`, `saveCount`, `repostCount` | Engagement                                                                      |
| `durationSeconds`, `width`, `height`                                               | Video details                                                                   |
| `coverUrl`, `images`                                                               | Cover image, and all images of a photo post                                     |
| `isPhotoPost`, `isPinned`, `isAd`, `isAiGenerated`                                 | Flags                                                                           |
| `hashtags`, `mentions`                                                             | Hashtags and mentioned usernames                                                |
| `music`                                                                            | Sound ID, title, author, original-sound flag, duration, cover and link          |
| `author`                                                                           | Author ID and username — or the full profile when `includeAuthorProfiles` is on |
| `profileId`                                                                        | The profile row this video belongs to (videos collected from a profile)         |
| `hasSubtitles`, `subtitles`                                                        | Whether the video has subtitles, and their text when `includeSubtitles` is on   |
| `comments`                                                                         | Comments with nested `replies`, when `includeComments` is on                    |
| `source`                                                                           | Where the video came from, e.g. `{ "type": "hashtag", "value": "booktok" }`     |

#### Profile fields

| Field                                                                       | Description                                         |
| --------------------------------------------------------------------------- | --------------------------------------------------- |
| `id`, `username`, `name`, `url`                                             | Account identity                                    |
| `bio`, `bioLink`, `avatarUrl`                                               | Profile details                                     |
| `followerCount`, `followingCount`, `likeCount`, `videoCount`, `friendCount` | Account statistics                                  |
| `verified`, `isPrivate`, `isBusinessAccount`, `isSeller`, `hasActiveStory`  | Flags                                               |
| `createdAt`                                                                 | When the account was created (when TikTok shows it) |

#### Example output

A profile row:

```json
{
  "type": "profile",
  "id": "68616495085350913",
  "username": "zachking",
  "name": "Zach King",
  "url": "https://www.tiktok.com/@zachking",
  "bio": "Bringing a little more wonder into the world, 15 seconds at a time",
  "bioLink": "https://youtu.be/ulVscDewRPI",
  "verified": true,
  "isPrivate": false,
  "followerCount": 86879109,
  "followingCount": 182,
  "likeCount": 1297944129,
  "videoCount": 617,
  "createdAt": "2016-02-29T07:29:22.000Z",
  "source": { "type": "profile", "value": "zachking" }
}
```

A video row from a hashtag, with comments and replies:

```json
{
  "type": "video",
  "id": "7064703044331523375",
  "url": "https://www.tiktok.com/@shereinabdelhady/video/7064703044331523375",
  "text": "there's alot of emotions in this video 😂 #fyp #gym #fitness",
  "createdAt": "2022-02-14T22:55:57.000Z",
  "isPhotoPost": false,
  "viewCount": 43800000,
  "likeCount": 8700000,
  "commentCount": 34200,
  "shareCount": 66600,
  "saveCount": 176619,
  "durationSeconds": 26,
  "coverUrl": "https://p16-common-sign.tiktokcdn-us.com/...",
  "hashtags": ["fyp", "gym", "fitness"],
  "mentions": [],
  "music": {
    "id": "7022717423417854725",
    "title": "without me",
    "author": "favsoundds",
    "isOriginal": true,
    "durationSeconds": 34
  },
  "author": { "id": "6733632838111921157", "username": "shereinabdelhady" },
  "profileId": null,
  "hasSubtitles": false,
  "source": { "type": "hashtag", "value": "fitness" },
  "comments": [
    {
      "id": "7066227699052135174",
      "text": "el último chico sintió hasta mariposas aksjkaka",
      "createdAt": "2022-02-19T01:32:29.000Z",
      "likeCount": 3624,
      "replyCount": 4,
      "isPinned": false,
      "isLikedByCreator": false,
      "author": {
        "id": "6803194333148742661",
        "username": "kmipito",
        "name": "kmi"
      },
      "replies": [
        {
          "id": "7071337386764567302",
          "text": "Siiiiiiiiiii 😂😂",
          "createdAt": "2022-03-04T20:00:39.000Z",
          "likeCount": 27,
          "author": {
            "id": "6764505400462263302",
            "username": "a_n_t_i_t_e_s_i_s"
          }
        }
      ]
    }
  ]
}
```

A follower row (in the Followers & following table):

```json
{
  "type": "follower",
  "profileId": "68616495085350913",
  "profileUsername": "zachking",
  "id": "7301234567890123456",
  "username": "example.user",
  "name": "Example User",
  "followerCount": 72,
  "followingCount": 172,
  "videoCount": 6
}
```

### How To Use

1. Add profiles, hashtags, search terms or TikTok links.
2. Set **Maximum videos per source**.
3. Optionally add date or popularity filters and a sort order.
4. Turn on comments, author profiles, subtitles or follower lists only if you need them.
5. Run the Actor and open the **Output** tab, or export the results as JSON, CSV or Excel, or read them through the Apify API.

### Pricing

This Actor uses pay-per-event pricing, so you only pay for results you receive:

- **Video** — each video or photo post saved to your dataset
- **Profile** — each profile row, and each unique author when **Add full author profiles** is on (charged once per author per run, however many of their videos you collect)
- **Comment** — each comment and each reply, only when comments are on
- **Follower** — each follower or followed account, only when follower lists are on
- **Actor start** — a small fee each time a run starts

Date filters, view and like filters, sorting, subtitles and country selection are **included at no extra cost**. Videos that are private, deleted or unavailable, and lists an account keeps hidden, are never charged. Current prices for each Apify plan are shown on the Actor's Pricing tab.

If a run reaches the maximum cost you set for it, it stops cleanly and keeps everything collected so far.

#### Free plan

On the Apify free plan every feature is available to try, with smaller amounts per run: up to **3** profiles, hashtags, search terms or links, **10** videos per source (**5** per section when you select several profile sections), **10** comments per video, **3** replies per comment and **20** accounts per follower list. Upgrade to a paid Apify plan to remove these limits.

### Best Practices

- Start with a small **Maximum videos per source** to check the output quickly.
- Use **Only videos newer than** for recurring monitoring, so each run only collects new videos. For profiles, the Actor jumps straight to your date range, so filtered runs are fast.
- Keep **Scrape as if from** on **United States** unless you need local results — it gives the most complete search results.
- Turn on comments only for runs that need them; runs without comments are faster and cheaper.
- Use JSON export when you need nested data such as comments, replies, subtitles and music.

### FAQ

#### Why did a source return fewer videos than I asked for?

The profile, hashtag or search may simply have fewer matching videos, especially with date or popularity filters. The run log tells you when a source returned fewer results and why.

#### Can I collect private accounts?

No. For a private account only its public profile details are saved. Liked videos and following lists are collected only when the account has made them public; otherwise the log says so and nothing is charged.

#### How does sorting work?

Sorting orders the videos collected from each source, up to your **Maximum videos per source**. For profiles, **Newest first** and **Oldest first** come straight from TikTok, so an oldest-first run really starts at the account's first video.

#### Are subtitles available for every video?

Only for videos that have subtitles on TikTok, including TikTok's auto-generated captions. `hasSubtitles` tells you which videos have them.

#### How long does a run take?

Most runs finish in under a minute. Several profiles with 10 videos each typically take 10–20 seconds. Comments, replies and long follower lists add time.

#### Why does a follower list stop at 1,000?

Follower lists are loaded page by page, so very long lists are slow to collect. The limit keeps runs fast and predictable.

### Responsible Use

This Actor collects publicly available data only. Make sure your use complies with TikTok's terms, applicable laws and data-protection rules such as the GDPR, especially when you process personal data such as usernames, profiles and comments.

### Troubleshooting

- **No results for a search term** — try the **United States** country setting and a simpler keyword.
- **A link was skipped** — check that it is a public TikTok video, profile, hashtag, sound or playlist link.
- **A profile could not be loaded** — the account may have been renamed, banned or deleted.

If something still does not work, open an issue on the Actor's Issues tab with your run link and we will take a look.

# Actor input Schema

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

TikTok usernames or profile links, one per row.

Supported formats:

- `khaby.lame`
- `@khaby.lame`
- `https://www.tiktok.com/@khaby.lame`

Each profile is saved once as its own row with `type: "profile"`, and its videos are saved as separate rows that link back to it through `profileId`. Use **Profile sections** below to also collect reposts, liked videos, playlists or stories.

## `hashtags` (type: `array`):

Hashtags to collect videos from, one per row, with or without the `#`.

Examples: `fitness`, `#booktok`.

## `searchQueries` (type: `array`):

Keywords to search TikTok for, one per row — exactly as you would type them into the TikTok search bar.

Choose what kind of results you want with **Search results type** below.

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

Direct TikTok links, one per row. The Actor recognises the link type automatically.

Supported links:

- Video — `https://www.tiktok.com/@user/video/7301234567890123456`
- Photo post — `https://www.tiktok.com/@user/photo/7301234567890123456`
- Profile — `https://www.tiktok.com/@user`
- Hashtag — `https://www.tiktok.com/tag/fitness`
- Sound — `https://www.tiktok.com/music/original-sound-7301234567890123456`
- Playlist — `https://www.tiktok.com/@user/playlist/name-7301234567890123456`

Short links (`vm.tiktok.com`, `vt.tiktok.com`) work too.

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

How many videos to collect from **each** profile, hashtag, search term, sound or playlist.

Direct video links always return one video each, regardless of this value.

## `searchSection` (type: `string`):

Which TikTok search tab to collect results from. Only applies to **Search terms**.

- **Videos** — matching videos
- **Photos** — matching photo posts
- **Creators** — matching accounts (returned as profiles)
- **Top** — TikTok's mixed "Top" tab

## `profileSections` (type: `array`):

Which parts of each profile to collect. Only applies to **Profiles** and profile links.

- **Videos** — the videos the account posted
- **Reposts** — videos the account reposted
- **Liked videos** — only visible when the account has made its likes public
- **Playlists** — every video in the account's playlists
- **Stories** — currently active stories

Sections the account keeps private are skipped and noted in the log.

## `newerThan` (type: `string`):

Skip videos posted before this date. Pick a date, or enter a relative period such as `7 days` or `3 months`.

For profiles, the Actor jumps straight to this date range instead of scrolling through older videos, so filtered runs finish faster.

## `olderThan` (type: `string`):

Skip videos posted after this date. Combine with **Only videos newer than** to collect a specific period.

## `minViews` (type: `integer`):

Only return videos with at least this many views. Leave empty to keep all videos.

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

Only return videos with at least this many likes. Leave empty to keep all videos.

## `sortBy` (type: `string`):

Order of the videos collected from each source.

- **Default** — the order TikTok shows them in
- **Newest first** / **Oldest first** — by upload date
- **Most viewed** / **Most liked** / **Most shared** — by engagement

Sorting applies to the videos collected from each source, up to your **Maximum videos per source**.

## `includeComments` (type: `boolean`):

Add comments to every video in the run. They are nested inside each video in the `comments` list, so a video and its conversation stay together in one row.

Comments are an optional add-on with their own price. A video appears in the dataset once its comments have been collected.

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

How many top-level comments to collect from each video.

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

Also collect replies to each comment. Replies are nested inside their comment in the `replies` list.

Requires **Include comments**. Each reply is charged as a comment.

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

How many replies to collect under each comment.

## `includeFollowers` (type: `boolean`):

Collect the accounts that follow each profile in the run.

Follower lists are an optional add-on with their own price.

## `includeFollowing` (type: `boolean`):

Collect the accounts each profile follows.

Some accounts hide this list. When that happens the profile is marked as private in the log and nothing is charged.

## `maxFollowersPerProfile` (type: `integer`):

How many followers (and, separately, how many followed accounts) to collect for each profile.

## `includeAuthorProfiles` (type: `boolean`):

Add the author's full profile — bio, followers, following, total likes, video count, links and more — inside each video's `author` field.

Useful for hashtag, search, sound and direct-link videos. Videos collected from **Profiles** already link to their profile row through `profileId`, so they are not affected.

Each unique author is charged once per run as a profile, however many of their videos you collect.

## `includeSubtitles` (type: `boolean`):

Add the full subtitle text of each video in the `subtitles` field, in every language TikTok provides. Included at no extra cost.

Only videos that have subtitles on TikTok (including auto-generated captions) return them.

## `country` (type: `string`):

The country TikTok sees your requests coming from. This affects which search, hashtag and sound results are shown. Included at no extra cost.

The default, **United States**, gives the most complete and reliable results. Countries where TikTok is not available are not listed.

## Actor input object example

```json
{
  "profiles": [
    "khaby.lame"
  ],
  "resultsPerPage": 10,
  "searchSection": "videos",
  "profileSections": [
    "videos"
  ],
  "sortBy": "default",
  "includeComments": false,
  "maxCommentsPerVideo": 20,
  "includeReplies": false,
  "maxRepliesPerComment": 10,
  "includeFollowers": false,
  "includeFollowing": false,
  "maxFollowersPerProfile": 100,
  "includeAuthorProfiles": false,
  "includeSubtitles": false,
  "country": "US"
}
```

# Actor output Schema

## `videos` (type: `string`):

No description

## `profiles` (type: `string`):

No description

## `followers` (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 = {
    "profiles": [
        "khaby.lame"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("calm_builder/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 = { "profiles": ["khaby.lame"] }

# Run the Actor and wait for it to finish
run = client.actor("calm_builder/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 '{
  "profiles": [
    "khaby.lame"
  ]
}' |
apify call calm_builder/tiktok-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,calm_builder/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/94Njmx6xraPSK5AVu/builds/Ta9Vsd4eN1oMpaSCd/openapi.json
