# TikTok Account Stats & Repost History Scraper (`vexaloom-labs/tiktok-account-stats-and-repost-history-scraper`) Actor

Look up TikTok accounts for follower/like/video stats, recent videos, and reposted content in one run. Optional session cookie unlocks a profile's full video history. No cookie required for account stats or reposts.

- **URL**: https://apify.com/vexaloom-labs/tiktok-account-stats-and-repost-history-scraper.md
- **Developed by:** [vexaloom-labs](https://apify.com/vexaloom-labs) (community)
- **Categories:** Social media, Videos, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 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.
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 Account Stats & Repost History Scraper

Look up any public TikTok account and export its stats, its public video history, the content it re-shared (reposts), its playlists and its current live status. Every account comes back as a profile row plus one row per video, repost or playlist, which makes it easy to schedule the actor and follow follower, like and posting trends over time. No TikTok account, cookies or proxy configuration are required.

### What this actor does

- **Account stats** — exact follower, following, hearts and video counts plus display name, bio, bio link, verification, business category, avatars and account settings.
- **Public video history** — walks an account's posts newest-first, up to the limit you set, instead of stopping at a fixed handful of recent posts.
- **Reposts** — the videos an account re-shared from other creators, with the original author preserved.
- **Playlists** — playlist names, covers, video counts and links; add individual playlist URLs to collect the videos inside them.
- **Live status** — whether the account is streaming right now, with the live room title, start time and viewer count when TikTok reports them.
- **Filters and limits** — a date window, a global row cap, a per-account row cap and optional derived statistics (average plays, median plays, posts per week).
- **New-video monitoring** — an `onlyNewVideos` mode schedules the actor as a change feed: it remembers each account's newest video between runs and emits only the videos posted since.
- **Pinned posts** — pinned videos carry `isPinned: true` and, with `pinnedFirst`, are emitted before the rest of the collected posts.
- **Stable play links** — every post row includes an `embedUrl` that plays the post in TikTok's embedded player without cookies, even after the session-bound media URLs expire.
- **Batch friendly** — mix usernames and profile URLs in one run; duplicates are removed automatically.
- **Empty fields are omitted** — no nulls, empty strings or placeholder values anywhere.

### Output per profile row

Profile rows have `rowType: "profile"`:

- `userId`, `secUid`, `username`, `displayName`
- `verified`, `privateAccount`, `isOrganization`, `ttSeller`
- `bio`, `bioLink`, `bioEmail` (when a valid address is written in the bio)
- `language`, `commerceCategory`
- `followerCount`, `followingCount`, `heartCount`, `videoCount`, `diggCount` — exact counts, never rounded abbreviations
- `avatarLarger`, `avatarMedium`, `avatarThumb`
- `createTime`, `createdAt`
- `commentSetting`, `duetSetting`, `stitchSetting`, `downloadSetting`, `followingVisibility`, `openFavorite`, `profileEmbedPermission`, `profileTab`, `UserStoryStatus`
- `isLive`, `liveRoomId`, `liveRoomTitle`, `liveStartTime`, `liveViewerCount` — present when TikTok reports the account's live status
- `averagePlays`, `medianPlays`, `postsPerWeek` — only with "Add Derived Statistics" enabled, computed over the videos fetched for that account in the run
- `profileUrl`, `scrapedAt`

### Output per video or repost row

Post rows have `rowType: "video"` for the account's own posts and `rowType: "repost"` for re-shared content, with `contentKind` mirroring the same value:

- `profileUsername` — the account the row was collected from
- `postId`, `postUrl`, `embedUrl` (permanent cookieless player link), `postType` (`video` or `slideshow`), `caption`, `textLanguage`, `locationCreated`
- `createTime`, `createdAt`
- `playCount`, `likeCount`, `commentCount`, `shareCount`, `collectCount`, `repostCount`
- `isPinned` — present when the account pinned this video to the top of its profile
- `stitchEnabled`, `shareEnabled`, `duetEnabled`, `commentStatus`
- `isAd`, `isAigc`, `isECVideo`, `privateItem`, `forFriend`, `originalItem`, `adLabelVersion`, `AIGCDescription`
- `author.*` — author snapshot at collection time: `id`, `secUid`, `username`, `displayName`, `bio`, `verified`, `privateAccount`, `roomId`, `avatarUrl`, `followerCount`, `followingCount`, `heartCount`, `videoCount`
- `music.*` — sound used: `id`, `title`, `authorName`, `original`, `duration`, `album`, `isCopyrighted`, `isCommerceMusic`, `isUnlimitedMusic`, `isPrivate`, `shootDuration`, `playUrl`, `coverUrl`, `musicUrl`, `dspLinks`
- `video.*` — technical metadata and media links: `width`, `height`, `duration`, `ratio`, `definition`, `bitrate`, `codecType`, `format`, `size`, `videoQuality`, `hasSubtitles`, `subtitleInfos`, `bitrateInfo`, `volumeInfo`, `playUrl`, `downloadAddr`, `cover`, `originCover`, `dynamicCover`
- `images` — image list for slideshow posts
- `hashtags`, `mentions`
- `mediaUrlExpiresAt` — when the signed media links expire, when TikTok provides it
- `stickersOnItem`, `effectStickers`, `diversificationLabels`, `channelTags`, `item_control`, `contents`, `poi`, `warnInfo` — extra post metadata, present when TikTok supplies it
- `sourcePlaylistId` — for videos collected from playlist URLs

### Output per playlist row

Playlist rows have `rowType: "playlist"`:

- `profileUsername` — the account that owns the playlist
- `playlistId`, `mixId`, `name`, `videoCount`
- `coverUrl`, `playlistUrl`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `usernames` | array | – | TikTok usernames with or without `@` |
| `profileUrls` | array | – | Full profile URLs, usable instead of or alongside usernames |
| `contentTypes` | array | `["profile","videos"]` | Any combination of `profile`, `videos`, `reposts`, `playlists` |
| `playlistUrls` | array | – | Playlist URLs to collect the videos inside them |
| `maxPostsPerProfile` | integer | `30` | Max post rows per account, combined across videos and reposts (0 skips posts) |
| `maxItems` | integer | `0` | Global cap on post rows for the whole run (0 = no cap) |
| `includeProfileInfo` | boolean | `true` | Set to false to emit post rows only |
| `oldestPostDate` | string | – | Only posts published on or after this date (`YYYY-MM-DD`, UTC) |
| `newestPostDate` | string | – | Only posts published on or before this date (`YYYY-MM-DD`, UTC) |
| `includeLiveStatus` | boolean | `true` | Adds live status fields to profile rows when TikTok reports them |
| `computedStats` | boolean | `false` | Adds average plays, median plays and posts per week to profile rows |
| `onlyNewVideos` | boolean | `false` | Monitoring mode: emit only videos posted since the previous run of the same account |
| `pinnedFirst` | boolean | `false` | Emit pinned videos before the rest of the post rows |

#### Example: stats snapshot for a watchlist

```json
{
  "usernames": ["nike", "adidas", "puma"],
  "contentTypes": ["profile"],
  "maxPostsPerProfile": 0,
  "computedStats": true
}
```

#### Example: recent videos and reposts since a date

```json
{
  "usernames": ["natgeo", "bbcnews"],
  "contentTypes": ["profile", "videos", "reposts"],
  "maxPostsPerProfile": 50,
  "oldestPostDate": "2026-09-01"
}
```

#### Example: repost history only

```json
{
  "usernames": ["khaby.lame"],
  "contentTypes": ["reposts"],
  "maxPostsPerProfile": 100
}
```

#### Example: collect the videos inside a playlist

```json
{
  "playlistUrls": ["https://www.tiktok.com/@natgeo/playlist/Cinematic-7509464647364283178"],
  "maxPostsPerProfile": 100
}
```

#### Example: full history for a short list

```json
{
  "usernames": ["corgibobaa"],
  "contentTypes": ["videos"],
  "maxPostsPerProfile": 500,
  "maxItems": 500
}
```

#### Example: new-video monitoring

```json
{
  "usernames": ["natgeo", "bbcnews"],
  "contentTypes": ["profile", "videos"],
  "maxPostsPerProfile": 50,
  "onlyNewVideos": true
}
```

Schedule this input daily. The first run returns the account's videos (up to the cap) and remembers the newest one; every later run returns only videos posted since the previous run, so you get a clean new-video feed without duplicates. Reposts and playlists are not filtered by this option.

### Use cases

- **Influencer vetting** — pull follower counts, engagement counters and recent performance for a shortlist of creators.
- **Follower growth tracking** — schedule a stats-only run to build a time series of followers, hearts and video counts.
- **Competitor monitoring** — watch posting cadence and engagement for brand accounts with date-windowed runs.
- **Repost analysis** — see exactly which outside content a creator chooses to amplify.
- **Playlist research** — export curated playlists as structured video lists.
- **Live campaign checks** — confirm whether an account is live and capture the stream details in the same run.

### FAQ

#### Do I need a TikTok account, cookies or a proxy?

No. The actor reads public profile data exactly as any logged-out visitor could. There is nothing to configure beyond your input list. (Older versions accepted `sessionCookie`, `cookies` and `ttwid`; those fields were removed because they never changed what the actor returns — old saved tasks that still send them keep working unchanged.)

#### How does onlyNewVideos behave on the first run?

The first run for an account returns all videos it collects (up to Max Post Rows per Profile) and stores the newest video as the monitoring checkpoint. Every following run returns only videos newer than that checkpoint. If the stored state is ever lost, the next run simply behaves like a first run again — you never get an error.

#### Why did a monitoring run return no video rows?

Either the account posted nothing since the previous run, or the checkpoint already covers its newest video. The run still succeeds and still emits the profile row, so a scheduled stats-only watch keeps working.

#### How far back does the video history go?

As far as you ask it to. `maxPostsPerProfile` caps the rows per account, and date filters let you stop at a chosen day. Large accounts can be paged to hundreds of videos; very large request sizes simply take longer.

#### Why does an account show no reposts?

Many accounts genuinely never repost. When an account has no publicly visible reposts, the run still succeeds and simply emits no repost rows.

#### Why can reposts be missing when I ask for both videos and reposts?

`maxPostsPerProfile` is a combined cap and videos are written first, so a video-heavy account can use the whole budget on videos. Choose `reposts` only if repost history is what you need.

#### Why are some fields missing from a row?

Empty fields are omitted on purpose. Some TikTok accounts do not publish a bio link, a location, subtitles or hashtags, and some payloads do not carry every optional flag — those fields are left out rather than filled with blanks.

#### What do averagePlays and medianPlays cover?

They are computed over the videos returned for that account in the same run, not over the account's entire history. Fetch more videos for a wider window.

#### Do the video and audio links work in my browser?

The `video.playUrl`, `video.downloadAddr` and `music.playUrl` links are short-lived addresses that TikTok binds to the session that requested them; opening them later usually returns Access Denied. They are kept because existing integrations reference them, but `embedUrl` is the durable, cookieless way to play a post: it is a permanent TikTok embed link that opens the post in TikTok's own player in any browser, with no account or cookies. `postUrl` remains the canonical link to the post page, and the image links (`cover`, `originCover`, avatars, playlist covers) resolve normally.

#### Which videos does pinnedFirst reorder?

TikTok lets an account pin up to three videos to the top of its profile. When `pinnedFirst` is on, any pinned video collected in the run is emitted before the other post rows, and it carries `isPinned: true`. Pinned videos normally appear inside their chronological position in TikTok's own API, so without this option they keep that position.

#### What happens to private, suspended or misspelled accounts?

They are skipped: no rows are written for them, the run still succeeds and the status message reports how many accounts could not be found. Profile stats for private accounts are returned when TikTok makes them public.

#### Is the account location or country included?

TikTok no longer includes an account region in its public profile payload, so the actor does not emit one.

#### Can I track changes over time?

Yes. Schedule the actor with a stats-only input and compare `followerCount`, `heartCount` and `videoCount` between runs using `scrapedAt` as the timestamp.

#### Is this affiliated with TikTok?

No. It is a third-party actor that reads publicly available TikTok web data.

# Actor input Schema

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

TikTok usernames to look up (with or without @). You can mix usernames and profile URLs, and list as many as you need in one run.

## `profileUrls` (type: `array`):

Full TikTok profile URLs (e.g. https://www.tiktok.com/@khaby.lame). Can be used instead of or together with Usernames.

## `contentTypes` (type: `array`):

Choose which rows to emit for every account. 'profile' = account stats and metadata, 'videos' = the account's posts, 'reposts' = content the account re-shared, 'playlists' = the account's playlists (names, covers, video counts).

## `playlistUrls` (type: `array`):

Full playlist URLs (e.g. https://www.tiktok.com/@natgeo/playlist/wildlife-7509464647364283178) to collect the videos inside those playlists. Independent of Usernames.

## `maxPostsPerProfile` (type: `integer`):

Maximum number of post rows per account, combined across videos and reposts. 0 skips post collection entirely (useful for a fast stats-only run). Playlist rows are not counted.

## `maxItems` (type: `integer`):

Global cap on post rows for the whole run across all accounts (0 = no limit). Profile and playlist rows are not counted.

## `includeProfileInfo` (type: `boolean`):

When off, no profile rows are written even if 'profile' is selected above; only post rows are produced.

## `oldestPostDate` (type: `string`):

Skip posts published before this date (UTC). Format: YYYY-MM-DD, e.g. 2026-01-01. Leave empty to collect the newest posts with no lower bound.

## `newestPostDate` (type: `string`):

Skip posts published after this date (UTC). Format: YYYY-MM-DD, e.g. 2026-01-01. Leave empty to start from each account's most recent post.

## `includeLiveStatus` (type: `boolean`):

Adds isLive and, when the account is live, the live room title, start time and viewer count to the profile row. Emitted only when TikTok reports the account's live status.

## `computedStats` (type: `boolean`):

Adds averagePlays, medianPlays and postsPerWeek to the profile row, computed over the videos fetched for that account in this run (the window is the returned set, not the full history).

## `onlyNewVideos` (type: `boolean`):

When on, each account emits only the videos posted since the previous run of that same account. The newest video seen is remembered between runs in the actor's named key-value store, so scheduling the actor turns it into a new-video monitor. The first run for an account returns all videos up to Max Post Rows per Profile and seeds the state. Reposts and playlists are not filtered. If the stored state is ever lost, the next run behaves like a first run.

## `pinnedFirst` (type: `boolean`):

When on, videos the account pinned to the top of its profile (TikTok's pinned posts) are emitted before the rest of the post rows collected in this run. Pinned videos also carry isPinned: true.

## Actor input object example

```json
{
  "usernames": [
    "natgeo"
  ],
  "contentTypes": [
    "profile",
    "videos",
    "reposts"
  ],
  "maxPostsPerProfile": 10,
  "maxItems": 0,
  "includeProfileInfo": true,
  "oldestPostDate": "",
  "newestPostDate": "",
  "includeLiveStatus": true,
  "computedStats": false,
  "onlyNewVideos": false,
  "pinnedFirst": false
}
```

# Actor output Schema

## `data` (type: `string`):

Dataset rows with a rowType discriminator: 'profile' for account stats, 'video'/'repost' for posts, 'playlist' for playlists.

# 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 = {
    "usernames": [
        "natgeo"
    ],
    "contentTypes": [
        "profile",
        "videos",
        "reposts"
    ],
    "maxPostsPerProfile": 10,
    "maxItems": 0,
    "includeProfileInfo": true,
    "oldestPostDate": "",
    "newestPostDate": "",
    "includeLiveStatus": true,
    "computedStats": false,
    "onlyNewVideos": false,
    "pinnedFirst": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("vexaloom-labs/tiktok-account-stats-and-repost-history-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 = {
    "usernames": ["natgeo"],
    "contentTypes": [
        "profile",
        "videos",
        "reposts",
    ],
    "maxPostsPerProfile": 10,
    "maxItems": 0,
    "includeProfileInfo": True,
    "oldestPostDate": "",
    "newestPostDate": "",
    "includeLiveStatus": True,
    "computedStats": False,
    "onlyNewVideos": False,
    "pinnedFirst": False,
}

# Run the Actor and wait for it to finish
run = client.actor("vexaloom-labs/tiktok-account-stats-and-repost-history-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 '{
  "usernames": [
    "natgeo"
  ],
  "contentTypes": [
    "profile",
    "videos",
    "reposts"
  ],
  "maxPostsPerProfile": 10,
  "maxItems": 0,
  "includeProfileInfo": true,
  "oldestPostDate": "",
  "newestPostDate": "",
  "includeLiveStatus": true,
  "computedStats": false,
  "onlyNewVideos": false,
  "pinnedFirst": false
}' |
apify call vexaloom-labs/tiktok-account-stats-and-repost-history-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vexaloom-labs/tiktok-account-stats-and-repost-history-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/deOnMBknQfl9b7ZlY/builds/tva5GT3U0Q1bbpOGj/openapi.json
