# TikTok Followers & Following Scraper: TikTok Followers List (`hyperbach/tiktok-followers-scraper`) Actor

TikTok followers scraper and following scraper: export the followers list and following list of any TikTok account, one row per follower with exact follower count, user id, secUid, bio, verified flag and follow time window. Newest first up to 10,000; only new followers on a schedule. No login.

- **URL**: https://apify.com/hyperbach/tiktok-followers-scraper.md
- **Developed by:** [Hyperbach](https://apify.com/hyperbach) (community)
- **Categories:** Social media, Lead generation, Automation
- **Stats:** 13 total users, 4 monthly users, 93.8% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 follower or following accounts

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?

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 Followers & Following Scraper: TikTok Followers List

**The followers and following lists of any TikTok account, newest first, one row per listed account with its exact follower count — 4,242,144, not 4,200,000 — its user id, secUid, bio, verified and private flags, and the window it followed in.** Give handles, profile URLs, user ids, secUids or share links; ask for followers, following or both, with a cap per list. TikTok serves about the newest 10,000 followers of an account to a logged-out reader (of a few large accounts, one page of 50): every list ends with a verdict that says whether it ended at TikTok's window, at your cap, or at the list's end. **[Run it on a schedule with `onlyNew`](https://apify.com/hyperbach/tiktok-followers-scraper/examples/monitor-new-tiktok-followers-daily) and get only the followers that came since the last run — 0 rows and $0 when none did.** Accounts that do not exist, are private or hide a list come back as a typed row, free. No login, no cookies, proxies handled for you.

### Why this scraper, not the other TikTok followers actors

- **Exact counts on every listed account.** TikTok's list carries each follower's exact follower, following, like and video counts next to the rounded ones the app shows. The lane leader returns the rounded ones and no secUid; here every row has `followers` (exact) and `followers_shown` (as the app shows it), with `counts_precision` saying which you got, plus `user_id` and `sec_uid`.
- **The follow time, for free.** TikTok's page cursor is the follow second of the page's last row, so every row carries the window it was followed in (`followed_after`, `followed_before`, about 30 follows wide) and its `position` in the list (1 = newest), at no extra cost: the walk reads only the 30-row pages.
- **Honest about the end of the list.** Logged out, TikTok serves about the newest 10,000 followers of an account and nothing older (measured on accounts from 143,000 to 143,000,000 followers); of a few large accounts it serves one page of 50 of each list, whatever is asked (gymshark, adidas and bbcnews of 31 brands we tried). Every list ends with a verdict: COMPLETE (TikTok said the list ended), SOURCE-LIMIT (TikTok's window, with the oldest follow time reached), CAPPED (your cap), NEW-ONLY or SINCE. Never a short dataset that looks whole.
- **Only the new followers since the last run.** With `onlyNew` a scheduled run reads from the newest follower down to the last run's read time (two minutes past it, for the follows TikTok shows late), skips every follower an earlier run delivered and delivers only the ones that came since, newest first; none came, 0 rows and nothing charged. Inside TikTok's window that is every new follower of an account that gains fewer than 10,000 between runs (fewer than 50 on the few accounts TikTok serves one page of).
- **Exact caps, free misses.** 2 asked = 2 delivered and charged. An account that does not exist, is private, or hides its following list gives one typed account row with TikTok's own status code, free. Filters (min / max followers, verified only, an e-mail in the bio, bio keywords) run before the bill: a row left out is not charged. Ready-made: [TikTok creators that brands follow, with an e-mail in the bio](https://apify.com/hyperbach/tiktok-followers-scraper/examples/tiktok-creators-brands-follow-with-email-in-bio).
- **Fast and plain.** The lists are read over plain HTTP, logged out, no browser: a 10,000-row list in about 2.5 minutes, a 2 + 2 run in a few seconds. Both lists of an account and several accounts are read side by side.
- **Contacts from the bio, settings, and more on request.** Each row has the e-mails, phone numbers and links found in the bio, the listed account's comment / duet / stitch / download settings and its TikTok Shop flag (TikTok shows no bio and no comment, duet or stitch setting of a private account). `includeProfileDetails` adds the created date, language, bio link and business category from each listed account's own page. Ready-made: [TikTok followers with account created date and language](https://apify.com/hyperbach/tiktok-followers-scraper/examples/tiktok-followers-with-account-created-date-and-language).

### Who it's for

- **Influencer marketing teams and agencies** — see who follows a creator and whom a creator follows, with exact counts per listed account, to vet audiences and find look-alike creators.
- **Brands and growth teams** — watch a competitor's or your own account on a schedule and get only the new followers of each day, with the window each one followed in.
- **Lead generation and outreach** — the followers of an account in a niche, filtered to verified accounts, a follower range or an e-mail in the bio, with the contacts extracted.
- **Researchers and data teams** — newest-first lists with positions, follow-time windows, stable ids (user id, secUid) and an honest end-of-list verdict, ready to join across runs.

### Quick start

**The newest 1,000 followers of an account**

```json
{
  "accounts": [
    "notionhq"
  ],
  "maxFollowersPerAccount": 1000
}
```

**Followers and following of several accounts**

```json
{
  "accounts": [
    "notionhq",
    "figma",
    "https://www.tiktok.com/@duolingo"
  ],
  "lists": "both",
  "maxFollowersPerAccount": 500,
  "maxFollowingPerAccount": 500
}
```

**Everything TikTok serves (about the newest 10,000)**

```json
{
  "accounts": [
    "notionhq"
  ],
  "lists": "followers"
}
```

**Only new followers since the last run (schedule it)**

```json
{
  "accounts": [
    "notionhq"
  ],
  "onlyNew": true
}
```

**Verified followers with 10,000+ followers and an e-mail in the bio**

```json
{
  "accounts": [
    "notionhq"
  ],
  "verifiedOnly": true,
  "minFollowers": 10000,
  "withEmailOnly": true
}
```

### Output

One row per follower or following account (row_type user), and one free row per input account with its record, its status and each list's verdict (row_type account):

| field | meaning |
|---|---|
| `row_type` | user (one follower or following account) or account (one per input: the target's record, its status and each list's verdict; free). |
| `relation` | On user rows: follower (follows of_username) or following (of_username follows them). |
| `of_user_id` | The account whose list this row comes from (its numeric user id, as a string). |
| `of_username` | That account's handle. |
| `input` | What you gave in accounts, verbatim. |
| `position` | The row's place in the list, 1 = the newest follower (or the most recent follow). Positions are counted 30 a page, so an account TikTok dropped from a page leaves a gap at the page's end. |
| `scraped_at` | When the row was read (UTC). |
| `user_id` | TikTok's numeric user id of the listed account, as a string. Stable across renames. |
| `sec_uid` | TikTok's secUid of the listed account, the id its own APIs take. |
| `username` | The listed account's handle, without @. |
| `nickname` | The listed account's display name. |
| `bio` | The listed account's profile text; empty when it has none, and on a private account (TikTok does not show it). |
| `bio_emails` | E-mail addresses found in the bio text (a regex; a @mention is not one). Empty when none. |
| `bio_phones` | Phone numbers found in the bio text (8 to 15 digits). Empty when none. |
| `bio_links` | URLs and bare domains found in the bio text. Empty when none. |
| `profile_url` | https://www.tiktok.com/@<username>. |
| `avatar_url` | The avatar at 1080 px (an expiring TikTok link). |
| `avatar_medium_url` | The avatar at 720 px. |
| `avatar_thumb_url` | The avatar at 100 px. |
| `verified` | TikTok's verified badge on the listed account. |
| `private_account` | The listed account is private. |
| `followers` | The listed account's followers, exact (TikTok's statsV2); on an account row, the target's exact follower count. |
| `following` | Accounts followed, exact. |
| `likes` | Likes received (hearts), exact. |
| `videos` | The account's video count. |
| `friends` | Mutual follows. |
| `followers_shown` | Followers as the app rounds them (4,200,000 for 4,242,144). |
| `following_shown` | Following as the app shows it. |
| `likes_shown` | Likes as the app rounds them. |
| `videos_shown` | Videos as the app shows them. |
| `counts_precision` | exact (TikTok's exact counts) or as_shown (only the rounded ones were served). |
| `followed_after` | The follow happened at or after this time (UTC): the follow-time window's start, from TikTok's page cursor (the follow second of the page's last row). |
| `followed_before` | The follow happened at or before this time (UTC): the previous page's cursor, or the read time on the newest page. |
| `follow_window_seconds` | followed_before minus followed_after: how precisely the follow time is known (about 30 follows wide). |
| `comment_setting` | everyone, friends or nobody (the listed account's setting); null on a private account (TikTok does not show it). |
| `duet_setting` | everyone, friends or nobody; null on a private account. |
| `stitch_setting` | everyone, friends or nobody; null on a private account. |
| `download_setting` | everyone, friends or nobody. |
| `liked_public` | The listed account shows the videos it liked. |
| `tt_seller` | The listed account sells on TikTok Shop. |
| `profile_detail_status` | With includeProfileDetails: ok when the listed account's own page was read (created_at, language, bio_link, business_category, is_organization filled), else why not (not_found, failed). Null when the add-on is off. |
| `created_at` | When the account was created (UTC): on user rows with includeProfileDetails, and on account rows. |
| `language` | The account's language as TikTok records it (user rows with includeProfileDetails; account rows). |
| `bio_link` | The link set in the profile; null when none (user rows with includeProfileDetails; account rows). |
| `business_category` | The category of a business account ("Shopping & Retail"); user rows with includeProfileDetails; account rows. |
| `is_organization` | TikTok marks the account as an organization (user rows with includeProfileDetails; account rows). |
| `is_live` | The account was live when its page was read (user rows with includeProfileDetails; account rows). |
| `has_story` | The account had a story up when its page was read (user rows with includeProfileDetails; account rows). |
| `read_country` | The country the row was read from (US). TikTok serves one list whatever the reader's country. |
| `run_tag` | runTag, copied onto every row. |
| `status` | On account rows: ok, private (counts shown, lists not public), not_found (no such account: the handle is free, or the account was banned or deleted), region_restricted, unavailable (withheld by TikTok), duplicate (the same account as another input), invalid_input (the entry is not a TikTok account), not_read (the run stopped before it) or failed. |
| `status_detail` | Why an account is not ok, in words, with TikTok's own status code. |
| `following_visibility` | On account rows: public, private or friends, as the account's page announces its following list. |
| `followers_status` | On account rows: ok, hidden (TikTok does not show the list), private (a private account), empty, refused (TikTok refused a page on three exits), error, or not_read. |
| `followers_requested` | maxFollowersPerAccount for this run; null = to the end TikTok serves. |
| `followers_delivered` | Follower rows this run delivered for the account (after the filters and the caps). |
| `followers_positions` | List positions walked (30 a page). Rows TikTok dropped from a page count as positions, not as rows. |
| `followers_coverage` | COMPLETE (TikTok said the list ended), SOURCE-LIMIT (TikTok ended the list far short of its own count: at its newest-10,000 window, or after one page of 50 on a few large accounts; followers_oldest_follow_at says where), CAPPED (your cap, maxItems or your maximum charge), NEW-ONLY (stopped at the last run's read time), SINCE (stopped at followedAfter), PARTIAL (a page was refused; the rows before it are delivered). |
| `followers_oldest_follow_at` | The follow time of the last row the walk reached (UTC): on a SOURCE-LIMIT list, where TikTok's window ends. |
| `followers_note` | How the followers list went, in words: positions against TikTok's count, where and why it stopped, rows the filters left out. |
| `following_status` | On account rows: ok, hidden (the account hides whom it follows), private, empty, refused, error, or not_read. |
| `following_requested` | maxFollowingPerAccount for this run; null = the whole list TikTok serves. |
| `following_delivered` | Following rows this run delivered for the account. |
| `following_positions` | List positions walked (30 a page). |
| `following_coverage` | COMPLETE, SOURCE-LIMIT, CAPPED, NEW-ONLY, SINCE or PARTIAL, as for followers_coverage. |
| `following_oldest_follow_at` | The follow time of the last row the walk reached (UTC). |
| `following_note` | How the following list went, in words. |
| `only_new` | The run had onlyNew on (account rows). |
| `seconds` | Seconds the account's lists took to read. |

Example record:

```json
{
  "row_type": "user",
  "relation": "follower",
  "of_user_id": "6907114026586719238",
  "of_username": "notionhq",
  "input": "notionhq",
  "position": 5,
  "scraped_at": "2026-10-07T19:47:39Z",
  "user_id": "7657579195245970454",
  "sec_uid": "MS4wLjABAAAA-2250ZO2rmnd4Q8KUIcKEcS7v-SmTrusBjwZyuuYjbmh3V8nXK4rTTpEK0zPzlvP",
  "username": "vista.webdesign",
  "nickname": "VISTA",
  "bio": "Nous transformons vos idées en sites qui convertissent\n🚀 Landing pages & sites web premium & performants\n📩 Collab : vista.smma@gmail.com",
  "bio_emails": [
    "vista.smma@gmail.com"
  ],
  "bio_phones": [],
  "bio_links": [],
  "profile_url": "https://www.tiktok.com/@vista.webdesign",
  "avatar_url": "https://p19-common-sign.tiktokcdn-us.com/tos-maliva-avt-0068/d0f1b50db2469f75fb45aa2c521e2419~tplv-tiktokx-cropcenter:1080:1080.jpeg?dr=9640&refresh_token=3aad59aa&x-expires=1791572400&x-signature=HObJWxIyt4MY75ib%2F%2FkLbuFJZIo%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
  "avatar_medium_url": "https://p16-common-sign.tiktokcdn-us.com/tos-maliva-avt-0068/d0f1b50db2469f75fb45aa2c521e2419~tplv-tiktokx-cropcenter:720:720.jpeg?dr=9640&refresh_token=00fb8a11&x-expires=1791572400&x-signature=qPxOjbWzhEcQtIKanISrOLd39sY%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
  "avatar_thumb_url": "https://p16-common-sign.tiktokcdn-us.com/tos-maliva-avt-0068/d0f1b50db2469f75fb45aa2c521e2419~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=904674ff&x-expires=1791572400&x-signature=YHVm51sSX5U1PunZUx7X0xgO83c%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
  "verified": false,
  "private_account": false,
  "followers": 1,
  "following": 5,
  "likes": 2,
  "videos": 1,
  "friends": 0,
  "followers_shown": 1,
  "following_shown": 5,
  "likes_shown": 2,
  "videos_shown": 1,
  "counts_precision": "exact",
  "followed_after": "2026-10-06T02:50:47Z",
  "followed_before": "2026-10-07T19:47:38Z",
  "follow_window_seconds": 147411,
  "comment_setting": "everyone",
  "duet_setting": "everyone",
  "stitch_setting": "everyone",
  "download_setting": "everyone",
  "liked_public": false,
  "tt_seller": false,
  "profile_detail_status": null,
  "created_at": null,
  "language": null,
  "bio_link": null,
  "business_category": null,
  "is_organization": null,
  "is_live": null,
  "has_story": null,
  "read_country": "US",
  "run_tag": "a-c1-c2",
  "status": null,
  "status_detail": null,
  "following_visibility": null,
  "followers_status": null,
  "followers_requested": null,
  "followers_delivered": null,
  "followers_positions": null,
  "followers_coverage": null,
  "followers_oldest_follow_at": null,
  "followers_note": null,
  "following_status": null,
  "following_requested": null,
  "following_delivered": null,
  "following_positions": null,
  "following_coverage": null,
  "following_oldest_follow_at": null,
  "following_note": null,
  "only_new": null,
  "seconds": null
}
```

### Pricing

**Pay only for the follower and following rows a run delivers**, with no start fee and no platform usage billed to you. Prices fall with your Apify plan.

| per 1,000 | Free | Starter | Scale | Business |
|---|---|---|---|---|
| Follower or following account (`user`) | $2.00 | $1.70 | $1.40 | $1.00 |
| Profile details on that row (`profile_detail`) | +$1.00 | +$0.85 | +$0.70 | +$0.50 |

A follower or following row is **$2.00 per 1,000 rows** on the Free plan and $1.00 on Business: a full list of TikTok's newest 10,000 followers is $20.00 on Free. The `+` fee is charged **only on a row that carries the thing**: with `includeProfileDetails`, a row that carries the listed account's own profile page ($1.00 per 1,000 such rows on the Free plan); a page TikTok did not serve is said on the row and not charged. Free: the account row of every input (its record, its status and each list's verdict), accounts that do not exist, are private or hide a list, rows the filters leave out, a run with `onlyNew` that finds nothing new (0 rows, $0), and a run that failed before its first row. `maxItems` and the caps per list are exact; before the first request the log says the most the run can cost at your plan's prices. Enterprise plans have their own rates — the Actor's Pricing tab shows the price for your plan.

### Usage patterns

- **Audience lists** — Give the accounts in `accounts` with `lists: followers` and a cap per account. Rows come newest first with `position`, the exact counts of each follower, and the window it followed in. The account row says how far TikTok's window reaches (`followers_coverage`, `followers_oldest_follow_at`). Ready-made: [Export the newest 1,000 TikTok followers of an account](https://apify.com/hyperbach/tiktok-followers-scraper/examples/export-tiktok-followers-of-an-account).
- **Whom an account follows** — `lists: following` reads the whole following list (TikTok serves it to its end, except one page of 50 on a few large accounts: the account row says SOURCE-LIMIT). Accounts that hide the list give a free account row with `following_status: hidden`; the account row's `following_visibility` says it before any list is read. Ready-made: [Check which TikTok accounts hide their following list](https://apify.com/hyperbach/tiktok-followers-scraper/examples/check-which-tiktok-accounts-hide-their-following-list).
- **New followers on a schedule** — Save a task with `onlyNew: true` and run it hourly or daily: each run delivers only the followers that came since the previous run, nothing when none did. The memory is per account and list, keyed by the user id, so the same account given as a handle, a URL or an id meets its memory; give `stateStoreName` to keep two tasks on the same account apart. With a cap per run, the followers the cap left out come in the next run. The first run is the baseline and delivers the whole list up to your cap. Ready-made: [Monitor new TikTok followers daily: only the new ones](https://apify.com/hyperbach/tiktok-followers-scraper/examples/monitor-new-tiktok-followers-daily).
- **Lead lists** — Filters run before the bill: `minFollowers` / `maxFollowers` (exact counts), `verifiedOnly`, `withEmailOnly`, `bioKeywords`. A row left out costs nothing and does not count against the caps. `includeProfileDetails` adds each listed account's created date, language, bio link and business category from its own page, charged per row. Ready-made: [Find TikTok followers by bio keyword: marketers, founders, SEO](https://apify.com/hyperbach/tiktok-followers-scraper/examples/find-tiktok-followers-by-bio-keyword).
- **A window in time** — `followedAfter` stops a list at a date (or a span such as 30 days). TikTok gives a follow-time window per page, not a second per row, so the last page delivered may hold up to 30 rows older than the date; the account row says so (`followers_coverage: SINCE`). Ready-made: [TikTok followers of the last 7 days](https://apify.com/hyperbach/tiktok-followers-scraper/examples/tiktok-followers-of-the-last-7-days).

### Input configuration

| field | type | default | what it does |
|---|---|---|---|
| `accounts` | `array` | `[]` | TikTok accounts, one per line, in any form: a handle (notionhq or @notionhq), a profile URL, a video or photo URL (its author's lists are read), a numeric user id, a secUid, or a share link (vm.tiktok.com/…). Handles are case-insensitive; an account named twice is read once. |
| `lists` | `followers` / `following` / `both` | `"followers"` | Which list of each account to read: its followers, the accounts it follows, or both. Each row says which (relation). |
| `maxFollowersPerAccount` | `integer` | `0` | How many of each account's followers to deliver, newest first; 0 = everything TikTok serves (about the newest 10,000, of a few large accounts 50; then the list ends: the account row says SOURCE-LIMIT and the oldest follow time reached). Exact: 2 asked = 2 delivered and charged. |
| `maxFollowingPerAccount` | `integer` | `0` | How many of the accounts each account follows to deliver, newest first; 0 = the whole list TikTok serves (to its end; one page of 50 on a few large accounts). Accounts that hide their following list give a free row that says so. |
| `followedAfter` | `string` |  | Only the rows followed on or after this date: 2026-10-01, or a span back from the run's start such as 3 days (72 hours), 2 weeks, 6 months (UTC). TikTok gives each page a follow-time window, not a second per row, so the last page delivered may hold up to 30 rows older than the date (the account row says so). |
| `onlyNew` | `boolean` | `false` | Deliver only the followers (or following) that appeared since the last run of the same task, newest first, and nothing when none did (0 rows, $0). The first run with this on delivers the whole list as the baseline. The memory is kept per account and list (by the user id) under stateStoreName, or under one default memory of your Apify account. Inside TikTok's window (the newest ≈ 10,000; 50 on a few large accounts) a scheduled run catches every new follower of an account that gains fewer than that between runs. |
| `stateStoreName` | `string` |  | The name of the memory onlyNew uses between runs (per account and list, keyed by the user id, so a renamed account or another form of it keeps its memory). Empty = one default memory for your Apify account; give a name to keep two tasks on the same account apart. |
| `minFollowers` | `integer` | `0` | Deliver only listed accounts with at least this many followers (exact count); 0 = no filter. Rows left out are not charged and do not count against the caps. |
| `maxFollowers` | `integer` | `0` | Deliver only listed accounts with at most this many followers; 0 = no filter. |
| `verifiedOnly` | `boolean` | `false` | Deliver only listed accounts with TikTok's verified badge. |
| `withEmailOnly` | `boolean` | `false` | Deliver only listed accounts whose bio carries an e-mail address (bio_emails). Extraction from the bio text only; no contact finding. |
| `bioKeywords` | `array` | `[]` | Deliver only listed accounts whose bio, name or handle contains one of these words (case-insensitive). Empty = no filter. |
| `includeProfileDetails` | `boolean` | `false` | Read each delivered account's own profile page too (one request per row) for its created date, language, bio link, business category and organization flag. Charged per row as a profile_detail event, besides the row. TikTok shows no region to a logged-out reader, so none is given. |
| `maxItems` | `integer` | `0` | Stop after this many charged rows across all accounts and lists; 0 = no limit. Free rows (the account rows, misses) do not count. The run also stops inside your maximum charge and says so. |
| `runTag` | `string` |  | A label copied onto every row (run_tag), to tell runs apart in a shared dataset. |
| `resumeFromRunId` | `string` |  | The id of an earlier run of this actor with the same input that stopped before the end: this run reads only the accounts and pages it had not finished, and delivers nothing twice. |

### Errors

**A run does not fail because of your input.** When an input cannot be used — a date that is not a date, an id in the wrong form, two settings that contradict each other — the run ends **Succeeded**, charges nothing, and says what to change:

- the status message starts with `INPUT REJECTED`;
- the dataset holds one row, `{"error": true, "code": "…", "message": "…"}`, and no results;
- the key-value store record `ERROR` holds the same object.

From code, check `error` on the first row before reading results.

An account that does not exist, is private or hides a list is not an error: it gets one free account row with the reason, and the run goes on with the others. A run that ends **Failed** is a fault on our side, never your input; it charges nothing, and we are alerted.

### FAQ

**How far back does a followers list go?**

Logged out, TikTok serves about the newest 10,000 followers of an account and then says the list ended; nothing older can be reached by any cursor (we tried every date back to the account's creation on two accounts). How far back that is depends on the account: a day on an account that gains 10,000 a day, more than a year on a slower one. The account row's followers_oldest_follow_at says where the window ended; followers_coverage says SOURCE-LIMIT. A few large accounts get one page of 50 of each list, whatever cursor, page size or country is asked (gymshark, adidas and bbcnews of 31 brands we tried in October 2026); their account row says SOURCE-LIMIT and "only 50 positions". A following list has no 10,000 window: it is served to its end, except on those accounts.

**What are followed_after and followed_before?**

TikTok's list cursor is the follow time, to the second, of the last row of each page of 30. So every row's follow time is known to lie at or after the cursor of its own page and at or before the cursor of the page before it (the read time for the newest page). follow_window_seconds is the width of that window. The exact second per row is not delivered.

**Why are there fewer rows than positions on some lists?**

TikTok pages its lists 30 positions at a time and drops rows it does not serve (deactivated accounts, for one), so a page can carry 18 to 30 rows. Positions count the page slots; rows are what was served. A following list of an old account can return about 93% of its count this way, and the verdict is still COMPLETE when TikTok said the list ended.

**What happens with private, missing or hidden accounts?**

They cost nothing. A private account gives one account row with status private and its counts; a handle nobody has gives not_found; an account that hides whom it follows gives following_status hidden with TikTok's status code 10222. The other accounts in the run are read as usual.

**Does onlyNew detect unfollows?**

Not in this version: it delivers the followers that came since the last run. Unfollows need a full re-read of a list and a diff; ask us if you need it.

**What is never charged?**

The account rows; accounts that do not exist, are private or hide a list; rows the filters leave out; a run with onlyNew that finds nothing new; and a run that failed before its first row.

**Can I get a follower's region, e-mail or Instagram?**

TikTok does not show a region to a logged-out reader (actors that output one invent it). E-mails, phone numbers and links are extracted from the bio text when the account put them there (bio_emails, bio_phones, bio_links); no contact finding beyond that. Linked Instagram or YouTube ids are not on TikTok's web record.

### Integration

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('hyperbach/tiktok-followers-scraper').call({"accounts": ["notionhq"], "maxFollowersPerAccount": 1000});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

#### Python

```python
from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('hyperbach/tiktok-followers-scraper').call(run_input={'accounts': ['notionhq'], 'maxFollowersPerAccount': 1000})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

#### CLI

```bash
apify call hyperbach/tiktok-followers-scraper --input '{"accounts": ["notionhq"], "maxFollowersPerAccount": 1000}'
```

#### REST

```bash
curl -X POST "https://api.apify.com/v2/acts/hyperbach~tiktok-followers-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' -d '{"accounts": ["notionhq"], "maxFollowersPerAccount": 1000}'
```

### Support

apify@hyperbach.com

*This page is generated from the Actor's schemas and a live sample — it cannot describe a field the Actor does not have.*

# Actor input Schema

## `accounts` (type: `array`):

TikTok accounts, one per line, in any form: a handle (notionhq or @notionhq), a profile URL, a video or photo URL (its author's lists are read), a numeric user id, a secUid, or a share link (vm.tiktok.com/…). Handles are case-insensitive; an account named twice is read once.

## `lists` (type: `string`):

Which list of each account to read: its followers, the accounts it follows, or both. Each row says which (relation).

## `maxFollowersPerAccount` (type: `integer`):

How many of each account's followers to deliver, newest first; 0 = everything TikTok serves (about the newest 10,000, of a few large accounts 50; then the list ends: the account row says SOURCE-LIMIT and the oldest follow time reached). Exact: 2 asked = 2 delivered and charged.

## `maxFollowingPerAccount` (type: `integer`):

How many of the accounts each account follows to deliver, newest first; 0 = the whole list TikTok serves (to its end; one page of 50 on a few large accounts). Accounts that hide their following list give a free row that says so.

## `followedAfter` (type: `string`):

Only the rows followed on or after this date: 2026-10-01, or a span back from the run's start such as 3 days (72 hours), 2 weeks, 6 months (UTC). TikTok gives each page a follow-time window, not a second per row, so the last page delivered may hold up to 30 rows older than the date (the account row says so).

## `onlyNew` (type: `boolean`):

Deliver only the followers (or following) that appeared since the last run of the same task, newest first, and nothing when none did (0 rows, $0). The first run with this on delivers the whole list as the baseline. The memory is kept per account and list (by the user id) under stateStoreName, or under one default memory of your Apify account. Inside TikTok's window (the newest ≈ 10,000; 50 on a few large accounts) a scheduled run catches every new follower of an account that gains fewer than that between runs.

## `stateStoreName` (type: `string`):

The name of the memory onlyNew uses between runs (per account and list, keyed by the user id, so a renamed account or another form of it keeps its memory). Empty = one default memory for your Apify account; give a name to keep two tasks on the same account apart.

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

Deliver only listed accounts with at least this many followers (exact count); 0 = no filter. Rows left out are not charged and do not count against the caps.

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

Deliver only listed accounts with at most this many followers; 0 = no filter.

## `verifiedOnly` (type: `boolean`):

Deliver only listed accounts with TikTok's verified badge.

## `withEmailOnly` (type: `boolean`):

Deliver only listed accounts whose bio carries an e-mail address (bio_emails). Extraction from the bio text only; no contact finding.

## `bioKeywords` (type: `array`):

Deliver only listed accounts whose bio, name or handle contains one of these words (case-insensitive). Empty = no filter.

## `includeProfileDetails` (type: `boolean`):

Read each delivered account's own profile page too (one request per row) for its created date, language, bio link, business category and organization flag. Charged per row as a profile_detail event, besides the row. TikTok shows no region to a logged-out reader, so none is given.

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

Stop after this many charged rows across all accounts and lists; 0 = no limit. Free rows (the account rows, misses) do not count. The run also stops inside your maximum charge and says so.

## `runTag` (type: `string`):

A label copied onto every row (run_tag), to tell runs apart in a shared dataset.

## `resumeFromRunId` (type: `string`):

The id of an earlier run of this actor with the same input that stopped before the end: this run reads only the accounts and pages it had not finished, and delivers nothing twice.

## Actor input object example

```json
{
  "accounts": [
    "notionhq"
  ],
  "lists": "followers",
  "maxFollowersPerAccount": 1000,
  "maxFollowingPerAccount": 1000,
  "onlyNew": false,
  "minFollowers": 0,
  "maxFollowers": 0,
  "verifiedOnly": false,
  "withEmailOnly": false,
  "bioKeywords": [],
  "includeProfileDetails": false,
  "maxItems": 100
}
```

# Actor output Schema

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

All scraped records in the default dataset. One row per follower or following account (row_type user), and one free row per input account with its record, its status and each list's verdict (row_type account):

## `runSummary` (type: `string`):

How the run went, per account and list: status, rows asked, served, delivered and filtered, positions walked against TikTok's count, the coverage verdict, the oldest follow time reached, pages and calls, retries and exit rotations, seconds; why the run stopped early, if it did; and the source counters (requests, walls, rotations, bytes).

# 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 = {
    "accounts": [
        "notionhq"
    ],
    "maxFollowersPerAccount": 1000,
    "maxFollowingPerAccount": 1000,
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("hyperbach/tiktok-followers-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 = {
    "accounts": ["notionhq"],
    "maxFollowersPerAccount": 1000,
    "maxFollowingPerAccount": 1000,
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("hyperbach/tiktok-followers-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 '{
  "accounts": [
    "notionhq"
  ],
  "maxFollowersPerAccount": 1000,
  "maxFollowingPerAccount": 1000,
  "maxItems": 100
}' |
apify call hyperbach/tiktok-followers-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,hyperbach/tiktok-followers-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/6xhJUM36RT686o4T8/builds/goHcUsZ9OYRrGJhyD/openapi.json
