I run a visa agency in Bali, which means I get asked to vet creators. An agency sends over "a Bali travel influencer, 500k followers, great engagement", and someone has to decide whether that is worth a free visa extension and a hotel night. Follower counts are useless for that — they are the one number you can buy. Views on the last ten posts are harder to fake, so that is the column I read.
My own Actor produces it: TikTok Influencer Scraper takes usernames and returns one row per creator, with avgViewsRecent and viewsToFollowersRatio alongside the usual counters. Last night I ran ten well-known accounts through it as a sanity check — usernames in, 35 seconds, ten rows, $0.0085 — and one row came back like this:
user followers avgViewsRecent viewsToFollowersRatio zachking 86,759,854 -142,605,608 -1.644 Negative average views. A ratio saying this creator gets minus 164% of his audience watching. Here is the video list that produced it, trimmed to the play counts:
68,000,000 a message to all AI -1,894,967,296 They rejected my application to Hogwarts but I still found a way… 1,700,000 Can I do extreme yoga? 🧘 5,700,000 my honest thoughts about the new flying roadster 9,500,000 AI ❌ Zach King ✅ 20,700,000 when you give your friend the aux cord… … One number out of 113 in that run was negative, and it dragged a 12-video mean 158 million views below zero.
The number is not corrupt, it is wrapped
-1,894,967,296 looks like garbage until you add 2³² to it:
4,294,967,296 − 1,894,967,296 = 2,400,000,000 That is a signed 32-bit integer overflow, and the result is not a random large number — it is exactly 2.4 billion, which is the view count this particular video is famous for. It is the 2019 Hogwarts broomstick illusion, one of the most-watched TikToks ever made. TikTok's own JSON stores the counter in a field that tops out at 2,147,483,647, and the two or three videos on the platform that have crossed that line come out the other side negative.
What makes this worth writing down is which half of the response wraps and which does not. My scraper reads two things per creator: the profile page, which carries a statsV2 block, and the oEmbed-style page that carries the recent video list. The profile counters are strings in that JSON:
"statsV2": { "followerCount": "86760108", "heartCount": "1304825113", "videoCount": "617" } "stats": { "followerCount": 86800000, "heartCount": 1300000000, "videoCount": 617 } Both blocks are in the same page. statsV2 is strings and exact; the older stats block is numbers and pre-rounded, which is its own reason to prefer V2. Strings do not overflow: Khaby Lame's row carries likes: 2692020130, comfortably past the 32-bit ceiling and perfectly intact, because it arrived as "2692020130" and went through Number(). The video list, by contrast, carries playCount as a plain JSON number, already wrapped by whatever service serialised it. The bug was never in my maths; it was one field, in one of two endpoints, typed differently from its neighbour.
My fix is four lines and the kind of thing I would have called paranoid a week ago:
const n = (x) => { const v = Number(x); return Number.isFinite(v) ? v : null; }; // TikTok's embed JSON ships view counts as a SIGNED 32-bit integer: anything past // 2,147,483,647 comes back negative (2,400,000,000 arrives as -1,894,967,296). const u32 = (x) => { const v = n(x); return v == null ? null : (v < 0 && v >= -2147483648 ? v + 4294967296 : v); }; Applied to playCount, and to diggCount, shareCount, commentCount and heart in the compatibility output — any of those could cross the line on a big enough account. Before and after, same creator, same twelve videos:
avgViewsRecent ratio max playCount before (0.1.37) -142,605,608 -1.644 -1,894,967,296 after (0.1.39) 215,308,333 2.482 2,400,000,000 If you are parsing TikTok yourself: assume every numeric counter in an embed payload is int32, and treat a negative view count as a signal to add 2³², not as a parse failure to log and drop.
Fixing it made the column worse, honestly
Look at that "after" number again: 215 million average recent views against 86.8 million followers, a ratio of 2.48. Technically correct, completely useless as a vetting signal.
The median of those twelve videos is 12.7 million. The mean is 17 times higher, because one 2019 viral is sitting in the list. That is not a Zach King quirk; it is what a mean does to a view distribution. Across the ten profiles in my run:
user mean median mean/median redbull 2,807,284 226,700 12.38 bellapoarch 2,265,430 570,400 3.97 gymshark 353,953 91,200 3.88 charlidamelio 3,318,110 2,000,000 1.66 duolingo 1,711,290 1,100,000 1.56 nasa 473,281 333,000 1.42 zachking 16,700,000 12,400,000 1.35 spotify 6,403 5,669 1.13 airbnb 8,809 8,274 1.06 mrbeast 16,110,000 17,600,000 0.92 Half the panel has a mean more than 1.5× its median. Red Bull's mean says 2.8 million views per post; its typical post gets 227,000. If you are filtering a list of creators on avgViewsRecent, you are ranking partly by "has one old hit", which is the opposite of what you want to know.
So read recentVideos[].playCount yourself and take the median. It is three lines and it is strictly better than the column I ship:
import statistics for row in items: views = [v["playCount"] for v in row.get("recentVideos", []) if v["playCount"]] row["medianViews"] = statistics.median(views) if views else None MrBeast is the tell that this is a real distribution and not an artefact: he is the only account in the panel whose mean is below its median, because he has no outlier — every recent post lands between 5.7 and 29.8 million. Consistency shows up as mean ≈ median. That is a signal in itself.
"Recent" is not a time window
The other thing I found while chasing the negative number. The video list a public TikTok profile hands out is "the latest posts", except pinned posts ride along at the top, so the list is neither ten items long nor chronological:
user n newest oldest span out of order duolingo 11 2026-11-02 2025-06-10 510d yes gymshark 13 2026-11-01 2026-07-09 115d yes zachking 12 2026-10-31 2019-12-09 2517d yes bellapoarch 13 2026-10-25 2026-05-01 177d yes spotify 10 2026-11-02 2026-10-29 3d no charlidamelio 10 2026-11-02 2026-10-26 6d no Seven of ten lists were out of chronological order, and five spanned more than 100 days. Zach King's "recent" videos span nearly seven years. If you need a real window — views in the last 30 days — you have to date the posts and filter. The timestamp is free, by the way: the first 32 bits of a TikTok video ID are a Unix timestamp, so int(video_id) >> 32 gives you the publish time without another request. That is how I built the table above.
The lifetime engagement rate is a different number than you think
One more, because it is the field people quote at me most. engagementRate is average likes per video as a share of followers — and "average likes per video" is lifetime, from the profile's total like count divided by its total video count. On an account with history, it describes the past, not now:
user engagementRate median recent views / followers bellapoarch 17.62% 0.62% airbnb 5.45% 0.99% gymshark 4.37% 1.36% duolingo 2.39% 5.98% nasa 8.61% 15.46% Bella Poarch's 17.6% is real arithmetic: 150 videos, an enormous lifetime like count, 91 million followers. It is also 28 times her current reach. Her typical recent post is seen by 0.6% of her audience. Both numbers are true; only one of them predicts what a brand gets next month. NASA runs the other way — a 62-video account opened in July 2026, where recent reach is nearly double the lifetime rate because there is barely any past to average over.
My rule now: engagementRate for "was this account ever good", median recent views over followers for "is it good today", and a flag when the two disagree by more than ~3×.
One more trap, then the call
Play counts arrive at display precision, and only play counts. In the 113 videos I pulled, everything below 10,000 was exact (3,010, 4,201, 6,822), everything from 10k to 1M sat on a 100-grid, and everything above 1M sat on a 100,000-grid — 1,100,000, 12,400,000, 2,400,000,000. Followers and likes are exact to the digit (18,381,348). So the error bar on a 1.1M-view post is ±50,000, and a week-over-week view delta under that is noise. Do not build a growth alert on it.
curl -s -X POST \ "https://api.apify.com/v2/acts/lergassy~tiktok-influencer-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"profiles":["gymshark","redbull","nasa"], "includeRecentVideos": true, "minFollowers": 10000, "maxFollowers": 100000}' Filtered-out and unreadable profiles are free, which is the point of the follower window: send ten thousand usernames from a hashtag export and pay only for the ones inside your range. Each run also appends one free type: "notice" row with the billing summary — filter on type != "notice" or it will turn up in your spreadsheet as a creator called undefined.
Where this tool is the wrong choice: anything needing a login. Follower lists, following lists, comments, a creator's full back catalogue, Stories — none of that is on a public profile page, so none of it is here. There is no browser and no login, which is also why ten profiles take 35 seconds.
And the line I should have had in my own tests from day one: a negative view count is not bad data, it is good data in the wrong integer width.