# TikTok Scraper — Videos, Profiles & Hashtags | from $3/1K (`bovi/tiktok-scraper`) Actor

Scrape TikTok profiles, videos, hashtag feeds, keyword search, comments, and TikTok Shop products without login. Residential proxy built-in — TikTok blocks datacenter IPs, most competitors fail silently. Probe-first stop-gate: zero charges if your run can't reach TikTok.

- **URL**: https://apify.com/bovi/tiktok-scraper.md
- **Developed by:** [Vitalii Bondarev](https://apify.com/bovi) (community)
- **Categories:** Social media, Marketing
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.91 / 1,000 profile items

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?

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

Scrape TikTok at scale without a login. Six modes cover the full TikTok data surface: user profiles, video feeds, hashtag trending, keyword search, video comments, and TikTok Shop products. Residential proxy is built in — TikTok blocks datacenter IPs at the edge, so competitors that skip this fail in production.

Pay per result.

***

### Why this actor beats the incumbents

| What breaks most TikTok actors | How this actor handles it |
|---|---|
| Datacenter IP blocks | Forces Apify RESIDENTIAL proxy; CORE probe validates on startup |
| Signer rot (X-Bogus re-obfuscated every 2–4 weeks) | Graceful degradation: CORE modes keep working when signer breaks; actor logs exact advice |
| Cold-start failures silently producing empty datasets | Probe-first stop-gate: actor aborts with actionable error before wasting your compute |
| DOM class-name scraping that breaks on every redesign | Rehydration JSON extraction (structural, not CSS-based) for CORE modes |
| No geo-validation | Cloud run on residential IP validates real-world reachability (TikTok geo-blocks certain countries) |

***

### 6 modes

#### CORE modes — no request signing, most stable

| Mode | Input field | What you get |
|---|---|---|
| `profile` | `usernames` | Follower/following/like/video counts, bio, verified flag, avatar |
| `product` | `productIds` | TikTok Shop PDP: title, price range, rating, sold count, images |
| `user_videos` | `usernames` | First page of a user's videos from rehydration JSON; full pagination if signer is healthy |

CORE modes parse TikTok's **server-side rehydration JSON** embedded in the page HTML. No X-Bogus signature required. These modes remain functional even when the signer breaks.

#### SIGNED modes — require X-Bogus signature

| Mode | Input field | What you get |
|---|---|---|
| `hashtag` | `hashtags` | Videos trending under a hashtag, ranked by TikTok's feed |
| `search` | `keywords` | Keyword search results with ranking position |
| `comments` | `videoIds` | Comments on a specific video, including reply thread info |

SIGNED modes call TikTok's internal API endpoints and require a valid X-Bogus HMAC signature. TikTok re-obfuscates the signing algorithm every **2–4 weeks**.

> **Signer maintenance note:** When TikTok rotates the algorithm, SIGNED modes will return a `signed_probe_failed` error. CORE modes continue working. See `src/signer.py` for the refresh procedure. The actor's probe-first gate tells you exactly which modes are affected before any records are charged.

***

### Output fields

#### Profile record (`mode=profile`)

| Field | Description |
|---|---|
| `user_id` | TikTok internal user ID |
| `sec_uid` | Encrypted user identifier (used for API pagination) |
| `username` | Handle (e.g. `charlidamelio`) |
| `nickname` | Display name |
| `verified` | Blue-check verified account |
| `follower_count` | Follower count |
| `following_count` | Following count |
| `heart_count` | Total likes received across all videos |
| `video_count` | Public video count |
| `bio` | Profile bio text |
| `region` | Account region code |
| `profile_url` | `https://www.tiktok.com/@{username}` |
| `avatar_url` | Avatar image URL |

#### Video record (`mode=user_videos`)

| Field | Description |
|---|---|
| `video_id` | TikTok video ID |
| `author_username` | Uploader handle |
| `description` | Video caption (may include hashtags inline) |
| `hashtags` | Extracted hashtag list |
| `duration` | Duration in seconds |
| `play_count` | View count |
| `like_count` | Like count |
| `comment_count` | Comment count |
| `share_count` | Share count |
| `collect_count` | Save/bookmark count |
| `created_at` | ISO-8601 UTC post time |
| `video_url` | `https://www.tiktok.com/@{author}/video/{id}` |
| `thumbnail_url` | Cover image URL |
| `music_title` | Background music title |
| `music_author` | Background music artist |
| `is_ad` | True if TikTok marked it as an ad |

#### Comment record (`mode=comments`)

| Field | Description |
|---|---|
| `comment_id` | Comment ID |
| `video_id` | Parent video ID |
| `author_username` | Commenter handle |
| `text` | Comment text |
| `like_count` | Comment likes |
| `reply_count` | Replies to this comment |
| `is_reply` | True if this is a reply to another comment |
| `parent_comment_id` | ID of parent comment (replies only) |
| `created_at` | ISO-8601 UTC post time |

#### TikTok Shop product record (`mode=product`)

| Field | Description |
|---|---|
| `product_id` | TikTok Shop product ID |
| `shop_id` | Seller shop ID |
| `title` | Product title |
| `description` | Product description |
| `category` | Product category |
| `brand` | Brand name |
| `price_min` | Lowest SKU price (USD float) |
| `price_max` | Highest SKU price (USD float) |
| `currency` | Price currency code (default: USD) |
| `discount_pct` | Discount percentage, if active |
| `rating` | Seller rating (0–5) |
| `review_count` | Number of reviews |
| `sold_count` | Units sold count |
| `image_urls` | List of product image URLs |
| `product_url` | `https://www.tiktok.com/shop/pdp/{id}` |

#### Search / hashtag result record (`mode=search` or `mode=hashtag`)

Same video fields as above, plus:

| Field | Description |
|---|---|
| `query` | The search keyword or hashtag that produced this result |
| `rank` | 1-based position in the result set |

***

### All records include

| Field | Description |
|---|---|
| `parse_confidence` | 0.0–1.0 parse quality score. 1.0 = all required fields found |
| `warnings` | List of warning codes (empty = clean record) |
| `scraped_at` | ISO-8601 UTC timestamp of the scrape run |

***

### How to use

#### Scrape a user profile

```json
{
  "mode": "profile",
  "usernames": ["charlidamelio", "khaby.lame"],
  "maxResults": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

#### Scrape a user's recent videos

```json
{
  "mode": "user_videos",
  "usernames": ["mrbeast"],
  "maxResults": 50
}
```

#### Search TikTok by keyword

```json
{
  "mode": "search",
  "keywords": ["viral recipe", "skincare routine"],
  "maxResults": 100
}
```

#### Scrape a hashtag feed

```json
{
  "mode": "hashtag",
  "hashtags": ["fyp", "foodtok"],
  "maxResults": 200
}
```

#### Scrape video comments

```json
{
  "mode": "comments",
  "videoIds": ["7123456789012345678"],
  "maxResults": 500
}
```

#### Scrape a TikTok Shop product

```json
{
  "mode": "product",
  "productIds": ["1729386234567890"],
  "maxResults": 10
}
```

***

### Pricing

**Pay-per-result** (PPE) — you pay only for records successfully pushed to the dataset. Failed lookups, probe requests, and skipped items are never charged.

PPE event: `tiktok-result`

***

### Reliability and proxy

TikTok performs aggressive geo-filtering and bot detection. Key facts:

- **Datacenter IPs are blocked instantly.** The actor defaults to `RESIDENTIAL` proxy and runs a connectivity probe before scraping anything.
- **Cloud geo-blocks are real.** Some Apify cloud regions are blocked by TikTok. The startup CORE probe validates that your run's residential IP can actually reach TikTok — if it can't, the actor exits with a clear error and zero charges.
- **Signer rot.** TikTok re-obfuscates the X-Bogus signing algorithm every 2–4 weeks. When this happens, SIGNED modes (`hashtag`, `search`, `comments`) return a `signed_probe_failed` error. CORE modes (`profile`, `product`, `user_videos` first page) continue working with no intervention. Monitor actor logs for `SIGNED probe: FAIL` to know when a signer update is needed.

***

*Not affiliated with TikTok or ByteDance. Data is retrieved from publicly accessible TikTok endpoints.*

### Integrations

Built for social-media analysts and content-intelligence teams pulling TikTok profiles, video feeds, hashtags, and comments at scale — the JSON/dataset output drops into the tools you already run, no glue code:

- **n8n / Make / Zapier** — trigger a run or pipe every new dataset item into 500+ apps (Google Sheets, Airtable, Slack, HubSpot, your database) with no code: [n8n](https://docs.apify.com/platform/integrations/n8n), [Make](https://docs.apify.com/platform/integrations/make), [Zapier](https://docs.apify.com/platform/integrations/zapier).
- **Webhooks** — fire your own endpoint the moment a run finishes, to push results straight into your pipeline ([docs](https://docs.apify.com/platform/integrations/webhooks)).
- **MCP server** — expose this actor as a tool to Claude, Cursor, or any [MCP client](https://mcp.apify.com) so an AI agent can pull this data mid-conversation ([guide](https://blog.apify.com/how-to-use-mcp/)).
- **API & SDKs** — fetch the dataset as JSON, CSV, or Excel through the Apify REST API or the Python / JS SDKs.

See all [Apify integrations](https://apify.com/integrations).

# Actor input Schema

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

Scraping mode. product=TikTok Shop PDP (no signing). profile=user profile info (no signing). user\_videos=a user's video feed (first page no signing; full pagination requires signer). hashtag=videos under a hashtag (requires signer). search=keyword search results (requires signer). comments=video comments (requires signer).

## `usernames` (type: `array`):

TikTok usernames to scrape. Used by modes: profile, user\_videos. Include or omit the leading '@' — both are accepted (e.g. 'charlidamelio' or '@charlidamelio').

## `productIds` (type: `array`):

TikTok Shop product IDs or full product URLs. Used by mode: product. Accepts bare IDs (e.g. '1729386234567890') or full URLs (e.g. 'https://www.tiktok.com/shop/pdp/1729386234567890').

## `videoIds` (type: `array`):

TikTok video IDs to fetch comments for. Used by mode: comments. Accepts bare numeric IDs (e.g. '7123456789012345678').

## `keywords` (type: `array`):

Keywords or phrases to search on TikTok. Used by mode: search. Each keyword runs as a separate search and returns up to maxResults videos.

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

Hashtags to scrape videos from. Used by mode: hashtag. Include or omit the leading '#' — both are accepted (e.g. 'fyp' or '#fyp').

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

Maximum total records to push to the dataset across all inputs. 0 = no limit. Applies per run, not per username/keyword/hashtag. Default: 50.

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

Apify proxy settings. RESIDENTIAL proxy is mandatory — TikTok instantly blocks datacenter IPs. Default uses Apify RESIDENTIAL group. Do not disable unless testing locally with your own residential proxy.

## Actor input object example

```json
{
  "mode": "user_videos",
  "usernames": [
    "charlidamelio",
    "khaby.lame"
  ],
  "productIds": [],
  "videoIds": [],
  "keywords": [],
  "hashtags": [],
  "maxResults": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset containing Tiktok Scraper records (username, nickname, verified, follower\_count, following\_count, heart\_count, video\_count, bio, region, profile\_url, parse\_confidence, scraped\_at, author\_username, description, duration, play\_count, like\_count, comment\_count).

# 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 = {
    "mode": "user_videos",
    "usernames": [
        "charlidamelio",
        "khaby.lame"
    ],
    "productIds": [],
    "videoIds": [],
    "keywords": [],
    "hashtags": [],
    "maxResults": 50,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("bovi/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 = {
    "mode": "user_videos",
    "usernames": [
        "charlidamelio",
        "khaby.lame",
    ],
    "productIds": [],
    "videoIds": [],
    "keywords": [],
    "hashtags": [],
    "maxResults": 50,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("bovi/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 '{
  "mode": "user_videos",
  "usernames": [
    "charlidamelio",
    "khaby.lame"
  ],
  "productIds": [],
  "videoIds": [],
  "keywords": [],
  "hashtags": [],
  "maxResults": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call bovi/tiktok-scraper --silent --output-dataset

```

## MCP server setup

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