A TikTok follower count is one field in one call. You do not need to page through a follower list, parse a profile page or stitch several requests together: give a handle to the userinfo-by-username endpoint and the response comes back with the account record plus a stats object holding the follower, following, like and video totals. This guide covers that call end to end - the exact request, every field in stats, why the numbers are current rather than cached, how to poll them into a growth curve, and what that polling costs once you are watching hundreds of accounts.
One call, four counters
The endpoint takes a single required query parameter, username, which is the public handle without the @ symbol. The base URL is https://api.primeapi.co/ and your key travels in the X-PrimeAPI-Key header on every request. That is the whole contract - no OAuth exchange, no session to keep alive. If you have not registered yet, a free PrimeApi account starts with 50 credits, which is enough to test this properly before you decide anything.
curl
curl -s "https://api.primeapi.co/userinfo-by-username?username=tiktok" \
-H "X-PrimeAPI-Key: YOUR_API_KEY"
Node.js (Axios)
const axios = require("axios");
async function getStats(username) {
const res = await axios.get(
"https://api.primeapi.co/userinfo-by-username",
{
params: { username },
headers: { "X-PrimeAPI-Key": "YOUR_API_KEY" }
}
);
const stats = res.data && res.data.stats;
if (!stats) throw new Error("No stats block for " + username);
return {
handle: res.data.user.uniqueId,
userId: res.data.user.id,
secUid: res.data.user.secUid,
followers: stats.followerCount,
following: stats.followingCount,
hearts: stats.heartCount,
videos: stats.videoCount,
balance: Number(res.headers["x-primeapi-balance"])
};
}
getStats("tiktok")
.then(r => console.log(r))
.catch(err => console.error(err.response ? err.response.data : err.message));
Python (Requests)
import requests
BASE = "https://api.primeapi.co/"
HEADERS = {"X-PrimeAPI-Key": "YOUR_API_KEY"}
def get_stats(username):
resp = requests.get(
BASE + "userinfo-by-username",
params={"username": username},
headers=HEADERS,
timeout=15,
)
resp.raise_for_status()
body = resp.json()
stats = body.get("stats") or {}
user = body.get("user") or {}
if "followerCount" not in stats:
return None
return {
"handle": user.get("uniqueId"),
"user_id": user.get("id"),
"sec_uid": user.get("secUid"),
"private": user.get("privateAccount"),
"followers": stats["followerCount"],
"following": stats["followingCount"],
"hearts": stats["heartCount"],
"videos": stats["videoCount"],
"balance": resp.headers.get("X-PrimeAPI-Balance"),
}
if __name__ == "__main__":
print(get_stats("tiktok"))
What is inside the stats object
The response splits into two blocks: user for identity and stats for the counters. The counters are integers, not formatted strings, so you can write them straight to a database column without stripping any "K" or "M" suffix - that formatting only exists in the TikTok interface.
| Field | Type | What it holds |
|---|---|---|
followerCount | int | Accounts following this profile. The number this article is about. |
followingCount | int | Accounts this profile follows. |
heartCount | int | Lifetime likes received across the profile's videos. |
heart | int | The same lifetime like total under a second key. Read heartCount and ignore this one. |
videoCount | int | Public videos on the profile. |
diggCount | int | Likes the account has given out. Commonly zero, so do not build a metric on it. |
The user block alongside it carries id (the permanent numeric ID), uniqueId (the handle), secUid, nickname, signature, verified, privateAccount, secret, createTime, the three avatar URLs and a bioLink object with a link string. Store id and secUid on the first call: handles get renamed, and a tracker keyed on uniqueId alone will silently start following the wrong account. The identity fields are covered in more depth in the walkthrough on getting TikTok user data via API, and the opaque identifier itself has its own explainer on what a TikTok secUid is and how to get one.
An abridged shape of what you parse:
{
"user": {
"id": "6881290705605477381",
"uniqueId": "tiktok",
"nickname": "TikTok",
"secUid": "MS4wLjABAAAA...",
"verified": true,
"privateAccount": false
},
"stats": {
"followerCount": 0,
"followingCount": 0,
"heartCount": 0,
"videoCount": 0,
"diggCount": 0,
"heart": 0
}
}
Why the numbers are real-time
PrimeApi does not keep a cached copy of profiles and serve it back to you. Each request is resolved upstream when it arrives, which is why the average response time sits around one second rather than being instant. The practical consequence is that two calls made a day apart give you a genuine day-over-day delta, and a call made right now reflects the profile right now - there is no shared cache window where every customer sees the same hour-old figure.
One honest caveat: TikTok's own counters are not perfectly instantaneous either. Follower totals on the platform settle over a short delay, so two calls seconds apart can legitimately return an identical number even though follows happened in between. That is upstream behaviour, not caching on this side, and it is the main reason polling faster than hourly rarely buys you anything but a bigger credit bill.
Polling patterns for tracking growth
A single call gives you a number. A growth curve needs the same call repeated on a schedule, and the shape of that schedule is where most of the engineering decisions live.
Fixed interval
The simplest tracker runs a cron job at a fixed hour, calls userinfo-by-username once per tracked handle, and appends a row. Predictable spend, predictable load, trivial to reason about. For a daily leaderboard or a weekly client report this is usually all you need. Pick a consistent time of day and stick to it - comparing a Monday morning reading against a Friday evening reading adds noise that has nothing to do with growth.
Tiered intervals
Fixed intervals waste credits on accounts that barely move. Split your watchlist into tiers instead: campaign accounts and fast-growing creators get an hourly poll, the mid-tier gets four checks a day, and the long tail gets one. Promote and demote automatically based on the last few deltas - if an account moved less than some threshold across three consecutive reads, drop it down a tier. On a list of a thousand handles this routinely cuts spend by more than half without losing resolution where it matters.
Write deltas, not duplicates
Storing every reading turns into millions of near-identical rows fast. Keep the latest value per account in a small state table and only append to the history table when the number actually changed, recording the change and the elapsed time with it. Your charts stay correct, your storage stays small, and gap detection becomes easy: a missing interval is visible because the timestamps jump.
import time
# handles: list of accounts to check this pass
def poll(handles, last_seen, sleep_between=0.7):
changes = []
for handle in handles:
row = get_stats(handle)
if not row:
continue
key = row["user_id"]
previous = last_seen.get(key)
if previous is None or previous != row["followers"]:
changes.append({
"user_id": key,
"handle": row["handle"],
"followers": row["followers"],
"delta": None if previous is None else row["followers"] - previous,
"checked_at": int(time.time()),
})
last_seen[key] = row["followers"]
time.sleep(sleep_between) # stay under 100 requests per minute
return changes
The sleep_between value is doing real work there. At 100 requests per minute the floor is 0.6 seconds per call, and a fixed 0.7 second pause keeps a single-threaded worker comfortably inside the window without needing any retry logic. If you would rather see the request and response before writing any of this, the interactive API playground runs the same call against your key in the browser.
What polling costs at scale
Every request is one credit, whether it returns a changed number or the same one as yesterday. That makes the arithmetic simple and unforgiving: cost equals accounts multiplied by polls per day. Below is what common watchlists work out to over a 30 day month, against the published PrimeApi pricing tiers.
| Accounts | Interval | Calls / day | Calls / 30 days | Tier that covers it |
|---|---|---|---|---|
| 50 | daily | 50 | 1,500 | Basic - $9.90 / 2,500 |
| 250 | daily | 250 | 7,500 | Pro - $59.90 / 50,000 |
| 1,000 | daily | 1,000 | 30,000 | Pro - $59.90 / 50,000 |
| 500 | every 6 hours | 2,000 | 60,000 | Ultra - $99.90 / 150,000 |
| 100 | hourly | 2,400 | 72,000 | Ultra - $99.90 / 150,000 |
| 1,000 | hourly | 24,000 | 720,000 | Above Enterprise - $279.00 / 500,000 |
Two things fall out of that table. First, hourly polling is roughly 24 times the price of daily polling for data that, given the upstream update delay, is often identical - reserve it for the accounts where the shape of the curve genuinely matters. Second, the rate limit is a separate ceiling from the credit balance: 100 requests per minute is 144,000 calls per day, so a thousand handles need at least ten minutes of wall clock to sweep even when credits are not the constraint. Schedule the sweep as a queue with a worker, not as a burst.
Also worth pricing in: because the profile call already returns heartCount and videoCount, you get the raw inputs for engagement work at no extra cost. Persist all four counters on every poll rather than only the follower number - going back for them later means paying for the same call twice. The method is in the guide to calculating TikTok engagement rate with an API.
Errors, balance and limits
Four failure cases come back as a JSON message rather than as data, and a long-running poller needs to branch on all of them. A missing header returns "Please sign up to primeapi.co". A wrong or unconfirmed key returns "PrimeAPI-Key is not available". Crossing 100 requests in a minute returns the per-minute limit message - back off and resume on the next window. An empty balance returns "Your balance has been exhausted...", which is the one that will quietly flatline a growth chart if nobody is watching, so alert on it rather than logging it. Reading the X-PrimeAPI-Balance header on every response and firing a warning below a threshold takes three lines and prevents that outage entirely. The full reference lives in the PrimeApi API documentation.
Requests rejected up front - no key, bad key, over the limit, no credits - are not charged. A valid lookup that finds nothing still costs its credit, so validate handles before you add them to a watchlist rather than discovering the typo 30 days into a schedule.
When a number is not enough
The counter tells you that an account gained 4,000 followers this week. It does not tell you who they are or what caused it. When you need the audience itself, the user-followers endpoint pages through the actual follower records with a count and time cursor - a far heavier operation, since it costs a credit per page rather than a credit per account, and the walkthrough on getting a TikTok follower list via API covers the paging properly. When you need the cause, pair the follower delta with that account's uploads over the same window using the secUid you already stored.
For most trackers, though, the honest answer is that the cheap call is the right call: one credit, one handle, four counters, real-time, repeated on a sensible schedule. PrimeApi is an independent service and is not affiliated with or endorsed by TikTok or ByteDance, so treat public profile data accordingly and keep only what your use case needs.
Frequently asked questions
Which endpoint returns a TikTok follower count?
userinfo-by-username. It takes one required parameter, username, and returns the profile record plus a stats object containing followerCount, followingCount, heartCount and videoCount. There is no separate counter endpoint - the follower total arrives with the profile lookup.
Is the follower count real-time or cached?
Real-time. PrimeApi does not cache responses, so the number you read is the number on the profile at the moment of the request. Average response time is around one second. TikTok itself updates these totals with a short delay of its own, so two calls seconds apart can return the same value even when new follows happened in between.
How many credits does a follower count check cost?
One credit per request, the same as every other endpoint. There is no cheaper counter-only call, so a follower check and a full profile lookup cost exactly the same - which is why you should read all four counters from the single response instead of calling again for each one. Your remaining balance comes back in the X-PrimeAPI-Balance response header.
How often can I poll a follower count?
The default rate limit is 100 requests per minute, which caps a single key at 144,000 calls per day. Credits usually run out before that ceiling does. For growth tracking, hourly is enough for fast-moving accounts and daily is enough for most, because TikTok's own counters do not update instantly.
What is the difference between followerCount and heartCount?
followerCount is how many accounts follow the profile. heartCount is the lifetime total of likes the profile's videos have received, and the response also carries a heart key holding the same total. diggCount is a separate counter for likes the account itself has given out, and it is frequently zero.
Can I track a private account's follower count?
The profile record includes privateAccount and secret flags, so you can tell a locked account from an open one and branch accordingly. Always check that the stats block is present before reading followerCount, and treat a missing or empty user block as a normal outcome for handles that were renamed, deleted or are unavailable in a region.