# TikTok Scraper: Videos, Profiles, Hashtags, Sounds (`fanndev/tiktok-all-in-one-scraper`) Actor

Scrape public TikTok without a login or API key: video engagement (views, likes, comments, shares) plus the uploader's follower and likes counts, account profiles, videos on a hashtag page, and videos using a sound. HTTP-only via TikTok's own embed and oEmbed surfaces - no browser.

- **URL**: https://apify.com/fanndev/tiktok-all-in-one-scraper.md
- **Developed by:** [Faisal Ahdan naufal](https://apify.com/fanndev) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 results

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: Videos, Profiles, Hashtags, Sounds

Scrape public TikTok data without a login, an account, or an API key. Five modes
in one actor, all HTTP-only — no browser, no request signing, no cookies.

| Mode | Give it | You get back |
|---|---|---|
| **video** | video url, short link, or numeric id | views, likes, comments, shares, caption, upload time, cover, sound — **plus the uploader's follower / likes / video counts** |
| **profile** | username, `@handle`, or profile url | followers, following, total likes, bio, verified, private flag, avatar |
| **hashtag** | `fyp` or `#fyp` | the videos TikTok shows on that tag's page, plus the tag's total video count |
| **music** | sound url or sound id | the videos using that sound, plus the sound's artist and total video count |
| **oembed** | full video url | TikTok's official lightweight record: title, author, thumbnail |

### Quick start

```json
{
  "mode": "video",
  "targets": [
    "https://www.tiktok.com/@complex/video/7626254334065511711",
    "https://vm.tiktok.com/ZGdxxxxxx/",
    "7626254334065511711"
  ],
  "proxyConfiguration": { "useApifyProxy": true }
}
```

Every entry form above works in `video` mode. Short links cost one extra request
to resolve; bare ids cost none.

### Sample output — `video` mode

```json
{
  "_mode": "video",
  "_source": "embed-frontity-state",
  "videoId": "7626254334065511711",
  "description": "can't even lie this is fire 🔥 [🎥: @nasa ]",
  "createTimeIso": "2026-04-08T05:21:59Z",
  "playCount": 11000,
  "diggCount": 427,
  "commentCount": 10,
  "shareCount": 14,
  "authorUniqueId": "complex",
  "authorNickname": "COMPLEX",
  "authorVerified": true,
  "authorFollowerCount": 6300000,
  "authorHeartCount": 394000000,
  "authorVideoCount": 11800,
  "musicId": "7301919715965077506",
  "musicName": "euphoria",
  "musicAuthorName": "inertia. & Aurenth",
  "locationCreated": "US",
  "isAd": false,
  "videoUrl": "https://www.tiktok.com/@complex/video/7626254334065511711"
}
```

The author block is the part worth noticing: **a list of video urls doubles as a
creator dataset**, because TikTok ships the uploader's account stats inside the
video payload. No second request per account.

### Sample output — `music` mode

```json
{
  "_mode": "music",
  "playlistType": "music",
  "entityId": "7301919715965077506",
  "musicArtist": "Aurenth",
  "entityVideoCount": 91900,
  "videoId": "7584740654534642957",
  "playCount": 10200000,
  "authorUniqueId": "coastenitaa",
  "videoUrl": "https://www.tiktok.com/@coastenitaa/video/7584740654534642957",
  "_warning": "single_page_surface_embed_playlist_is_not_paginable;playlist_rows_carry_play_count_only_no_digg_or_comment_counts"
}
```

### What this actor does **not** do, and why

This is the honest part, and it is short on purpose. TikTok's consumer web pages
are behind a WAF that answers with a 1.4 KB stub, and its internal JSON APIs
require a request signature minted by TikTok's own obfuscated JavaScript. This
actor uses only the embed and oEmbed surfaces, which are published for third-party
use. That buys reliability and a tiny cost per row, and it costs the following:

- **No comments.** `/api/comment/list/` is signature-gated. You get the comment
  *count*, not the comments.
- **No search, no trending, no For You.** Same gate.
- **No "all videos by this user".** Listing an account's posts needs the signed
  `/api/post/item_list/`. `profile` mode returns account stats; to collect a
  creator's videos you need their video urls or ids.
- **Hashtag and sound listings are one page** — about 6 and 9 videos. The embed's
  own loader is the signature-gated endpoint, so `maxItemsPerTarget` cannot
  produce more. `entityVideoCount` tells you how many exist in total so you can
  see the gap rather than guess at it.
- **No Top Ads / Creative Center.** Those APIs answer
  `{"code":40101,"msg":"no permission"}` to anonymous callers.
- **Counters are rounded by TikTok**, not by this actor: `playCount` arrives as
  `11000`, not `11043`. `videoWidth`/`videoHeight`/`videoDuration` are often
  `null` outright -- TikTok omits the `videoMeta` block on many videos and ships
  the placeholder `99` on some of the rest.

Full measurements behind each of these are in
[CRAWLING\_METHOD.md](CRAWLING_METHOD.md).

### Reading the output

Every row carries a small envelope so a mixed dataset stays readable:

- `_mode` — which surface produced the row.
- `_input` — the target as resolved from your entry.
- `_source` — `embed-frontity-state` or `oembed-api`.
- `_index` — position within a hashtag/music listing.
- `_warning` — a **ceiling**, not a failure (single-page listing, thin listing
  rows, rounded play counts, profile without a video list).
- `_error` + `_errorDetail` — `INVALID_INPUT`, `NOT_FOUND`, `PRIVATE_ACCOUNT`,
  `EMPTY_RESULT`, `BLOCKED_AFTER_RETRIES`, `UPSTREAM_SHAPE_CHANGED`.

**Every input produces at least one row.** A username that no longer exists
becomes a `NOT_FOUND` row, not a silent omission — so you can always reconcile
your input list against the dataset line for line.

### Proxies and pacing

TikTok throttles embed requests **per IP** and answers with `HTTP 503` after a
handful of calls. The actor handles this by rotating the TLS fingerprint *and* the
proxy session, then backing off (2s → 4s → 8s) across four attempts.

Rotating the fingerprint alone does not lift the throttle, so:

- **More than a few targets: use a proxy.** `{"useApifyProxy": true}` is the
  default; residential helps most.
- **`delaySeconds` defaults to 2.** Lower it only behind rotating residential
  proxies.
- A run that hits `BLOCKED_AFTER_RETRIES` on every target ends as a **failed**
  run rather than an empty success, so a throttled run never looks like "this
  target has no data".

### Tips

- **Engagement benchmarking:** run `hashtag` or `music` to discover video ids,
  then feed those ids into `video` mode for the full counter set.
- **Creator lists:** one `video` call per creator's recent post gives you their
  follower count, total likes and post count in the same row.
- **Bulk url validation:** `oembed` mode is 1.3 KB per target against 302 KB for
  the embed page — use it to check which of thousands of urls are still live
  before spending on the heavier mode.
- **Sound trend tracking:** `entityVideoCount` on a sound is the total number of
  videos using it. Re-run on a schedule and diff that number.

### Local development

```bash
pip install -r requirements.txt
python test_local.py            # all five modes against live TikTok
python test_local.py video      # one mode
```

The test asserts that required fields per mode are **non-null** — a mode that
returns a row full of nulls fails rather than passing on row count alone.

# Actor input Schema

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

Which TikTok surface to scrape. 'video' is the richest: views, likes, comments, shares plus the author's follower/heart/video counts and the sound used. 'profile' returns account stats only (the embed publishes no video list). 'hashtag' and 'music' return the videos TikTok currently shows on those pages - one page of roughly 6 and 9 videos respectively, which is all the embed ships. 'oembed' is TikTok's official lightweight record for a video url: title, author and thumbnail, no engagement counters.

## `targets` (type: `array`):

What to scrape, matched to the mode. For video: video or photo urls, vm.tiktok.com / vt.tiktok.com short links, or bare numeric video ids. For profile: usernames, @handles or profile urls (e.g. "nasa", "@nasa", "https://www.tiktok.com/@nasa"). For hashtag: tags with or without the # (e.g. "fyp"). For music: a music url - its slug ends in the id - or the bare music id (e.g. "7301919715965077506"). For oembed: full video urls only, since TikTok's oEmbed endpoint rejects bare ids.

## `maxItemsPerTarget` (type: `integer`):

Cap on rows per target for the hashtag and music modes. Leave at 0 for everything the embed shipped, which is about 6 videos for a hashtag and 9 for a sound - TikTok's embed loader is signature-gated, so there is no deeper page to request and a higher value cannot produce more. Ignored by the video, profile and oembed modes, which always produce exactly one row per target.

## `delaySeconds` (type: `integer`):

Seconds to wait between targets. TikTok answers repeated embed requests from one IP with HTTP 503 after a handful of calls, so pacing keeps a run alive far longer than hammering does. Lower it only when running through rotating residential proxies.

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

Strongly recommended for more than a few targets. TikTok's embed throttle is IP-bound, and this actor rotates both the egress IP and the TLS fingerprint when it hits a 503 - without a proxy only the fingerprint can change, which on its own does not lift the throttle.

## Actor input object example

```json
{
  "mode": "video",
  "targets": [
    "https://www.tiktok.com/@complex/video/7626254334065511711"
  ],
  "maxItemsPerTarget": 0,
  "delaySeconds": 2,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Scraped videos, profiles, hashtag listings or sound listings.

# 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 = {
    "targets": [
        "https://www.tiktok.com/@complex/video/7626254334065511711"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fanndev/tiktok-all-in-one-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 = { "targets": ["https://www.tiktok.com/@complex/video/7626254334065511711"] }

# Run the Actor and wait for it to finish
run = client.actor("fanndev/tiktok-all-in-one-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 '{
  "targets": [
    "https://www.tiktok.com/@complex/video/7626254334065511711"
  ]
}' |
apify call fanndev/tiktok-all-in-one-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fanndev/tiktok-all-in-one-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/QSavEhPVQqxP6FBjF/builds/5nYTITWvpnqCpQlMj/openapi.json
