# TikTok Data Scraper — Video, Hashtag & Sound Performance (`simpleapi/tiktok-data-scraper`) Actor

🎬 TikTok Data Scraper extracts high-value insights from TikTok—captions, hashtags, engagement stats, creators & trends. 🚀 Perfect for market research, influencer discovery, and content strategy. ⚡ Fast, reliable, and automation-friendly.

- **URL**: https://apify.com/simpleapi/tiktok-data-scraper.md
- **Developed by:** [SimpleAPI](https://apify.com/simpleapi) (community)
- **Categories:** Social media, Lead generation, Automation
- **Stats:** 3 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Data Scraper — Video, Hashtag & Sound Performance

TikTok Data Scraper — Video, Hashtag & Sound Performance pulls public TikTok videos, hashtag feeds, sound/music feeds, playlists, profiles, comments and replies, then diffs each run against your last one to compute real `playCountDelta`, `diggCountDelta`, `commentCountDelta`, `viewVelocity` and an honestly-derived `trendingNow` flag. Every record ships as structured JSON with `play_count`, `digg_count`, `comment_count`, an HD no-watermark link and a canonical deep-link URL. Social media managers, marketing agencies and trend researchers use it to watch one specific video, hashtag or sound gain — or lose — traction over multiple runs instead of capturing a single static snapshot. Point it at any public username, hashtag, sound or playlist and start tracking.

***

### 📱 What is TikTok Data Scraper — Video, Hashtag & Sound Performance?

It's a TikTok data scraper that collects public content across six starting points — profiles, videos, hashtags, sounds, playlists and single video IDs — and layers cross-run growth analytics on top of the same content. No TikTok account, login, cookies or app review is required: every category reads data TikTok already serves to a logged-out visitor.

**Key features:**

- 🔎 **Discovery by profile, hashtag, sound, playlist or direct video ID** — accepts full URLs, `@handles`, plain usernames, numeric user IDs, hashtag names or numeric music/playlist IDs
- 📹 **Core identifying fields** — `video_id`, `title`/`content_desc`, `author.unique_id`/`author.nickname`, `duration`, and an HD no-watermark download link (`hdplay`)
- 📈 **Cross-run performance data** — `playCountDelta`, `diggCountDelta`, `commentCountDelta`, `viewVelocity` (plays/hour since your last run) and `trendingNow` per video, plus real aggregate deltas for hashtags and sounds
- 🌍 **Geographic and temporal fields** — `region`, `hashtagRegion` localization, ISO `publishedAt` (from TikTok's own `create_time`) and `scrapedAt` stamps
- 🎚️ **Real filters** — per-category content-type selection, per-source result caps or unlimited "scrape all", replies on/off, named watchlists, and a baseline-reset toggle
- 📤 **Export formats** — standard Apify dataset exports (JSON, CSV, Excel, HTML table, RSS) from the Output tab, no extra conversion step

***

### 📊 What data can I extract with TikTok Data Scraper?

The Actor returns one JSON row per video, comment, reply, follower/following entry, profile lookup or growth summary — every row shares a common envelope plus a category-specific `data` object.

| Field | Example value | Use case |
| --- | --- | --- |
| `section` | `"hashtags"` | which collector produced the row (users / hashtags / music / playlists / videos / search) |
| `category` | `"videos"` | the record type within that section |
| `data.video_id` | `"7647758735229635871"` | unique video ID for joins and de-duplication |
| `data.title` / `data.content_desc` | `"POV: when the beat drops"` | caption text for content analysis |
| `data.author.unique_id` | `"creator"` | link a video back to the posting account |
| `data.author.nickname` | `"Creator"` | display name for reporting |
| `data.play_count` | `13686605` | current view count |
| `data.digg_count` | `2579867` | current like count |
| `data.comment_count` | `34525` | current comment count |
| `data.share_count` | `88210` | current share count |
| `data.download_count` | `4210` | current download count |
| `data.duration` | `27` | video length in seconds |
| `data.hdplay` | `"https://.../hd-nowm.mp4"` | direct HD, no-watermark download link |
| `data.music_info.title` | `"original sound"` | the sound used on the video |
| `data.playCountDelta` | `128400` | view growth since your last tracked run |
| `data.viewVelocity` | `1780.5` | plays gained per hour since the last run |
| `data.trendingNow` | `true` | honest flag — only true when plays genuinely grew |
| `publishedAt` | `"2026-07-15T09:12:44Z"` | when the video was posted, from TikTok's own timestamp |
| `scrapedAt` | `"2026-08-01T14:05:11Z"` | when this row was collected |
| `videoUrl` | `"https://www.tiktok.com/@creator/video/7647758735229635871"` | canonical deep link back to the video |

#### 📈 Performance & growth data

Every video record carries `playCountDelta`, `diggCountDelta`, `commentCountDelta`, `growthDeltaAvailable`, `viewVelocity` and `trendingNow`, diffed against a snapshot from your previous run under the same `watchlistName`. Tracked hashtags additionally emit one `hashtag_growth` summary row per hashtag with the hashtag's own real aggregate counters — `view_count`/`user_count` and their deltas `hashtagViewCountDelta`/`hashtagUserCountDelta` — never borrowed from a video's metric names. Tracked sounds emit one `music_growth` row with `video_count` and `musicVideoCountDelta`. On the first run for any entity these delta and velocity fields are `null`/`false`, not a faked zero, because there is nothing to compare against yet; run again later with the same watchlist name to get real numbers.

#### 🎯 Targeting & scope data

`categories` picks which content types to pull per profile (`videos`, `favourite_videos`, `followers`, `following`, `search`, `user_info`); `hashtagRegion` (a two-letter code) localizes hashtag feeds; `watchlistName` scopes a tracking group so unrelated trackers sharing the Actor don't collide; `resetBaseline` clears a watchlist's stored snapshots and starts fresh. Each content source has its own result cap (`maxUserResults`, `commentsPerUrl`, `videosPerHashtag`, `videosPerMusic`, `videosPerPlaylist`) or a matching "scrape all" boolean, and `repliesPerComment` controls whether — and how many — nested replies come back per comment.

***

### 🔍 How does this differ from TikTok's official API?

TikTok does publish official APIs, but neither is built for pulling public data about accounts, hashtags or sounds you don't control. The Display API only returns data for the single user who has logged in through Login Kit's OAuth flow — it cannot fetch another account's videos, a hashtag feed or a sound's usage — and the Research API is free but gated to accredited non-profit academic researchers in eligible regions (initially the US, Europe and Brazil) who pass a manual application with roughly a four-week review, per TikTok's own developer documentation (checked 2026-08-04).

| Feature | TikTok Official APIs | This Actor |
| --- | --- | --- |
| Access to arbitrary public accounts/hashtags/sounds | ❌ Display API is scoped to the logged-in user only | ✅ any public profile, hashtag, sound or playlist |
| Who can even apply | Research API: accredited academic/non-profit researchers, eligible regions only | ✅ anyone with an Apify account |
| Setup | OAuth app registration + redirect URI, or a multi-week research-access application | Fill in the input form and click Start |
| Cross-run growth tracking | Not offered | `playCountDelta`/`viewVelocity`/`trendingNow` built in |
| Output shape | Provider-specific per API | One consistent JSON schema across every category |
| Commercial/marketing use | Research API's terms restrict use to approved research | No such restriction on the data returned |

#### 🚧 Why can't I just use TikTok's own API for arbitrary accounts?

Because neither official surface is designed for it. The Display API's whole model is OAuth consent — it hands back data for the person who just logged into your app, never a third party's profile, hashtag or sound. The Research API does reach public content, but access is restricted to vetted academic institutions in a limited set of regions and requires a documented research proposal and ethics review before TikTok grants a token — not a fit for a marketer who just needs this week's hashtag numbers. This Actor covers that gap by reading the same public pages a logged-out visitor sees, for any username, hashtag, sound or playlist, with no application process.

Use TikTok's official APIs when you're building a consumer app your own users log into, or when you qualify as an approved academic researcher needing TikTok's research-grade data license. Use this Actor when you need structured data about public videos, hashtags, sounds or profiles you don't control, on your own schedule.

***

### 💼 How to use data extracted from TikTok

#### 📱 Social media & marketing teams

Track a campaign hashtag or a branded sound across every run: set `hashtags: ["yourcampaign"]` or `musicUrls: ["<sound id>"]`, keep `trackGrowth` on and reuse the same `watchlistName` on every run. Each pass returns fresh `hashtagViewCountDelta`/`musicVideoCountDelta` on the summary row and per-video `playCountDelta`/`viewVelocity` on every clip using the tag or sound, so a weekly schedule turns into a real growth chart instead of a single count.

#### 🏢 Agencies monitoring client accounts

Agencies running the same watchlist across several client profiles or competitor accounts get one dataset per run with `userUrls` set to each handle and `categories: ["videos"]`. Because `data.author.unique_id`, `play_count`, `digg_count` and `playCountDelta` all come back on every row, a single export lets you rank multiple clients or competitors by growth in one sheet, without opening TikTok manually for each one.

#### 🔬 Market research and trend intelligence

Researchers spotting emerging sounds or hashtags before they peak use `trendingNow` and `viewVelocity` on `hashtag_growth`/`music_growth` rows and on individual videos: query a shortlist of candidate hashtags or sounds on a recurring schedule and filter for `trendingNow: true` plus a rising `viewVelocity` to flag genuinely accelerating content rather than content that is simply large.

#### 🤖 AI agents and automated pipelines

Because every field is typed JSON with a stable envelope (`section`, `category`, `data`), agent frameworks can call this Actor as a tool to answer questions like "is this hashtag still trending" or "how did this video perform since Monday" by diffing `playCountDelta`/`trendingNow` across two dataset runs, without writing a TikTok parser of their own.

***

### ⬇️ Input sample

| Parameter | Required | Type | Description | Example value |
| --- | --- | --- | --- | --- |
| `userUrls` | No | array | Profile URLs, `@handles`, usernames or numeric user IDs | `["https://www.tiktok.com/@taylorswift"]` |
| `categories` | No | array | Content to collect per profile: `videos`, `favourite_videos`, `followers`, `following`, `search`, `user_info` (default `["videos"]`) | `["videos", "user_info"]` |
| `maxUserResults` | No | integer | Max items per profile/category, minimum `1`, default `10` | `50` |
| `scrapeAllUserResults` | No | boolean | Ignore the cap and collect everything available (default `false`) | `false` |
| `videoUrlsComments` | No | array | Video URLs or IDs to scrape comments from | `["https://www.tiktok.com/@user/video/7300000000000000000"]` |
| `commentsPerUrl` | No | integer | Max comments per video, minimum `1`, default `10` | `100` |
| `scrapeAllComments` | No | boolean | Collect every available comment per video (default `false`) | `false` |
| `repliesPerComment` | No | integer | Max replies per comment, minimum `0` (`0` skips replies), default `0` | `5` |
| `scrapeAllReplies` | No | boolean | Collect every available reply per comment (default `false`) | `false` |
| `hashtags` | No | array | Hashtag names or URLs | `["dance"]` |
| `videosPerHashtag` | No | integer | Max videos per hashtag, minimum `1`, default `10` | `50` |
| `scrapeAllHashtagVideos` | No | boolean | Collect every available hashtag video (default `false`) | `false` |
| `hashtagRegion` | No | string | Two-letter country code, default `"US"` (pattern `^[A-Za-z]{2}$`) | `"GB"` |
| `musicUrls` | No | array | Music/sound URLs or IDs | `["7123456789012345678"]` |
| `videosPerMusic` | No | integer | Max videos per music track, minimum `1`, default `10` | `50` |
| `scrapeAllMusicVideos` | No | boolean | Collect every available video for the sound (default `false`) | `false` |
| `playlistIDs` | No | array | Numeric playlist (mix) IDs | `["7100000000000000000"]` |
| `videosPerPlaylist` | No | integer | Max videos per playlist, minimum `1`, default `10` | `50` |
| `scrapeAllPlaylistVideos` | No | boolean | Collect every available playlist video (default `false`) | `false` |
| `videoDetailsList` | No | array | Video IDs or URLs to fetch full detail records for | `["7647758735229635871"]` |
| `trackGrowth` | No | boolean | Compute cross-run deltas/velocity, default `true` | `true` |
| `watchlistName` | No | string | Groups this run's growth snapshots, default `"default"` | `"campaign-launch-2026"` |
| `resetBaseline` | No | boolean | Clear stored snapshots for this watchlist and start fresh, default `false` | `false` |
| `minRequestInterval` | No | number | Seconds between requests, min `0`, max `10`, default `1.2` | `1.2` |
| `maxRetries` | No | integer | Retries per request before giving up, min `0`, max `10`, default `3` | `3` |
| `apiKey` | No | string | Optional premium key from the data source that lifts its rate limit, default `""` | `""` |
| `proxyConfiguration` | No | object | Proxy settings, default `{"useApifyProxy": false}` (direct connection, auto-escalates on block) | `{"useApifyProxy": false}` |

```json
{
  "hashtags": ["dance"],
  "videosPerHashtag": 50,
  "hashtagRegion": "US",
  "trackGrowth": true,
  "watchlistName": "dance-campaign-2026",
  "minRequestInterval": 1.2,
  "maxRetries": 3,
  "proxyConfiguration": { "useApifyProxy": false }
}
```

**Common pitfall:** the schema enforces `minimum: 1` on the per-source caps (`maxUserResults`, `commentsPerUrl`, `videosPerHashtag`, `videosPerMusic`, `videosPerPlaylist`), but each field's own description text says "Minimum 40" — that text is not enforced in the current build. Passing `10` genuinely returns up to 10 items; passing `0` or a negative number falls back to `10` rather than erroring. Also note `hashtagRegion` must match a strict two-letter pattern (`US`, not `USA`) or the run fails validation before it starts.

***

### ⬆️ Output sample

Results are typed, normalized JSON pushed live to the dataset as soon as each item is collected — a run that stops partway through still keeps everything already saved. Export from the Output tab as JSON, CSV, Excel, HTML table or RSS.

```json
{
  "section": "hashtags",
  "category": "videos",
  "input": "dance",
  "user": "dance",
  "userType": "hashtag",
  "publishedAt": "2026-07-15T09:12:44Z",
  "scrapedAt": "2026-08-01T14:05:11Z",
  "hashtagUrl": "https://www.tiktok.com/hashtag/dance",
  "videoUrl": "https://www.tiktok.com/@creator/video/7647758735229635871",
  "profileUrl": "https://www.tiktok.com/@creator",
  "data": {
    "video_id": "7647758735229635871",
    "title": "POV: when the beat drops",
    "region": "US",
    "duration": 27,
    "play_count": 13686605,
    "digg_count": 2579867,
    "comment_count": 34525,
    "share_count": 88210,
    "download_count": 4210,
    "collect_count": 51200,
    "create_time": 1752570764,
    "hdplay": "https://tiktokcdn.example/hd-nowm.mp4",
    "music_info": { "title": "original sound", "author": "creator" },
    "author": { "unique_id": "creator", "nickname": "Creator" },
    "playCountDelta": 128400,
    "diggCountDelta": 5100,
    "commentCountDelta": 220,
    "growthDeltaAvailable": true,
    "previousRunAt": "2026-07-25T09:00:02Z",
    "viewVelocity": 1780.5,
    "trendingNow": true
  }
}
```

A tracked hashtag also emits one `hashtag_growth` summary row (`hashtag_id`, `hashtag_name`, `view_count`, `user_count`, `hashtagViewCountDelta`, `hashtagUserCountDelta`), and a tracked sound emits one `music_growth` row (`music_id`, `title`, `author`, `video_count`, `musicVideoCountDelta`) — both null/false on their first run, honestly, until a second run gives them something to diff against.

<details><summary>Every field the Actor can write, by record type</summary>

**Envelope (every record):** `section`, `category`, `input`, `user`, `userType`, `publishedAt`, `scrapedAt`, `data`, plus whichever deep links apply — `videoUrl`, `profileUrl`, `hashtagUrl`, `musicUrl`, `playlistUrl`.

**`data` on video-type rows** (`videos`, `favourite_videos`, `search`, hashtag/music/playlist videos, `video_detail`): `aweme_id`, `video_id`, `region`, `title`, `content_desc`, `cover`, `ai_dynamic_cover`, `origin_cover`, `duration`, `play`, `wmplay`, `hdplay`, `size`, `wm_size`, `hd_size`, `music`, `music_info`, `play_count`, `digg_count`, `comment_count`, `share_count`, `download_count`, `collect_count`, `create_time`, `anchors`, `anchors_extras`, `is_ad`, `commerce_info`, `commercial_video_info`, `item_comment_settings`, `mentioned_users`, `author`, `is_nff_or_nr`, `is_top`, plus growth fields `growthDeltaAvailable`, `previousRunAt`, `playCountDelta`, `diggCountDelta`, `commentCountDelta`, `viewVelocity`, `trendingNow`.

**`data` on `hashtag_growth` rows:** `hashtag_id`, `hashtag_name`, `view_count`, `user_count`, `growthDeltaAvailable`, `previousRunAt`, `hashtagViewCountDelta`, `hashtagUserCountDelta`.

**`data` on `music_growth` rows:** `music_id`, `title`, `author`, `video_count`, `growthDeltaAvailable`, `previousRunAt`, `musicVideoCountDelta`.

**`data` on `comments`/`replies` rows:** passed through unmodified from the comment API, so the exact key set can vary; commonly includes `id`, `text`, `digg_count`, `reply_total`, `create_time`, `user.nickname`, `user.unique_id`, `user.signature`, `user.region`, `user.verified`, `user.follower_count`, and `reply_to_comment_id` on reply rows.

**`data` on `user_info` rows:** passed through unmodified from the profile API; commonly includes `user.id`, `user.uniqueId`, `user.nickname`, `user.signature`, `user.bioLink.link`, `user.ins_id`, `user.twitter_id`, `user.youtube_channel_id`, `stats.followerCount`, `stats.followingCount`, `stats.heartCount`, `stats.videoCount`.

**`data` on `followers`/`following` rows:** passed through unmodified; commonly includes `id`, `unique_id`, `nickname`, `avatar`.

</details>

***

### 🎯 How do you filter and target specific videos, hashtags and sounds?

**Category vs. exact reference.** For profiles, `categories` is a fixed select — `videos`, `favourite_videos`, `followers`, `following`, `search`, `user_info` — while `search` inside that list takes a free-text query. Hashtags, sounds and playlists are looked up by exact name or numeric ID, not fuzzy search, so `hashtags: ["dance"]` matches the hashtag page exactly rather than anything containing "dance".

**Scope precision.** `hashtagRegion` narrows a hashtag feed to a specific two-letter market. `watchlistName` scopes an entire tracking group so you can run several independent trackers — one per campaign, client or sound — from the same Actor without their growth snapshots colliding. `resetBaseline` intentionally throws away a watchlist's stored counts and restarts it from this run.

**The closest thing to a quality threshold** is `repliesPerComment`: leave it at `0` to pull comments without their reply noise, or raise it (or set `scrapeAllReplies`) once you actually need the thread.

**Volume controls.** Every content source has its own numeric cap (`maxUserResults`, `commentsPerUrl`, `videosPerHashtag`, `videosPerMusic`, `videosPerPlaylist`) or a matching `scrapeAll*` boolean for unlimited collection; pagination against TikTok's own cursors is handled internally, so you never pass a page number yourself.

```json
{ "hashtags": ["dance"], "videosPerHashtag": 50, "hashtagRegion": "GB", "trackGrowth": true, "watchlistName": "uk-dance-tracker" }
```

```json
{ "musicUrls": ["7123456789012345678"], "scrapeAllMusicVideos": true, "trackGrowth": true, "watchlistName": "sound-audit" }
```

```json
{ "videoUrlsComments": ["https://www.tiktok.com/@creator/video/7647758735229635871"], "commentsPerUrl": 200, "repliesPerComment": 5 }
```

***

### ▶️ Want to try other TikTok scrapers?

| Scraper | What it extracts |
| --- | --- |
| [TikTok Shop Data Scraper — Reviews & Sentiment Analysis](https://apify.com) | TikTok Shop product listings, reviews and sentiment scoring |
| [TikTok Trending Creator Insights Scraper](https://apify.com) | Trending creator profiles with per-video stats |

| Comparable social-platform scrapers | What it extracts |
| --- | --- |
| [X (Twitter) User Profile & Tweets Scraper](https://apify.com) | Tweets and profile data by user or place |
| [Twitter (X) Tweets Scraper by Keyword & Hashtag](https://apify.com) | Tweets by keyword or hashtag search |
| [Threads Search Post Scraper](https://apify.com) | Threads posts by search term |
| [Pinterest Profile Scraper](https://apify.com) | Pinterest profile data with email/website contacts |

***

### 🔌 How to extract TikTok data programmatically

Every run is a standard Apify Actor call — start it with your API token, poll the run, then read the dataset back as JSON. No separate signup or API key for TikTok itself is required.

#### 🐍 Python example

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_API_TOKEN>")

run = client.actor("<ACTOR_ID>").call(run_input={
    "hashtags": ["dance"],
    "videosPerHashtag": 50,
    "hashtagRegion": "US",
    "trackGrowth": True,
    "watchlistName": "dance-campaign-2026",
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    data = item.get("data", {})
    print(data.get("video_id"), data.get("play_count"), data.get("playCountDelta"))
```

#### 📤 Export to spreadsheets or CRM

From the Output tab, export directly to CSV or Excel and map `data.author.unique_id`, `data.play_count`, `data.playCountDelta` and `videoUrl` to your usual creator/performance columns — no reshaping needed since every video row already shares the same flat `data` structure.

***

### ⚖️ Is it legal to scrape TikTok?

Scraping publicly accessible TikTok pages — videos, hashtag feeds, sound feeds, profiles and public comments — that require no login is generally lawful in most jurisdictions, and courts have repeatedly held that public web data is not protected merely by being online. That said, the records this Actor returns include personal data — usernames, nicknames, bios, follower counts and commenters' regions and verification status — so GDPR (if you or the data subjects are in the EU/UK) and CCPA (for California residents) can apply to how you store and use it, separately from whether the initial collection itself was lawful. You are responsible for having a lawful basis for any storage or downstream use of personal data, and for complying with TikTok's own Terms of Service for your use case. Consult legal counsel for commercial applications involving bulk storage of personal data.

***

### ❓ FAQ

#### Does this Actor tell me when a video's data is out of date?

Yes — every row carries a `scrapedAt` timestamp showing exactly when it was collected, and `growthDeltaAvailable`/`previousRunAt` tell you whether a comparison baseline exists yet for that video, hashtag or sound.

#### Can I get hashtag and sound performance data along with video records?

Yes. A tracked hashtag emits its own `hashtag_growth` summary row (`view_count`, `user_count` and their deltas) alongside every individual video row from that hashtag, and a tracked sound emits a matching `music_growth` row (`video_count` and its delta).

#### How accurate are the play/like/comment counts?

The Actor returns counts exactly as TikTok reports them at request time. Accuracy after that depends on how quickly the underlying video's stats change; re-run periodically with the same `watchlistName` if you need a validated trend rather than a single point-in-time count.

#### How many records can I get per run?

There is no Actor-side hard cap when a `scrapeAll*` toggle is on — collection continues until TikTok's own feed for that source is exhausted. Without it, the relevant numeric field (`maxUserResults`, `commentsPerUrl`, `videosPerHashtag`, `videosPerMusic`, `videosPerPlaylist`) sets the limit, defaulting to `10`. Every queued profile/hashtag/sound/playlist/video is currently processed one at a time rather than in parallel, so a run with many inputs takes proportionally longer.

#### How does watchlist targeting work?

`watchlistName` groups this run's growth snapshots under one label. Reuse the exact same name across runs of the same video, hashtag or sound to get real deltas from the second run onward; give a different watchlist to a separate tracking effort so the two never mix their baselines.

#### Why did my run return zero followers?

The upstream data source currently disables the followers endpoint entirely and returns an explicit "unavailable" response for it. The Actor detects that signal, logs the honest reason, and returns 0 rows for `followers` rather than pretending it worked — this is a known upstream limitation, not an error in your input.

#### Does this Actor work with Claude, ChatGPT and AI agent frameworks?

There is no dedicated MCP server for this Actor. It is callable as a standard Apify Actor run from any agent framework capable of making an HTTP request or using the `apify-client` SDK, so an agent can trigger a run and read the resulting dataset like any other tool call.

#### Can I use this without a TikTok API key or developer account?

Yes. No TikTok login, cookies, developer account or OAuth app is required. The optional `apiKey` input is a premium key from the underlying data provider that lifts its own rate limit for faster collection — leave it empty and the Actor runs on the free tier.

***

### ✅ Conclusion

TikTok Data Scraper — Video, Hashtag & Sound Performance turns TikTok's public videos, hashtags, sounds, playlists, profiles and comments into structured JSON, then adds what a single scrape can't give you: real cross-run deltas, view velocity and a trending flag for anything you choose to track. It's built for social teams, agencies and researchers who need to watch specific content move over time rather than capture one snapshot. Add a username, hashtag, sound or playlist to the input, give your tracking scope a `watchlistName`, and run it again next week to see what actually changed.

# Actor input Schema

## `userUrls` (type: `array`):

One or more profiles. Examples: https://www.tiktok.com/@taylorswift · taylorswift · @taylorswift · 107955

## `categories` (type: `array`):

Pick which kinds of content to collect for each profile above. Multiple selections are supported.

## `maxUserResults` (type: `integer`):

Maximum items to fetch per profile / section. Ignored when '🌊 Scrape all results' is enabled. Minimum 40.

## `scrapeAllUserResults` (type: `boolean`):

Ignore the limit above and collect everything available for each profile.

## `videoUrlsComments` (type: `array`):

TikTok video URLs or IDs to scrape comments from.

## `commentsPerUrl` (type: `integer`):

Maximum comments per video. Ignored when '🌊 Scrape all comments' is on. Minimum 40.

## `scrapeAllComments` (type: `boolean`):

Collect every available comment per video (slower, more usage).

## `repliesPerComment` (type: `integer`):

Maximum replies per comment. Set to 0 to skip replies entirely. Ignored when '🌊 Scrape all replies' is on.

## `scrapeAllReplies` (type: `boolean`):

Collect every available reply for each comment (slower, more usage).

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

TikTok hashtag names or URLs to scrape videos from.

## `videosPerHashtag` (type: `integer`):

Maximum videos per hashtag. Ignored when '🌊 Scrape all hashtag videos' is on. Minimum 40.

## `scrapeAllHashtagVideos` (type: `boolean`):

Collect every available video for each hashtag (slower, more usage).

## `hashtagRegion` (type: `string`):

Two-letter country code used for localization (e.g. US, GB, IN).

## `musicUrls` (type: `array`):

TikTok music URLs or IDs to scrape videos from.

## `videosPerMusic` (type: `integer`):

Maximum videos per music track. Ignored when '🌊 Scrape all music videos' is on. Minimum 40.

## `scrapeAllMusicVideos` (type: `boolean`):

Collect every available video for each music track (slower, more usage).

## `playlistIDs` (type: `array`):

TikTok playlist IDs to scrape videos from.

## `videosPerPlaylist` (type: `integer`):

Maximum videos per playlist. Ignored when '🌊 Scrape all playlist videos' is on. Minimum 40.

## `scrapeAllPlaylistVideos` (type: `boolean`):

Collect every available video for each playlist (slower, more usage).

## `videoDetailsList` (type: `array`):

TikTok video IDs or URLs to retrieve full details for.

## `trackGrowth` (type: `boolean`):

When ON, every video record gets playCountDelta/diggCountDelta/commentCountDelta/growthDeltaAvailable/viewVelocity/trendingNow, and every tracked hashtag/music gets its own real aggregate deltas (hashtagViewCountDelta/hashtagUserCountDelta/musicVideoCountDelta), all diffed against the previous run.

## `watchlistName` (type: `string`):

Groups this run's growth snapshots so unrelated trackers sharing this Actor don't collide. Use the exact same name across runs of the same tracked video/hashtag/sound to get real deltas from run 2 onward. Example: 'campaign-launch-2026'.

## `resetBaseline` (type: `boolean`):

Clears the stored snapshot for every tracked entity in this run (under the current watchlist name) and starts a fresh baseline — this run's counts become the new starting point, so THIS run's own delta/velocity fields will be null/false again, exactly like a genuine first run.

## `minRequestInterval` (type: `number`):

Seconds to wait between consecutive requests. Lower is faster but more likely to be rate-limited.

## `maxRetries` (type: `integer`):

How many times to retry a failed/blocked request before giving up.

## `apiKey` (type: `string`):

Optional key that lifts the rate limit for faster collection. Leave empty for the free tier.

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

By default the scraper runs WITHOUT a proxy. If TikTok blocks the direct connection, it automatically escalates: 🛡️ datacenter → 🏠 residential, then sticks with residential. You can also force a proxy here.

## Actor input object example

```json
{
  "userUrls": [
    "https://www.tiktok.com/@taylorswift"
  ],
  "categories": [
    "videos"
  ],
  "maxUserResults": 10,
  "scrapeAllUserResults": false,
  "commentsPerUrl": 10,
  "scrapeAllComments": false,
  "repliesPerComment": 0,
  "scrapeAllReplies": false,
  "videosPerHashtag": 10,
  "scrapeAllHashtagVideos": false,
  "hashtagRegion": "US",
  "videosPerMusic": 10,
  "scrapeAllMusicVideos": false,
  "videosPerPlaylist": 10,
  "scrapeAllPlaylistVideos": false,
  "trackGrowth": true,
  "watchlistName": "default",
  "resetBaseline": false,
  "minRequestInterval": 1.2,
  "maxRetries": 3,
  "apiKey": "",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All scraped items in the Actor's default dataset.

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

// Run the Actor and wait for it to finish
const run = await client.actor("simpleapi/tiktok-data-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 = { "userUrls": ["https://www.tiktok.com/@taylorswift"] }

# Run the Actor and wait for it to finish
run = client.actor("simpleapi/tiktok-data-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 '{
  "userUrls": [
    "https://www.tiktok.com/@taylorswift"
  ]
}' |
apify call simpleapi/tiktok-data-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,simpleapi/tiktok-data-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/16TJFLGPC9DCyfCwF/builds/UHQayIR9O1CfBvbfl/openapi.json
