DEV Community

Matvey
Matvey

Posted on

A 2.4-Billion-View TikTok Came Back as 1,894,967,296 and Took the Whole Average With It

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
Enter fullscreen mode Exit fullscreen mode

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…
 …
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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   }
Enter fullscreen mode Exit fullscreen mode

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);
};
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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%
Enter fullscreen mode Exit fullscreen mode

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}'
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (2)

Collapse
 
vietautomation profile image
Viet Automation •

Great debugging write-up. The "one field typed differently from its neighbour" part is the real lesson.

Two thoughts from running similar metrics pipelines:

  1. Adding 2^32 back only recovers the true value while it's below ~4.29B. If a video ever passes that, it wraps to a small positive number and looks completely normal, so the fix can't detect it. It might be worth cross-checking playCount against another source when one exists (for example, a play count far below the like count is physically impossible) and flagging the row instead of trusting it.

  2. For vetting creators I'd consider a median (or a trimmed mean) for avgViewsRecent instead of a plain mean. One viral outlier, correct or wrapped, shouldn't decide whether a creator looks good, and the median would have stayed sane even before you found the overflow.

Also +1 to preferring the string-typed statsV2 block. Treating every big counter as a string until you parse it explicitly saves a lot of pain.

Collapse
 
junfengzhou01 profile image
Junfeng Zhou •

Nice catch. Worth checking likes, comments and shares the same way, since they come from the same JSON and a big enough post can wrap those too.