# TikTok Follower Count Scraper & Monitor - Exact, Not Rounded (`neverempty/tiktok-follower-count-scraper`) Actor

Follower, like and video counts for any public TikTok profile, taken from the exact figures TikTok publishes, not the rounded ones it displays. Turn monitoring on and later runs return a profile only when a count actually moves, so you are never charged twice for the same numbers.

- **URL**: https://apify.com/neverempty/tiktok-follower-count-scraper.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (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 $5.84 / 1,000 profile returneds

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 Follower Count Scraper & Monitor — exact counts, not rounded

Follower, like and video counts for any public TikTok profile — and, with monitoring on, **you are never charged for the same unchanged profile twice**. A profile comes back only when one of its counts actually moves.

The counts are the **exact figures TikTok publishes on the page**, not the rounded ones it shows on screen. That difference is the whole reason this Actor is useful for monitoring, and it is measurable: of the **14** profiles that could be read on 2026-09-06, **11** had a displayed figure that differed from the exact one, by up to **41519** followers.

| Account | TikTok displays | Exact figure | Difference |
|---|---|---|---|
| duolingo | 18,000,000 | 17,960,673 | −39,327 |
| shopify | 2,100,000 | 2,058,481 | −41,519 |
| mkbhd | 2,300,000 | 2,335,056 | +35,056 |

A large account can sit at the same displayed number for weeks. Monitoring built on that number reports "no change" while the account is in fact gaining thousands of followers a day. This Actor watches the exact figure, so a change is a change.

Every number in this README can be recounted from this repository: `test/fixtures/measured-2026-09-06.json` holds one entry per profile read that day, and `test/parse.test.mjs` recounts them.

### Best for

- **Creator and influencer research** — pull the real follower, like and video counts for a list of accounts before you pay anyone.
- **Watching a roster on a schedule** — turn monitoring on and a profile comes back only on the days its numbers actually moved.
- **Building a growth history** — `followerDelta` and `changedFields` give you the change since the last run, so you can store a series without diffing anything yourself.

### How fast, and what it costs to run

Measured on this Actor on 2026-09-06, from Apify:

| | |
|---|---|
| 3 profiles, monitoring on | **5 seconds** |
| Platform cost of that run | **$0.00108** (about $0.00036 per profile read) |
| Requests | one page per profile, no proxy unless a bot check appears |

Because unchanged profiles are read but not returned, monitoring stays profitable for you: reading twenty accounts and being charged for the one that moved still costs less than a cent to run.

### What each row contains

- **What you asked for and what TikTok returned** — `inputUsername`, `username`, and `usernameMatchesInput`, so a renamed or redirected account is visible rather than silently swapped.
- **Identity** — `userId`, `secUid`, `profileUrl`, `nickname`, `signature` (bio), `bioLink`.
- **Exact counts** — `followerCount`, `followingCount`, `likeCount`, `videoCount`, `friendCount`.
- **What TikTok shows on screen** — `followerCountDisplay`, `likeCountDisplay`, `displayIsRounded`, `followerCountDisplayDiff`.
- **Flags** — `verified`, `privateAccount`, `isOrganization`, `ttSeller`, `language`.
- **Account age** — `createdAtEpoch` and `createdAt` (the same instant, as TikTok gives it and as ISO).
- **Monitoring** — `isFirstCheck`, `previousCheckedAt`, `previousFollowerCount`, `followerDelta`, `changedFields`.

`bioLink` is returned **as the creator typed it**, which is often `example.com` with no scheme. No `https://` is added, because adding one would invent an address the creator never wrote. An e-mail address or phone number written into the bio or that field is removed.

`region` is not returned: it is not on the profile page. Of the 14 profiles measured, 0 had it.

### Monitoring, and what it costs

Turn **monitoring mode** on and the Actor remembers each account's counts. On later runs:

- a profile whose counts have not moved is **not returned and not charged**;
- a profile whose counts moved comes back with `followerDelta` and `changedFields`;
- an account it has never seen is always returned once, so nothing is missed on the first run.

Choose whether *any* of the five counts counts as a change, or only the follower count.

The counts are remembered **per account, not per list**, so adding a username to a list does not reset the history or re-charge the profiles already being watched.

Monitoring adds a flat access fee of **$5, charged once per calendar month (UTC)**, on the first monitoring run of that month — once, no matter how many lists or scheduled tasks you run. That fee pays for watching on the days when nothing moved — those runs return no rows and cost nothing else. **Runs with monitoring off never trigger it.**

### Rows that are never charged

When there is nothing to return, this Actor says why instead of returning an empty result:

- `no-such-profile` — TikTok returned no profile for that username. TikTok answers with the **same code (10221)** for a name that never existed, one that was changed, and one that is no longer available, and its own message is inconsistent between them, so this row **does not claim the account was banned**. It reports the code and asks you to check the spelling.
- `blocked` — TikTok answered with a bot check rather than the profile. From some networks that arrives as HTTP 200 with a 1,462-byte page containing no data at all; it is reported as a bot check, never as an account with no followers. Profiles are requested directly first, and only a bot check makes the run switch to a proxy session and try again.
- `no-change` — monitoring ran and nothing had moved.
- `no-match` — profiles were read and none matched your filters.
- `unreadable` — the page could not be parsed, or TikTok answered with a status code of its own whose meaning has not been measured here. The code is reported as-is and is not translated into a claim about the account. Nothing is guessed.
- `no-results` — the profile loaded but TikTok published none of the counts. It is not returned as an account with zero followers, and it is not charged.

### Which pages this Actor requests

Only `https://www.tiktok.com/@username`. TikTok's `robots.txt` disallows `/search`, `/search/user`, `/embed/@`, `/shop/view/product/`, `/discover/trending/detail/` and the internal API paths, and this Actor cannot be made to request them: anything whose path is not a `/@username` page is rejected before any request is made — with or without `https://` in front of it — so a hashtag, sound or shop link is never read as if it were an account.

### Filters

Minimum and maximum followers (compared against the exact figure), verified only, has a bio link, skip private accounts, and keyword include/exclude across the display name, bio and username. Filters are applied **after** the profiles are read and **before** anything is charged.

### Input

| Field | What it does |
|---|---|
| `usernames` | The accounts to read: `@nasa`, `nasa`, or a profile URL. |
| `maxProfiles` | How many rows to return in total. |
| `monitoringMode` | Remember the counts and return a profile only when they move. |
| `monitorMode` | Whether any count counts as a change, or only the follower count. |
| `resetMonitoringState` | Forget what was remembered for this list and start a fresh history. |
| `minFollowers` / `maxFollowers` | Follower range, compared against the exact figure. |
| `onlyVerified` | Keep only verified accounts. |
| `onlyWithBioLink` | Keep only accounts with a link in the bio. |
| `excludePrivate` | Drop accounts TikTok marks as private. |
| `keywords` / `keywordMatch` / `excludeKeywords` | Filter on display name, bio and username. |
| `useProxy` | Every profile is requested directly first (fastest, and it works from Apify). This only decides whether a bot check is retried through a proxy. |

### Questions

**Why do your numbers differ from what TikTok shows on the profile?**
Because TikTok publishes two figures and shows you the rounded one. This Actor returns both: `followerCount` is the exact figure from the page's own data, `followerCountDisplay` is what the screen shows, and `followerCountDisplayDiff` is the gap. In the 14 profiles measured on 2026-09-06, 11 differed, by up to 41,519 followers.

**Does monitoring charge me when nothing changed?**
No. A profile whose watched counts have not moved is not returned and not charged; the run returns a single free `no-change` row instead. The $5 monthly access fee is what pays for those quiet days, and it is charged once per calendar month no matter how many lists or scheduled tasks you run.

**What happens to a username that does not exist?**
It comes back as `no-such-profile` with TikTok's own status code, and it is not charged. TikTok answers with the same code for a name that never existed, one that was renamed and one that is no longer available, so this Actor reports the code rather than claiming which — it will not tell you an account was banned.

**Do I need a proxy?**
Not usually. Every profile is requested directly first, which is the fastest and cheapest path and worked on every attempt measured on 2026-09-06. From some networks TikTok answers with a 1,462-byte page containing no data; that is reported as a bot check, and with `useProxy` on the run switches to a proxy session and tries again.

**Can I watch private accounts?**
They are returned by default, and are marked `privateAccount`. How much TikTok publishes for them has not been measured here, so nothing is claimed about it.

### How this differs from other TikTok follower scrapers

- **It reads the exact figure, not the rounded one.** A rounded count can sit at the same value for weeks on a large account, so monitoring built on it reports "no change" while the account is growing by thousands a day.
- **It never charges twice for the same unchanged numbers**, and the counts are remembered per account rather than per list, so adding a username does not reset the history.
- **It only requests `/@username` pages.** Anything else — a hashtag, a sound, a shop link — is rejected before a request is made.
- **It separates "no profile" from "bot check" from "no counts published"**, and none of those rows are charged.

### Example input

```json
{
  "usernames": ["@nasa", "@duolingo"],
  "monitoringMode": true,
  "monitorMode": "any-change",
  "maxProfiles": 100
}
```

# Actor input Schema

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

Usernames (@nasa), plain names (nasa) or profile URLs (https://www.tiktok.com/@nasa, with or without the scheme). A video URL works too - the username is taken from it. Only /@username pages are requested, because TikTok's robots.txt disallows /search, /embed/@ and the shop pages; anything whose path is not a /@username page - a hashtag, a sound, a shop link - is rejected rather than read as a username. A name TikTok returns nothing for is reported as 'no such profile' with TikTok's own code, never guessed and never called banned.

## `maxProfiles` (type: `integer`):

How many rows to return in total. You are charged for the rows you actually receive.

## `monitoringMode` (type: `boolean`):

Off = return every profile you listed, charged per row. On = remember the counts and, on later runs, return a profile only when one of them actually moved; unchanged profiles cost nothing. The counts are remembered per account, not per list, so adding a username to the list does not re-charge the ones already being watched. **Monitoring adds a flat access fee of $5, charged once per calendar month (UTC)** on the first monitoring run of that month, no matter how many lists or scheduled tasks you run. That fee is what pays for watching on the days when nothing moved - those runs return no rows and cost nothing else. Runs with monitoring off never trigger it.

## `monitorMode` (type: `string`):

Any change = the profile comes back when followers, following, likes, videos or friends move. Follower change only = it comes back only when the follower count moves. Both compare the exact figures TikTok publishes, not the rounded ones it displays - a rounded count can sit at the same value for weeks on a large account, which is why monitoring built on the displayed number reports 'no change' when the account is in fact growing.

## `resetMonitoringState` (type: `boolean`):

Clears every remembered count for this Actor, so the next run returns each profile once again and starts a fresh history. This affects all your monitoring runs, because the counts are stored per account rather than per list.

## `minFollowers` (type: `integer`):

Drop profiles below this follower count. Compared against the exact figure, not the rounded one shown on screen. 0 = no minimum.

## `maxFollowers` (type: `integer`):

Drop profiles above this follower count. 0 = no maximum.

## `onlyVerified` (type: `boolean`):

Keep only accounts TikTok marks as verified.

## `onlyWithBioLink` (type: `boolean`):

Keep only accounts that put a link in their bio. The link is returned as the creator typed it, which is often 'example.com' without https:// - no scheme is added, because adding one would invent an address the creator never wrote. An e-mail address or phone number written into that field is removed.

## `excludePrivate` (type: `boolean`):

Drop accounts TikTok marks as private. They are returned by default. (No private account appeared in the 16 profiles measured on 2026-09-06, so how much TikTok publishes for them has not been measured here.)

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

Keep only profiles whose display name, bio or username contains these words.

## `keywordMatch` (type: `string`):

Any = at least one keyword. All = every keyword.

## `excludeKeywords` (type: `array`):

Drop profiles whose display name, bio or username contains any of these words.

## `useProxy` (type: `boolean`):

Every profile is requested directly first, which is the fastest and cheapest path and works from Apify (measured 3 out of 3 on 2026-09-06). From some networks TikTok answers instead with HTTP 200 and a 1,462-byte page containing no profile data at all. That page is reported as a bot check, never as an account with no followers. With this on, the run switches to a proxy session and tries again; with it off, the bot check is returned as a row and no proxy is paid for.

## Actor input object example

```json
{
  "usernames": [
    "@nasa",
    "@duolingo"
  ],
  "maxProfiles": 100,
  "monitoringMode": false,
  "monitorMode": "any-change",
  "resetMonitoringState": false,
  "minFollowers": 0,
  "maxFollowers": 0,
  "onlyVerified": false,
  "onlyWithBioLink": false,
  "excludePrivate": false,
  "keywords": [],
  "keywordMatch": "any",
  "excludeKeywords": [],
  "useProxy": true
}
```

# Actor output Schema

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

One row per TikTok profile: the username you asked for and the one TikTok resolved it to, with a flag saying whether they match; the numeric user id and secUid; display name, bio and the bio link exactly as the creator typed it; follower, following, like, video and friend counts taken from the exact figures on the page, plus the rounded figures TikTok shows on screen and the difference between them; verified, private, organisation and seller flags; the account language and creation time; and, in monitoring mode, the previous follower count, the change since the last run, and which counts moved. Usernames TikTok returns nothing for, pages that came back as a bot check, and profiles that had not changed come back as their own rows and are not charged.

# 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": [
        "@nasa",
        "@duolingo"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/tiktok-follower-count-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": [
        "@nasa",
        "@duolingo",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/tiktok-follower-count-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": [
    "@nasa",
    "@duolingo"
  ]
}' |
apify call neverempty/tiktok-follower-count-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/tiktok-follower-count-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/9Z28lvHGsTsymOb7w/builds/rcunFIPSffILZSc7c/openapi.json
