# Tiktok Video Scraper (`scraper_creator/tiktok-video-scraper`) Actor

Scrape video data and follower counts from public TikTok profiles. Extract video URLs, captions, views, likes, comments, shares, and posting dates for reporting and analysis.

- **URL**: https://apify.com/scraper_creator/tiktok-video-scraper.md
- **Developed by:** [Scraper Creator](https://apify.com/scraper_creator) (community)
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 tiktok posts

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?

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 Video Scraper

Collect public videos and photo posts from TikTok profiles, together with engagement statistics and the account's current follower count. Get captions, hashtags, post URLs, publication dates, views, likes, comments, and more in a structured dataset for reporting and analysis.

The Actor reads the profile's Videos section and follows pagination to older posts. It uses a separate browser session, does not require TikTok login cookies, and does not download media files.

### How to use

1. Add one to five public TikTok profile URLs.
2. Set the maximum number of posts to return **per profile**. The default is 100; the allowed range is 1–10,000.
3. Under **Advanced settings**, keep the default residential proxy in Malaysia or choose another country. Direct connections do not return video lists on Apify cloud, so a run with no proxy stops immediately with `PROXY_REQUIRED` in its run summary.
4. Start the Actor, inspect the dataset and run summary, then export the results or retrieve them through the Apify API.

The maximum is a limit, not a guaranteed result count. A profile may have fewer accessible posts, and some profiles may expose follower counts while their video lists remain unavailable.

### Input

```json
{
  "profiles": ["https://www.tiktok.com/@nba"],
  "resultsPerPage": 100
}
```

| Field | Required | Description |
| --- | --- | --- |
| `profiles` | Yes | Array of 1–5 public HTTPS TikTok profile URLs. |
| `resultsPerPage` | No | Maximum successfully returned posts per profile, from 1 to 10,000. Defaults to 100. Despite its name, this is the total per-profile limit, not the size of a single network request. |
| `proxyConfiguration` | No | Apify proxy configuration. Defaults to a residential proxy in Malaysia (`MY`). See [Proxy](#proxy). |

Use profile URLs such as `https://www.tiktok.com/@nba`. Both `tiktok.com` and `www.tiktok.com` are accepted. Do not include query parameters or URL fragments. Individual post URLs, shortened links, and bare usernames are not supported as input.

The Actor uses the profile's **Latest** view and includes pinned posts. Results are therefore not guaranteed to be strictly chronological. Other sorting modes and excluding pinned posts are not supported.

#### Additional API fields

Existing integrations can continue to send the following optional fields with the values shown. They do not enable additional scraping features and are omitted from the input form:

```json
{
  "profiles": ["https://www.tiktok.com/@nba"],
  "resultsPerPage": 100,
  "profileScrapeSections": ["videos"],
  "profileSorting": "latest",
  "excludePinnedPosts": false,
  "commentsPerPost": 0,
  "topLevelCommentsPerPost": 0,
  "maxRepliesPerComment": 0,
  "maxFollowersPerProfile": 0,
  "maxFollowingPerProfile": 0,
  "shouldDownloadAvatars": false,
  "shouldDownloadCovers": false,
  "shouldDownloadSlideshowImages": false,
  "shouldDownloadVideos": false
}
```

These settings collect post statistics and follower totals only. Comment text, follower and following lists, and media downloads are not supported.

#### Proxy

The Actor uses an Apify residential proxy in Malaysia by default:

```json
{
  "profiles": ["https://www.tiktok.com/@nba"],
  "resultsPerPage": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "MY"
  }
}
```

In Apify cloud tests on 2026-10-19, direct connections received empty video-list responses, and a residential proxy without a country also failed. Residential proxies in Malaysia (`MY`) and Singapore (`SG`) succeeded. Other countries are untested. Each profile uses one proxy session, so its page and video-list requests share an IP. On Apify, a missing `proxyConfiguration` falls back to this default, and an explicit choice of no proxy stops the run with `PROXY_REQUIRED`. Local runs may still connect directly.

Proxy availability depends on your account. Residential proxy traffic adds cost and does not guarantee access to TikTok. Check the Actor's pricing and your proxy rates before running.

### Output

Each dataset row represents one video or photo post. The following is a saved example, not live statistics:

```json
{
  "id": "7697426312062946591",
  "text": "Hali with the BIG one hand jam 💪 #NBA #basketball #Pacers #TyreseHaliburton",
  "createTime": 1792196738,
  "createTimeISO": "2026-10-17T00:25:38.000Z",
  "webVideoUrl": "https://www.tiktok.com/@nba/video/7697426312062946591",
  "authorMeta": {
    "id": "134941634731003904",
    "name": "nba",
    "profileUrl": "https://www.tiktok.com/@nba",
    "fans": 27302212
  },
  "playCount": 20500,
  "diggCount": 1398,
  "commentCount": 27,
  "shareCount": 35,
  "collectCount": 64,
  "repostCount": 0,
  "isSlideshow": false,
  "fromProfileSection": "videos",
  "hashtags": [
    { "name": "NBA" },
    { "name": "basketball" },
    { "name": "pacers" },
    { "name": "tyresehaliburton" }
  ],
  "scrapedAt": "2026-10-17T09:33:26.340Z"
}
```

| Field | Description |
| --- | --- |
| `id`, `webVideoUrl` | Post ID and canonical URL. Photo posts use a `/photo/` URL; videos use `/video/`. IDs are strings. |
| `text` | Post caption, with original line breaks and blank lines preserved when available. |
| `hashtags` | Array of objects such as `{ "name": "basketball" }`, using TikTok's tag names without an added `#` prefix. Empty when none are available. |
| `createTime` | Publication time as a Unix timestamp in seconds. |
| `createTimeISO` | The same publication time in UTC ISO 8601 format. |
| `authorMeta.id`, `authorMeta.name`, `authorMeta.profileUrl` | Verified author ID, username, and profile URL. The ID is a string. |
| `authorMeta.fans` | Current profile follower total, repeated on the profile's post rows. This is not the follower count at the time each post was published. |
| `playCount` | Public view count as an integer. |
| `diggCount`, `commentCount` | Like and comment counts as integers, or `null` when unavailable. |
| `shareCount`, `collectCount`, `repostCount` | Share, save, and repost counts as integers, or `null` when unavailable. |
| `isSlideshow` | `true` for a photo slideshow and `false` for a video. |
| `fromProfileSection` | The value `videos`, including photo posts returned in this section. |
| `scrapedAt` | Time this row was assembled, in UTC ISO 8601 format. |

#### Data accuracy

- **Unknown counts stay `null`.** Missing optional metrics are not replaced with zero. A confirmed zero is returned as `0`. Compact labels such as `46.8K` are not converted into exact counts.
- **Counts are snapshots.** Engagement statistics can change during or after a run. The follower total is read from the profile and attached only after the post's author has been verified.
- **Caption formatting follows the source.** When TikTok supplies valid caption lines, `text` joins them with line breaks, preserving blank lines. Otherwise it uses the available description without guessing line breaks.
- **Public statistics are not private analytics.** This Actor does not retrieve owner-only TikTok Analytics data or historical follower totals.

### Partial results and errors

Inspect `RUN-SUMMARY` in the run's default key-value store when checking completeness. It contains per-profile status, page and result counts, available follower totals, and errors.

- Invalid individual posts are skipped so other posts can still be processed.
- A profile or pagination failure preserves results already collected and allows the next profile to proceed.
- Some valid results can produce a successful Apify run while `RUN-SUMMARY.status` is `partial` and `partial` is `true`. Always inspect the summary if complete coverage matters.
- An exhausted pay-per-event budget or an abort signal stops collection. With no recorded errors, `RUN-SUMMARY.status` is `stopped` even when `partial` is `false`; profiles not yet attempted may be absent from the summary. Check the run status, per-profile statuses, and coverage of the requested profiles rather than treating `partial: false` as proof of completion. If errors also occurred, the run status may instead be `partial` or `failed`.
- If errors occur and no valid posts are available after all profiles have been attempted, the run fails.
- Storage or charging failures stop the run. Error records are not added to the post dataset.

Missing or invalid post identity, author identity, publication time, caption, content type, or view count causes the post to be skipped. An unreadable profile identity or follower total causes that profile to fail.

Per-profile status `limit_reached` means the requested maximum was reached; it does not mean there are no more posts. Invalid responses, timeouts, stalled pagination, and an unverified empty list are reported as errors rather than interpreted as a profile with no posts.

Profiles with no returned posts produce no dataset rows, including no standalone follower row. A follower total read successfully may still be available in the profile's `RUN-SUMMARY` entry. Integrations that need this value must read the summary separately.

### Refreshing statistics

Start a new run with a fresh default dataset to refresh counts. Each new run starts from the profile's Latest view, including pinned posts, and continues until the per-profile limit or the end of the accessible feed. Posts outside that limit will not be refreshed.

Within the same run, duplicate post IDs are removed. If a run restarts with its existing dataset, already written posts are skipped rather than refreshed.

Profiles whose saved results already meet the limit are not reopened. Their resumed summary reports `limit_reached`, zero fetched pages, and `followers: null` because no fresh follower count was read; saved dataset rows are unchanged.

### Scope and limitations

- Collects accessible public videos and photo posts from the profile's Videos section. It does not collect Stories, favorites, repost feeds, or private posts.
- Returns follower totals, not follower identities or following lists.
- Returns comment counts, not comment text or replies.
- Does not download videos, audio, avatars, covers, or slideshow images.
- Some public profiles expose follower totals while video-list requests fail. Successful access to one profile does not guarantee access to another, and proxies may not resolve the issue.
- The 10,000-result input limit is not a guarantee of full profile coverage. TikTok changes and access restrictions can affect pagination and data availability.
- Apify cloud runs have been validated only for small runs (2 posts per profile) through residential proxies in Malaysia and Singapore. Multi-page pagination in the cloud, other proxy countries, and large or long-running crawls have not yet been validated.

# Changelog

This Actor's version history is a separate document: https://apify.com/scraper_creator/tiktok-video-scraper/changelog.md

# Actor input Schema

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

One to five public TikTok profile URLs.

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

Maximum successfully returned posts per account, including video and photo posts.

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

Defaults to an Apify residential proxy in Malaysia. In Apify cloud tests (2026-10-19), direct connections returned empty video lists and a residential proxy without a country failed; Malaysia (MY) and Singapore (SG) succeeded. Residential proxy traffic adds cost. On Apify, choosing no proxy stops the run immediately with PROXY_REQUIRED.

## Actor input object example

```json
{
  "profiles": [
    "https://www.tiktok.com/@nba"
  ],
  "resultsPerPage": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "MY"
  }
}
```

# Actor output Schema

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

No description

## `summary` (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": [
        "https://www.tiktok.com/@nba"
    ],
    "resultsPerPage": 100,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "MY"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scraper_creator/tiktok-video-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": ["https://www.tiktok.com/@nba"],
    "resultsPerPage": 100,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "MY",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("scraper_creator/tiktok-video-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": [
    "https://www.tiktok.com/@nba"
  ],
  "resultsPerPage": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "MY"
  }
}' |
apify call scraper_creator/tiktok-video-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scraper_creator/tiktok-video-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/4ZijOA4R5rxzDI9FJ/builds/RNJEJyzgOzMQOcEg8/openapi.json
