Skip to content

ChocoData-com/tiktok-follower-scraper

Repository files navigation

TikTok Follower Scraper

TikTok Follower Scraper

TikTok Follower Scraper for extracting follower records, the exact follower total and a next-page cursor for a public TikTok account from TikTok.com. This repo has a free script that shows you exactly where the free path stops, and a TikTok follower data API that returns 30 records per page with cursor pagination.

This is the follower endpoint on its own. The TikTok Scraper repo covers the rest of the surface.

This repo reads public accounts only, and it does not publish anybody's follower list. Follower rows are real members of the public: on the page we sampled, 0 of 30 were verified, the median account had 100 followers, and 16 of 30 carried a written bio. So the committed sample is the response envelope with the followers array cut to 0 of 30, the row shape is documented as a schema table rather than a data dump, and the example script writes rows to a local gitignored file. More on that in Scope.

Last updated: 2026-07-20. Working against TikTok.com as of July 2026, and re-verified whenever TikTok changes their markup.

Every JSON block on this page was captured from the live API on 2026-07-20. The followers array is cut to 0 rows everywhere, and each block says so; the envelope fields shown are verbatim. The committed sample is in tiktok_follower_scraper_api_data/. Every code example calls the actual API and is runnable from tiktok_follower_scraper_api_codes/.

pip install requests
export CHOCODATA_API_KEY="your_key"     # free: 1,000 requests, one-time, no card
python tiktok_follower_scraper_api_codes/follower.py tiktok

Those three lines return this envelope, live from TikTok.com (the followers array holds 30 rows, cut to 0 here):

{
  "uniqueId": "tiktok",
  "follower_count": 94900000,
  "total": 94891522,
  "followers_count": 30,
  "has_more": true,
  "next_cursor": "1784582209",
  "followers": []
}

The total is the field people come for, and it is the exact count (94,891,522) where follower_count is the rounded display number. That total is the surface a direct call cannot reach on its own:

The follower list a direct call cannot reach, next to the managed one

That is the whole point of this repo. The rest of this page is the scope it holds itself to, the free path and where it stops, and the endpoint reference.


Contents


Scope: public accounts, personal data stays local

A follower list is a list of people, and most of them are private individuals with nothing public about them beyond the fact that they follow an account. That shapes what this repo is willing to be.

What it does: read the follower records TikTok exposes for a public account, one page of 30 at a time, plus the exact follower total and a cursor for the next page.

What it does not do: publish those rows. On the page we captured for @tiktok, 0 of 30 accounts were verified, the median account had 100 followers, and 16 of 30 carried a bio. That is a list of ordinary members of the public, so:

  • The committed sample (follower_envelope.json) is the envelope with followers cut to 0 of 30.
  • The per-row fields are documented as a schema table, and their shape is committed as follower_row_schema.json with counts only, no rows.
  • The example scripts print the envelope and per-page aggregates, and write the rows themselves to a gitignored followers.jsonl, so a follower list does not land in your terminal, a screenshot, or a CI log.

The data is yours to handle under whatever policy applies to you. This repo just keeps it off the public internet by default.


Free TikTok Follower Scraper

Reading a follower list from TikTok's own web endpoints is a two-hop job, and the free path gets through the first hop and stops dead at the second. No key, no cost:

python free_scraper/tiktok_follower_free_scraper.py tiktok

Source: free_scraper/tiktok_follower_free_scraper.py. Hop 1 fetches the profile page and reads secUid out of the rehydration blob, which works from a plain client. Hop 2 calls TikTok's own follower-list XHR with that secUid, and that is the wall:

The free follower path: hop one works, hop two returns an empty 200

Hop 2 returns HTTP 200 with a zero-byte body, 3 times out of 3. Measured on 2026-07-20: the profile page handed over secUid (293,593 bytes, 200), and the follower-list XHR then answered 200 with 0 bytes on every one of three tries. TikTok's list endpoint needs a signature its own JavaScript generates in a real browser; without it you get a 200 and nothing to parse. It is not a 403 and not an error body, so r.json() throws on an empty string and a naive wrapper logs "no followers" forever. The script prints no follower rows, because on the free path there are none.

Avoid getting blocked when scraping TikTok followers

The empty 200 above is the headline, and it is not the only thing that fails quietly on this surface. All of the following is measured against TikTok on 2026-07-20, on @tiktok.

follower_count is rounded, total is exact, in the same envelope. follower_count is 94,900,000, the display number; total is 94,891,522, the real one. Read the wrong field into a time series and you record a number that is wrong by tens of thousands, with nothing to alert on.

The page size is 30, and asking for more returns nothing. count=30 returned 30 rows; count=31, 40 and 50 each returned 502, not a truncation to 30. So 30 is the ceiling per call, and you reach the rest through the cursor.

The uncursored first page is a moving target. Calling without a cursor returned a different set of 30 accounts each time, so its aggregates move between calls: the per-page median own-follower-count was 100, 119, 192 and 236 across the uncursored calls we made. Paging forward with next_cursor is stable and does not repeat rows (0 overlapping ids across two consecutive pages), but re-fetching "page one" compares different strangers each time.

What bites you Why What it costs you
The list XHR answers 200 with an empty body TikTok's follower-list endpoint needs a browser-minted signature; a plain client gets a zero-byte 200, 3 of 3. A try/except around r.json() logs "no followers" forever, with no error and no 403 to tell you it is a wall.
follower_count is rounded, total is exact Both ship in the same envelope; follower_count is TikTok's display value, total is the real count. A trend built on follower_count is quietly wrong by tens of thousands.
count caps at 30 30 returned 30 rows; 31, 40 and 50 each returned 502. Ask for 50 and you get a 502, not 30 rows. Page with the cursor instead.
The uncursored first page churns It returned a different set of 30 accounts on each call; the cursor pages forward without repeating (0 overlap over two pages). Re-fetch "page one" to diff growth and you are comparing different people, not new followers.
Rows are personal data 0 of 30 verified, median 100 followers, 16 of 30 with a bio: ordinary members of the public. Log or publish them and you have shipped a list of private individuals.
Avatar URLs expire Each row's avatar is a signed CDN URL with an expiry parameter. Store the URL and it 403s later. Fetch the image promptly if you need it.

Using the Chocodata TikTok Follower Scraper API

The managed option, and the one this repo is built around: the Chocodata TikTok Follower Scraper API. One GET returns the page of 30 follower records that a direct call answers with an empty 200, plus the exact follower total and the next_cursor, with a ~99% success rate and no proxy management. It resolves the account's secUid for you and pages the list. Free for the first 1,000 requests.


TikTok Follower Scraper API reference

Below is the TikTok Follower Scraper API reference to get you started: authentication, the error bodies, the rate limits, and the endpoint itself.

Quickstart

curl "https://api.chocodata.com/api/v1/tiktok/follower?api_key=YOUR_KEY&username=tiktok&count=30"
import requests

r = requests.get(
    "https://api.chocodata.com/api/v1/tiktok/follower",
    params={"api_key": "YOUR_KEY", "username": "tiktok", "count": 30},
    timeout=120,
)
d = r.json()
print(d["total"], d["followers_count"], d["has_more"])
# 94891522 30 True

After running the example script, your terminal should look something like this. It prints the envelope and per-page aggregates, and keeps the rows out of view:

Running the TikTok Follower Scraper API

Get a key at chocodata.com (1,000 requests, one-time, no card).

Authentication

Pass your key as the api_key query parameter. There is no header form and no OAuth step.

https://api.chocodata.com/api/v1/tiktok/follower?api_key=YOUR_KEY&username=tiktok

Errors

Nothing below is billed: you are only charged on a 2xx.

Status error code Meaning Billed What to do
400 invalid_params Neither username nor url was supplied. The body names the issue and its path. no Fix the query string.
401 INVALID_API_KEY Key missing, unrecognised, or revoked. no Check api_key. Get one at chocodata.com.
402 INSUFFICIENT_CREDITS Balance exhausted. no Top up or upgrade.
404 item_not_found The account does not exist, was deleted, or is private. no Check the handle.
429 RATE_LIMITED Over your plan's concurrency. no Back off and retry.
502 extraction_failed TikTok did not serve this page. A count above 30 also lands here. no Retry, and keep count at 30 or below.

Unlike the per-video endpoints, this one does return a 404: because it resolves the profile first, it can tell a missing or private account apart from a page it simply could not serve. We ran https://example.com through the url parameter as a control and it returned 502, not a 200 carrying some other account under the TikTok schema.

A missing account, verbatim:

curl "https://api.chocodata.com/api/v1/tiktok/follower?api_key=YOUR_KEY&username=this_handle_does_not_exist_zzq123"
{"error": "item_not_found", "message": "account_not_found_or_private", "retryable": false}

A bad key, verbatim:

{"error": {"code": "INVALID_API_KEY", "message": "Api key not recognised."}}

A call with no account, verbatim:

{"error": "invalid_params", "issues": [{"code": "custom", "message": "username or url is required", "path": []}]}

The scripts in this repo turn each documented status into an actionable message, so a typo'd handle does not hand you a stack trace:

TikTok Follower Scraper API error handling

Rate limits and concurrency

The limit is concurrency: how many requests you may have in flight at once.

Plan Concurrent requests
Free 10
Vibe 30
Pro 50
Custom 100 to 500+

Exceed it and you get 429, not a queue. Every call is a synchronous GET: there is no webhook, callback or async job to poll. The examples use timeout=120 because this endpoint makes two upstream hops (the profile page, then the list) and the slowest call in the latency run below took 3.91s.

Follower: follower records with cursor pagination

One page of follower records for a public account, plus the exact follower total and a cursor for the next page. This is the surface a direct call cannot reach: TikTok's own follower-list XHR answers a plain client with HTTP 200 and a zero-byte body.

Param Type Required Default Description
username string one of username/url - The TikTok handle. Use this one. A leading @ is stripped for you.
url string (URL) one of username/url - Full profile URL; the handle is parsed out of it.
count integer no 30 Rows to return this call. 1 to 30. 31, 40 and 50 each returned 502, so 30 is the page size.
cursor string no - The next_cursor from a previous call. Page forward with it rather than re-fetching page one.
country string (ISO-2) no us Egress location. Pin it and keep it constant across a series.
api_key string yes - Your key. Query parameter, not a header.
curl "https://api.chocodata.com/api/v1/tiktok/follower?api_key=YOUR_KEY&username=tiktok&count=30"

Real response envelope. The 10 envelope fields are verbatim; the followers array is cut to 0 of 30 rows, because those rows are members of the public and this repo does not publish them (committed sample, same cut):

{
  "uniqueId": "tiktok",
  "secUid": "MS4wLjABAAAAv7iSuuXDJGDvJkmH_vz1qkDZYo1apxgzaxdBSeIuPiM",
  "url": "https://www.tiktok.com/@tiktok",
  "socialPlatform": "tiktok",
  "follower_count": 94900000,
  "followers": [],
  "followers_count": 30,
  "has_more": true,
  "next_cursor": "1784582209",
  "total": 94891522
}

Each entry in followers carries these 11 fields. This is a schema, not a data dump:

The 11-field follower row schema

Field Type Description
position integer 1-based index within this page.
id string Numeric account id.
uniqueId string The @handle.
nickname string Display name.
signature string | null Bio text. Present on 16 of the 30 rows sampled.
verified boolean 0 of 30 were verified on the page sampled.
secUid string Stable account identifier.
avatar string Signed CDN URL, expires.
url string Profile URL built from the handle.
followerCount integer Their own follower count. Median 100 across the sample.
followingCount integer Their own following count.

total is the field most people come for, and it is the exact one. follower_count is 94,900,000 rounded; total is 94,891,522. Same rounded-versus-exact split you see elsewhere on TikTok, different field names.

Field notes:

  1. followers_count is the number of rows in this response (30), not the account's follower total. That is total.
  2. Pagination does not repeat itself. Two consecutive pages of 30 returned 0 overlapping ids. Pass next_cursor through as cursor and keep going while has_more is true.
  3. count above 30 is a hard 502, not a truncation to 30. Ask for 31 and you get nothing.
  4. The example keeps rows out of your terminal. follower.py prints the envelope and per-page aggregates, and appends rows to a gitignored followers.jsonl only when you pass --write. Follower rows are personal data and do not belong in your scroll-back.

Runnable: tiktok_follower_scraper_api_codes/follower.py


Page through a public account's follower list

The job this endpoint is built for: walk the whole list, one page of 30 at a time, following the cursor. paginate_followers.py does that, writes the rows to a gitignored file, and prints per-page counts only:

export CHOCODATA_API_KEY="your_key"
python tiktok_follower_scraper_api_codes/paginate_followers.py tiktok --pages 2

Paging through a follower list

Each page reports its row count, how many were verified, how many carried a bio, the median own-follower-count, and the overlap with everything seen so far. That overlap was 0 across two consecutive pages, which is how you confirm the cursor is advancing rather than handing back the same rows. The rows go to followers.jsonl; the terminal never shows a handle.

Source: tiktok_follower_scraper_api_codes/paginate_followers.py


Measured latency

6 consecutive calls to /tiktok/follower from a laptop against the live API on 2026-07-20. All 6 returned 200. Each call makes two upstream hops, the profile page then the list, which is why it runs slower than the single-page endpoints.

Measured latency for the TikTok follower endpoint

Metric Value
calls made 6
200 responses 6 of 6
median 2.31s
fastest 1.53s
slowest 3.91s

6 calls from one machine on one evening is a small sample, which is why the examples set timeout=120. Reproduce it with the scripts in this repo.


License

MIT. See LICENSE.