Vetting TikTok influencers for brand partnerships requires analyzing multiple signals: engagement rate, posting consistency, follower growth patterns, and content relevance. Manual vetting takes hours per creator. This tutorial builds an automated scoring system using the Scavio TikTok API that fetches creator profiles and recent videos, calculates engagement metrics, and outputs a composite score. Each API call costs 1 credit ($0.005), and a full creator analysis uses 2-3 credits.
Prerequisites
- Python 3.9+ installed
- requests library installed
- A Scavio API key from scavio.dev
- TikTok usernames or secUids of influencers to evaluate
Walkthrough
Step 1: Fetch creator profile data
Get the creator profile including follower count, following count, total likes, and video count. This is the foundation for all scoring metrics.
import requests, os
API_KEY = os.environ['SCAVIO_API_KEY']
TIKTOK_URL = 'https://api.scavio.dev/api/v1/tiktok'
def get_profile(username: str) -> dict:
resp = requests.post(f'{TIKTOK_URL}/user/info',
headers={'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'},
json={'username': username})
resp.raise_for_status()
user = resp.json().get('data', {}).get('user', {})
stats = resp.json().get('data', {}).get('stats', {})
return {
'username': user.get('uniqueId', ''),
'nickname': user.get('nickname', ''),
'verified': user.get('verified', False),
'followers': stats.get('followerCount', 0),
'following': stats.get('followingCount', 0),
'likes': stats.get('heartCount', 0),
'videos': stats.get('videoCount', 0)
}
profile = get_profile('example_creator')
print(f'{profile["username"]}: {profile["followers"]:,} followers, {profile["videos"]} videos')Step 2: Fetch recent videos for engagement analysis
Get the creator's recent videos to calculate actual engagement rates. Profile-level stats can be misleading without per-video analysis.
def get_recent_videos(username: str, count: int = 20) -> list:
resp = requests.post(f'{TIKTOK_URL}/user/posts',
headers={'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'},
json={'username': username, 'count': count, 'cursor': 0})
resp.raise_for_status()
videos = resp.json().get('data', {}).get('videos', [])
return [{
'id': v.get('id', ''),
'desc': v.get('desc', ''),
'plays': v.get('stats', {}).get('playCount', 0),
'likes': v.get('stats', {}).get('diggCount', 0),
'comments': v.get('stats', {}).get('commentCount', 0),
'shares': v.get('stats', {}).get('shareCount', 0),
'create_time': v.get('createTime', 0)
} for v in videos]
videos = get_recent_videos('example_creator')
print(f'Fetched {len(videos)} recent videos')Step 3: Calculate engagement and consistency scores
Compute engagement rate per video, average engagement, posting frequency, and content consistency. These metrics form the scoring components.
import statistics
from datetime import datetime
def calculate_metrics(profile: dict, videos: list) -> dict:
if not videos:
return {'engagement_rate': 0, 'consistency': 0, 'avg_plays': 0}
followers = max(profile['followers'], 1)
# Per-video engagement rate
engagement_rates = []
for v in videos:
total_engagement = v['likes'] + v['comments'] + v['shares']
rate = (total_engagement / max(v['plays'], 1)) * 100
engagement_rates.append(rate)
avg_engagement = statistics.mean(engagement_rates)
engagement_stdev = statistics.stdev(engagement_rates) if len(engagement_rates) > 1 else 0
# Posting consistency (days between posts)
timestamps = sorted([v['create_time'] for v in videos if v['create_time']])
if len(timestamps) > 1:
gaps = [(timestamps[i+1] - timestamps[i]) / 86400
for i in range(len(timestamps) - 1)]
avg_gap = statistics.mean(gaps)
gap_stdev = statistics.stdev(gaps) if len(gaps) > 1 else avg_gap
else:
avg_gap = 30
gap_stdev = 30
avg_plays = statistics.mean([v['plays'] for v in videos])
return {
'avg_engagement_rate': round(avg_engagement, 2),
'engagement_consistency': round(100 - min(engagement_stdev * 10, 100), 1),
'avg_days_between_posts': round(avg_gap, 1),
'posting_consistency': round(100 - min(gap_stdev * 10, 100), 1),
'avg_plays': int(avg_plays),
'likes_to_followers': round((statistics.mean([v['likes'] for v in videos]) / followers) * 100, 2)
}Step 4: Build the composite influencer score
Combine all metrics into a single 0-100 score with weighted components. Higher scores indicate better partnership potential.
def score_influencer(profile: dict, metrics: dict) -> dict:
# Engagement score (0-40 points)
eng_rate = metrics['avg_engagement_rate']
engagement_score = min(eng_rate * 5, 40) # 8%+ engagement = max score
# Consistency score (0-20 points)
consistency_score = (metrics['engagement_consistency'] +
metrics['posting_consistency']) / 10
consistency_score = min(consistency_score, 20)
# Reach score (0-20 points)
followers = profile['followers']
if followers >= 1000000:
reach_score = 20
elif followers >= 100000:
reach_score = 15
elif followers >= 10000:
reach_score = 10
elif followers >= 1000:
reach_score = 5
else:
reach_score = 2
# Authenticity score (0-20 points)
# High following/follower ratio = potential fake engagement
ratio = profile['following'] / max(profile['followers'], 1)
auth_score = 20 if ratio < 0.1 else 15 if ratio < 0.3 else 10 if ratio < 0.5 else 5
total = round(engagement_score + consistency_score + reach_score + auth_score, 1)
return {
'total_score': total,
'engagement_score': round(engagement_score, 1),
'consistency_score': round(consistency_score, 1),
'reach_score': reach_score,
'authenticity_score': auth_score,
'grade': 'A' if total >= 80 else 'B' if total >= 60 else 'C' if total >= 40 else 'D'
}Step 5: Run the full scoring pipeline
Combine all steps into a single function that takes a username and returns a complete influencer report. Batch score multiple creators.
import time
def full_influencer_report(username: str) -> dict:
profile = get_profile(username)
videos = get_recent_videos(username, count=20)
metrics = calculate_metrics(profile, videos)
scores = score_influencer(profile, metrics)
return {
'profile': profile,
'metrics': metrics,
'scores': scores,
'credits_used': 2 # 1 for profile + 1 for videos
}
def batch_score(usernames: list) -> list:
reports = []
for username in usernames:
report = full_influencer_report(username)
reports.append(report)
p = report['profile']
s = report['scores']
print(f'{p["username"]}: {s["total_score"]}/100 ({s["grade"]}) - '
f'{p["followers"]:,} followers, '
f'{report["metrics"]["avg_engagement_rate"]}% engagement')
time.sleep(0.3)
total_credits = sum(r['credits_used'] for r in reports)
print(f'\nTotal credits: {total_credits} (${total_credits * 0.005:.2f})')
return sorted(reports, key=lambda r: r['scores']['total_score'], reverse=True)
# Score multiple influencers:
# ranked = batch_score(['creator1', 'creator2', 'creator3'])Python Example
import os, requests, statistics, time
API_KEY = os.environ['SCAVIO_API_KEY']
TT = 'https://api.scavio.dev/api/v1/tiktok'
def tiktok(endpoint, body):
return requests.post(f'{TT}/{endpoint}',
headers={'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json'},
json=body).json()
def score_creator(username):
profile = tiktok('user/info', {'username': username}).get('data', {})
stats = profile.get('stats', {})
videos_data = tiktok('user/posts', {'username': username, 'count': 20, 'cursor': 0})
videos = videos_data.get('data', {}).get('videos', [])
if not videos:
return {'username': username, 'score': 0}
eng_rates = [(v['stats']['diggCount'] + v['stats']['commentCount']) /
max(v['stats']['playCount'], 1) * 100 for v in videos
if v.get('stats')]
avg_eng = statistics.mean(eng_rates) if eng_rates else 0
return {'username': username, 'followers': stats.get('followerCount', 0),
'engagement': round(avg_eng, 2), 'score': min(round(avg_eng * 10), 100)}
result = score_creator('example_creator')
print(f'{result["username"]}: {result["score"]}/100 ({result["engagement"]}% engagement)')JavaScript Example
const API_KEY = process.env.SCAVIO_API_KEY;
const TT = 'https://api.scavio.dev/api/v1/tiktok';
async function tiktokApi(endpoint, body) {
const resp = await fetch(`${TT}/${endpoint}`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
return resp.json();
}
async function scoreCreator(username) {
const profile = await tiktokApi('user/info', { username });
const stats = profile.data?.stats || {};
const postsData = await tiktokApi('user/posts', { username, count: 20, cursor: 0 });
const videos = postsData.data?.videos || [];
const engRates = videos.map(v => {
const s = v.stats || {};
return ((s.diggCount || 0) + (s.commentCount || 0)) / Math.max(s.playCount || 1, 1) * 100;
});
const avgEng = engRates.reduce((a, b) => a + b, 0) / Math.max(engRates.length, 1);
console.log(`${username}: ${Math.min(Math.round(avgEng * 10), 100)}/100 (${avgEng.toFixed(2)}% eng)`);
}
scoreCreator('example_creator').catch(console.error);Expected Output
example_creator: 87,432 followers, 156 videos
Fetched 20 recent videos
example_creator: 72.5/100 (B)
Engagement: 32.5/40 (6.5% avg rate)
Consistency: 16.8/20
Reach: 10/20
Authenticity: 15/20
Total credits: 2 ($0.01)