Overview
This workflow tracks TikTok influencer metrics weekly for active and prospective campaign partners. It records follower counts, engagement rates, posting frequency, and content themes, then compares against the previous week to surface significant changes. The output helps brand managers identify creators whose metrics are growing, declining, or showing fraud signals.
Trigger
Cron schedule (every Monday at 8 AM UTC)
Schedule
Runs every Monday at 8 AM UTC
Workflow Steps
Load influencer roster
Read the list of TikTok creators being tracked from configuration or CRM.
Fetch current metrics
Call the Scavio TikTok API for each creator to get current followers, video count, and recent engagement.
Compare against previous week
Load stored metrics from last week and compute deltas for followers, engagement rate, and posting frequency.
Flag significant changes
Identify creators with unusual follower spikes, engagement drops, or posting frequency changes.
Generate weekly report
Compile metrics and changes into a structured report with creator-level summaries.
Update baseline
Store current metrics as the new baseline for next week's comparison.
Python Implementation
import requests
import json
from pathlib import Path
from datetime import datetime
API_KEY = "your_scavio_api_key"
BASE_URL = "https://api.scavio.dev/api/v1/tiktok"
def fetch_creator_metrics(username: str) -> dict:
res = requests.post(
f"{BASE_URL}/user",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"username": username},
timeout=15,
)
res.raise_for_status()
data = res.json()
videos = data.get("videos", [])
total_views = sum(v.get("views", 0) for v in videos)
total_likes = sum(v.get("likes", 0) for v in videos)
avg_er = (total_likes / total_views) if total_views > 0 else 0
return {
"username": username,
"followers": data.get("followers", 0),
"following": data.get("following", 0),
"video_count": len(videos),
"avg_engagement_rate": round(avg_er, 4),
"total_views_recent": total_views,
"checked_at": datetime.utcnow().isoformat(),
}
def run():
roster = json.loads(Path("influencer_roster.json").read_text())
baseline_path = Path("influencer_baseline.json")
baseline = json.loads(baseline_path.read_text()) if baseline_path.exists() else {}
current_metrics = {}
changes = []
for creator in roster:
username = creator["username"]
metrics = fetch_creator_metrics(username)
current_metrics[username] = metrics
prev = baseline.get(username)
if prev:
follower_change = metrics["followers"] - prev.get("followers", 0)
follower_pct = (follower_change / prev["followers"] * 100) if prev.get("followers") else 0
er_change = metrics["avg_engagement_rate"] - prev.get("avg_engagement_rate", 0)
if abs(follower_pct) > 10 or abs(er_change) > 0.02:
changes.append({
"username": username,
"follower_change": follower_change,
"follower_pct": round(follower_pct, 1),
"er_change": round(er_change, 4),
"flag": "spike" if follower_pct > 20 else "drop" if follower_pct < -10 else "shift",
})
baseline_path.write_text(json.dumps(current_metrics, indent=2))
report = {
"date": datetime.utcnow().strftime("%Y-%m-%d"),
"creators_tracked": len(roster),
"significant_changes": changes,
"metrics": current_metrics,
}
Path(f"influencer_report_{report['date']}.json").write_text(json.dumps(report, indent=2))
print(f"Tracked {len(roster)} creators, {len(changes)} significant changes")
for c in changes:
print(f" {c['username']}: {c['flag']} ({c['follower_pct']:+.1f}% followers, {c['er_change']:+.4f} ER)")
if __name__ == "__main__":
run()JavaScript Implementation
const API_KEY = "your_scavio_api_key";
const BASE_URL = "https://api.scavio.dev/api/v1/tiktok";
async function fetchCreatorMetrics(username) {
const res = await fetch(`${BASE_URL}/user`, {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}`, "content-type": "application/json" },
body: JSON.stringify({ username }),
});
if (!res.ok) throw new Error(`scavio ${res.status}`);
const data = await res.json();
const videos = data.videos ?? [];
const totalViews = videos.reduce((s, v) => s + (v.views ?? 0), 0);
const totalLikes = videos.reduce((s, v) => s + (v.likes ?? 0), 0);
return {
username,
followers: data.followers ?? 0,
following: data.following ?? 0,
videoCount: videos.length,
avgEngagementRate: totalViews > 0 ? Math.round((totalLikes / totalViews) * 10000) / 10000 : 0,
totalViewsRecent: totalViews,
checkedAt: new Date().toISOString(),
};
}
async function run() {
const fs = await import("fs/promises");
const roster = JSON.parse(await fs.readFile("influencer_roster.json", "utf8"));
let baseline = {};
try { baseline = JSON.parse(await fs.readFile("influencer_baseline.json", "utf8")); } catch {}
const currentMetrics = {};
const changes = [];
for (const creator of roster) {
const metrics = await fetchCreatorMetrics(creator.username);
currentMetrics[creator.username] = metrics;
const prev = baseline[creator.username];
if (prev) {
const followerChange = metrics.followers - (prev.followers ?? 0);
const followerPct = prev.followers ? (followerChange / prev.followers) * 100 : 0;
const erChange = metrics.avgEngagementRate - (prev.avgEngagementRate ?? 0);
if (Math.abs(followerPct) > 10 || Math.abs(erChange) > 0.02) {
changes.push({
username: creator.username,
followerChange,
followerPct: Math.round(followerPct * 10) / 10,
erChange: Math.round(erChange * 10000) / 10000,
flag: followerPct > 20 ? "spike" : followerPct < -10 ? "drop" : "shift",
});
}
}
}
await fs.writeFile("influencer_baseline.json", JSON.stringify(currentMetrics, null, 2));
const date = new Date().toISOString().slice(0, 10);
const report = { date, creatorsTracked: roster.length, significantChanges: changes, metrics: currentMetrics };
await fs.writeFile(`influencer_report_${date}.json`, JSON.stringify(report, null, 2));
console.log(`Tracked ${roster.length} creators, ${changes.length} significant changes`);
for (const c of changes) console.log(` ${c.username}: ${c.flag} (${c.followerPct > 0 ? "+" : ""}${c.followerPct}% followers)`);
}
run();Platforms Used
TikTok
Trending video, creator, and product discovery