Tracking Player Social Growth During the World Cup: A Data Study
Every World Cup, a familiar scene plays out in newsrooms and agency Slack channels. Someone drops a screenshot: "this guy gained 3 million followers since the round of 16." Everyone reacts with the fire emoji. And then nothing happens, because a screenshot isn't data. There's no baseline, no daily curve, no engagement context, no way to compare him to the other twelve players having breakout tournaments at the same time. The screenshot is a fact with no method behind it.
If you're an analyst, a journalist, an agency strategist, or a brand scout, you need the method, not the screenshot. You need a repeatable system that watches a defined set of players, captures their public numbers on a schedule, and turns the raw counts into the two metrics that actually matter: growth rate and engagement rate. That system is what separates "I think this player is blowing up" from "here is the curve, here is the rate, here is how he compares to the field."
This is a practical, build-it-yourself methodology for tracking football player social media growth during the World Cup. We'll cover which players to watch and why, how to snapshot their data daily across platforms, how to compute growth rate and engagement rate correctly, and how to assemble it into a daily study you can publish or pitch. Code is in both Node.js and Python. And we'll be honest about the hard limit up front: you can only ever see public numbers, never private analytics. That's fine. Public numbers, captured well, tell most of the story.
TL;DR: To track football player social media growth during the World Cup, build a watchlist, snapshot public follower counts and recent-post engagement daily, then compute day-over-day growth rate and engagement rate (average engagement per post divided by followers). Store it as a time series and rank players by both. You can't get private analytics, only public counts, but that's enough for a solid study. Code below.
Step 1: Decide Which Players to Watch
A study is only as good as its sample. Throwing every player from every squad into the tracker wastes credits and buries your signal in noise. Be deliberate. In practice, four buckets of players are worth watching during a tournament:
- The young breakout candidates. Players under, say, 23 with modest current followings who are starting or likely to feature. These have the most upside, because their percentage growth can be enormous off a small base. They're where the next overnight surge story comes from.
- The established stars. The household names. Their absolute growth can still be huge, and they're your benchmark for what "normal big-tournament growth" looks like at the top end.
- The dark horses by nation. Players from teams that tend to overperform or capture neutral affection. A deep run by a smaller footballing nation mints new global names fast.
- Position-driven wildcards. Goalkeepers who pull off a penalty-shootout heroics, set-piece specialists, anyone whose role sets them up for a single iconic moment. These are unpredictable, so keep a few "in case" slots.
Twenty to forty players is a sensible study size. Big enough to be representative, small enough that daily snapshots across three platforms stay cheap. For each player, record their handle on each platform you're tracking.
Step 2: The Data Model
Keep it boring and flat. Every snapshot produces one row per player per platform, and you append rows forever. A row is:
timestamp, player, platform, followers, recent_post_count, total_recent_engagement
The first three columns identify the observation. followers drives growth rate. The last two, the engagement summed across a player's most recent posts and how many posts that was, drive engagement rate. That's everything you need. Resist the urge to store more; you can always derive richer metrics from these primitives later.
We'll use the SociaVault API for the data. Base URL is https://api.sociavault.com, every request authenticates with an X-API-Key header, and each request costs one credit. With a 30-player watchlist across three platforms, a daily snapshot is about 90 to 120 credits depending on how many posts you pull for engagement, which is very manageable for a multi-week study.
New here? Start free with SociaVault for 50 free credits to pilot the tracker on a smaller list before you commit to the full study. Endpoint docs live at docs.sociavault.com.
Step 3: Setup
The shared client.
// Node 18+ includes global fetch.
const API_KEY = process.env.SOCIAVAULT_API_KEY;
const BASE = "https://api.sociavault.com/v1/scrape";
async function sv(path, params = {}) {
const qs = new URLSearchParams(params).toString();
const res = await fetch(`${BASE}${path}?${qs}`, {
headers: { "X-API-Key": API_KEY },
});
if (!res.ok) throw new Error(`SociaVault ${res.status}: ${await res.text()}`);
return res.json();
}
import os
import requests
API_KEY = os.environ["SOCIAVAULT_API_KEY"]
BASE = "https://api.sociavault.com/v1/scrape"
def sv(path, params=None):
res = requests.get(
f"{BASE}{path}",
headers={"X-API-Key": API_KEY},
params=params or {},
timeout=30,
)
res.raise_for_status()
return res.json()
Step 4: Capture Followers and Recent Engagement
We need two things per player per platform: the current follower count, and the engagement on their recent posts. Let's build that for Instagram using /v1/scrape/instagram/profile for the count and /v1/scrape/instagram/posts for recent posts.
async function instagramSnapshot(username, postLimit = 12) {
const profile = await sv("/instagram/profile", { username });
const p = profile.profile || profile.user || profile.data || profile;
const followers =
p.follower_count ?? p.followers ?? p.edge_followed_by?.count ?? null;
const postsResp = await sv("/instagram/posts", {
username,
limit: postLimit,
});
const posts = postsResp.posts || postsResp.data || [];
const totalEngagement = posts.reduce(
(sum, post) =>
sum +
(post.like_count || post.likes || 0) +
(post.comment_count || post.comments || 0),
0,
);
return { followers, recentPosts: posts.length, totalEngagement };
}
def instagram_snapshot(username, post_limit=12):
profile = sv("/instagram/profile", {"username": username})
p = profile.get("profile") or profile.get("user") or profile.get("data") or profile
followers = (
p.get("follower_count")
or p.get("followers")
or (p.get("edge_followed_by") or {}).get("count")
)
posts_resp = sv("/instagram/posts", {"username": username, "limit": post_limit})
posts = posts_resp.get("posts") or posts_resp.get("data") or []
total_engagement = sum(
(post.get("like_count") or post.get("likes") or 0)
+ (post.get("comment_count") or post.get("comments") or 0)
for post in posts
)
return {"followers": followers, "recent_posts": len(posts), "total_engagement": total_engagement}
The TikTok version uses /v1/scrape/tiktok/profile, which typically returns both follower count and recent video stats in one response, so it's often a single call.
async function tiktokSnapshot(username) {
const data = await sv("/tiktok/profile", { username });
const stats = data.stats || data.user?.stats || {};
const followers = stats.followerCount ?? data.follower_count ?? null;
const videos = data.videos || data.data?.videos || [];
const totalEngagement = videos.reduce((sum, v) => {
const s = v.stats || {};
return (
sum + (s.diggCount || 0) + (s.commentCount || 0) + (s.shareCount || 0)
);
}, 0);
return { followers, recentPosts: videos.length, totalEngagement };
}
def tiktok_snapshot(username):
data = sv("/tiktok/profile", {"username": username})
stats = data.get("stats") or (data.get("user") or {}).get("stats") or {}
followers = stats.get("followerCount") or data.get("follower_count")
videos = data.get("videos") or (data.get("data") or {}).get("videos") or []
total_engagement = 0
for v in videos:
s = v.get("stats") or {}
total_engagement += (s.get("diggCount") or 0) + (s.get("commentCount") or 0) + (s.get("shareCount") or 0)
return {"followers": followers, "recent_posts": len(videos), "total_engagement": total_engagement}
For X, /v1/scrape/twitter/profile gives followers and /v1/scrape/twitter/user-tweets gives recent posts to sum engagement from. The pattern is identical to Instagram, so you can copy that structure and swap the endpoints and field names.
Step 5: Run the Daily Snapshot
Tie the platform functions into a single daily run over the watchlist, and append the rows to your store.
import { appendFile } from "node:fs/promises";
const watchlist = [
{ name: "Player A", instagram: "player_a", tiktok: "player_a" },
{ name: "Player B", instagram: "player_b", tiktok: "player_b" },
// ...
];
async function dailySnapshot(watchlist, file = "growth.csv") {
const ts = new Date().toISOString();
for (const p of watchlist) {
const tasks = [];
if (p.instagram) tasks.push(["instagram", instagramSnapshot(p.instagram)]);
if (p.tiktok) tasks.push(["tiktok", tiktokSnapshot(p.tiktok)]);
for (const [platform, promise] of tasks) {
try {
const s = await promise;
const line = `${ts},${p.name},${platform},${s.followers},${s.recentPosts},${s.totalEngagement}\n`;
await appendFile(file, line);
} catch (e) {
console.error(` ${p.name}/${platform} failed: ${e.message}`);
}
}
}
console.log(`Snapshot complete at ${ts}`);
}
dailySnapshot(watchlist);
import csv, os
from datetime import datetime, timezone
watchlist = [
{"name": "Player A", "instagram": "player_a", "tiktok": "player_a"},
{"name": "Player B", "instagram": "player_b", "tiktok": "player_b"},
# ...
]
def daily_snapshot(watchlist, file="growth.csv"):
ts = datetime.now(timezone.utc).isoformat()
exists = os.path.exists(file)
with open(file, "a", newline="") as f:
w = csv.writer(f)
if not exists:
w.writerow(["ts", "player", "platform", "followers", "recent_posts", "total_engagement"])
for p in watchlist:
jobs = []
if p.get("instagram"):
jobs.append(("instagram", lambda u=p["instagram"]: instagram_snapshot(u)))
if p.get("tiktok"):
jobs.append(("tiktok", lambda u=p["tiktok"]: tiktok_snapshot(u)))
for platform, fn in jobs:
try:
s = fn()
w.writerow([ts, p["name"], platform, s["followers"], s["recent_posts"], s["total_engagement"]])
except Exception as e:
print(f" {p['name']}/{platform} failed: {e}")
print(f"Snapshot complete at {ts}")
daily_snapshot(watchlist)
Schedule this once a day at a consistent time, the same hour every day matters, because comparing a morning snapshot to an evening one inflates your growth numbers. A cron job, a GitHub Action on a schedule, or a cloud scheduler all work. Consistency is the only rule that matters.
Step 6: Compute Growth Rate and Engagement Rate
Now the analysis. Two metrics carry the study.
Growth rate is day-over-day percentage change in followers. It answers "how fast is this player's audience expanding right now."
Engagement rate is average engagement per post divided by follower count, expressed as a percentage. It answers "how much does this player's audience actually care," which is the question that separates a big follower number from a valuable one. A player with 5 million passive followers can be worth less to a brand than one with 800,000 who all show up in the comments.
def growth_rate(prev_followers, cur_followers):
if not prev_followers:
return None
return round((cur_followers - prev_followers) / prev_followers * 100, 2)
def engagement_rate(total_engagement, recent_posts, followers):
if not followers or not recent_posts:
return None
avg_engagement_per_post = total_engagement / recent_posts
return round(avg_engagement_per_post / followers * 100, 2)
def analyze(history):
"""history: rows for ONE player+platform, sorted by ts ascending."""
results = []
for prev, cur in zip(history, history[1:]):
results.append({
"player": cur["player"],
"platform": cur["platform"],
"date": cur["ts"][:10],
"followers": cur["followers"],
"growth_rate_pct": growth_rate(prev["followers"], cur["followers"]),
"engagement_rate_pct": engagement_rate(
cur["total_engagement"], cur["recent_posts"], cur["followers"]
),
})
return results
function growthRate(prev, cur) {
if (!prev) return null;
return Math.round(((cur - prev) / prev) * 10000) / 100;
}
function engagementRate(totalEngagement, recentPosts, followers) {
if (!followers || !recentPosts) return null;
const avgPerPost = totalEngagement / recentPosts;
return Math.round((avgPerPost / followers) * 10000) / 100;
}
Rank your players by growth_rate_pct and you have the daily "who's surging" leaderboard. Rank by engagement_rate_pct and you find the players whose audiences are genuinely invested, not just numerous. The most interesting players in any study are the ones high on both lists at once.
Step 7: Turn It Into a Study
A pile of numbers isn't a study. A study has a narrative the numbers support. A few framings that consistently work for this kind of data:
- The surge leaderboard. Daily ranking by growth rate. Simple, shareable, and it updates itself. Great for a recurring tournament column.
- Growth vs engagement quadrants. Plot players with growth rate on one axis and engagement rate on the other. The top-right quadrant, fast-growing and highly engaged, is where the real breakout stars live. This single chart is often the centerpiece of a good study.
- The before-and-after. For each breakout player, show the flat baseline, the ramp, and the new plateau. Pair it with the match that caused it and you've got a clean cause-and-effect story.
- Platform personality. Compare a player's growth across platforms. Surging on TikTok but flat on Instagram tells a different story (clip-driven virality) than the reverse (brand and lifestyle appeal).
The Honest Limits
Three things you must be straight about, especially if you're publishing:
- You only get public numbers. Follower counts and public engagement on public posts. You cannot see impressions, reach, watch time, audience demographics, or anything that lives in a player's private creator dashboard. Every metric in this study is derived from what a logged-out visitor can see. That's a real constraint, and good practice is to state it in your methodology note so readers know exactly what the numbers represent.
- Engagement rate from recent posts is an estimate. You're sampling the last dozen or so posts, not a player's entire history. A single viral post in the window can skew the average. Note your sample size and consider reporting a median alongside the mean.
- Cadence sets resolution, and consistency is everything. Daily snapshots show daily change. If you miss a day, interpolate carefully or flag the gap. And always snapshot at the same time of day, or your growth rates become noise.
On alternatives: the platforms' official APIs can return some of this data with sanctioned access, but they come with approval processes, per-platform integration work, and rate limits, and they still won't hand you another account's private analytics. The reason to use a unified API is to snapshot every player on every platform with one consistent script, which is exactly what a daily study needs.
Bringing It Together
Tracking football player social media growth during the World Cup isn't complicated, it's disciplined. Pick a deliberate watchlist, snapshot public followers and recent-post engagement at the same time every day, store it as a flat time series, and compute growth rate and engagement rate from those primitives. Rank, chart, and the story tells itself. The difference between a screenshot and a study is nothing more than method plus consistency, and now you have both.
Set your tracker running before the group stage and you'll own the definitive growth dataset for the entire tournament while everyone else is still posting screenshots.
Start free with SociaVault and use your 50 free credits to pilot a watchlist today. Pair this with the story behind the numbers in our piece on the World Cup follower surge, and the docs have every profile and posts endpoint you'll need to build the full study.
Found this helpful?
Share it with others who might benefit
Ready to Try SociaVault?
Start extracting social media data with our powerful API. No credit card required.