Picking the wrong TikTok creator for a brand partnership wastes budget and damages credibility. Vanity metrics like follower count hide fake engagement, inconsistent posting, and off-brand content. This tutorial builds a creator vetting pipeline using the Scavio TikTok API to check real engagement rates, content consistency, audience signals, and posting frequency. Each API call costs $0.005 and gives you data that influencer platforms charge hundreds per month to access.
Prerequisites
- Python 3.9+ installed
- requests library installed
- A Scavio API key from scavio.dev
- A list of TikTok creator usernames to evaluate
Walkthrough
Step 1: Pull creator profile and stats
Use the TikTok profile endpoint to get a creator's follower count, following count, total likes, and bio. The follower/following ratio is a quick authenticity signal.
import os, requests, time
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
TT_URL = 'https://api.scavio.dev/api/v1/tiktok'
TT_H = {'Authorization': f'Bearer {SCAVIO_KEY}', 'Content-Type': 'application/json'}
def get_creator_profile(username: str) -> dict:
"""Get creator profile data."""
resp = requests.post(f'{TT_URL}/profile', headers=TT_H,
json={'username': username})
data = resp.json().get('data', {})
user = data.get('user', {})
stats = data.get('stats', {})
return {
'username': username,
'nickname': user.get('nickname', ''),
'bio': user.get('signature', ''),
'verified': user.get('verified', False),
'sec_user_id': user.get('secUid', ''),
'followers': stats.get('followerCount', 0),
'following': stats.get('followingCount', 0),
'total_likes': stats.get('heartCount', 0),
'total_videos': stats.get('videoCount', 0),
'follower_ratio': stats.get('followerCount', 0) / max(stats.get('followingCount', 1), 1),
}
profile = get_creator_profile('charlidamelio')
print(f"Creator: @{profile['username']} ({profile['nickname']})")
print(f"Followers: {profile['followers']:,}")
print(f"Total likes: {profile['total_likes']:,}")
print(f"Videos: {profile['total_videos']}")
print(f"Follower ratio: {profile['follower_ratio']:.0f}:1")
print(f"Verified: {profile['verified']}")Step 2: Analyze recent content and engagement rates
Pull the creator's recent posts and calculate real engagement rates. Average engagement below 2% on TikTok is a red flag for fake followers.
def analyze_content(username: str, sec_user_id: str) -> dict:
"""Analyze a creator's recent content for engagement quality."""
resp = requests.post(f'{TT_URL}/user/posts', headers=TT_H,
json={'sec_user_id': sec_user_id, 'count': 20})
posts = resp.json().get('data', {}).get('videos', [])
if not posts:
return {'error': 'No posts found'}
engagement_rates = []
view_counts = []
for post in posts:
stats = post.get('stats', {})
views = stats.get('playCount', 0)
likes = stats.get('diggCount', 0)
comments = stats.get('commentCount', 0)
shares = stats.get('shareCount', 0)
if views > 0:
eng_rate = (likes + comments + shares) / views
engagement_rates.append(eng_rate)
view_counts.append(views)
avg_engagement = sum(engagement_rates) / len(engagement_rates) if engagement_rates else 0
avg_views = sum(view_counts) / len(view_counts) if view_counts else 0
view_consistency = min(view_counts) / max(view_counts) if view_counts and max(view_counts) > 0 else 0
return {
'posts_analyzed': len(posts),
'avg_engagement_rate': avg_engagement,
'avg_views': avg_views,
'min_views': min(view_counts) if view_counts else 0,
'max_views': max(view_counts) if view_counts else 0,
'view_consistency': view_consistency, # Higher = more consistent
'engagement_quality': 'good' if avg_engagement > 0.05 else 'average' if avg_engagement > 0.02 else 'low',
}
# Need sec_user_id from profile step
if profile.get('sec_user_id'):
content = analyze_content(profile['username'], profile['sec_user_id'])
print(f"Content Analysis: @{profile['username']}")
print(f" Posts analyzed: {content['posts_analyzed']}")
print(f" Avg engagement: {content['avg_engagement_rate']:.1%}")
print(f" Avg views: {content['avg_views']:,.0f}")
print(f" View consistency: {content['view_consistency']:.0%}")
print(f" Quality: {content['engagement_quality']}")Step 3: Check brand safety and content relevance
Search the creator's video descriptions for brand-safe content. Check if their content aligns with your brand's niche by analyzing video topics.
def check_brand_fit(username: str, sec_user_id: str, brand_keywords: list) -> dict:
"""Check if a creator's content fits your brand."""
resp = requests.post(f'{TT_URL}/user/posts', headers=TT_H,
json={'sec_user_id': sec_user_id, 'count': 20})
posts = resp.json().get('data', {}).get('videos', [])
descriptions = [post.get('desc', '').lower() for post in posts]
all_text = ' '.join(descriptions)
# Check brand keyword relevance
keyword_matches = {kw: sum(1 for d in descriptions if kw.lower() in d)
for kw in brand_keywords}
relevance = sum(keyword_matches.values()) / (len(descriptions) * len(brand_keywords)) if descriptions and brand_keywords else 0
# Brand safety checks
unsafe_terms = ['controversial', 'scandal', 'banned', 'nsfw', 'drama']
safety_flags = [t for t in unsafe_terms if t in all_text]
# Check if creator does sponsored content
sponsored_count = sum(1 for d in descriptions if any(w in d for w in ['#ad', '#sponsored', '#partner', 'collab']))
return {
'keyword_relevance': keyword_matches,
'relevance_score': relevance,
'safety_flags': safety_flags,
'brand_safe': len(safety_flags) == 0,
'sponsored_posts': sponsored_count,
'sponsored_rate': sponsored_count / len(descriptions) if descriptions else 0,
}
if profile.get('sec_user_id'):
brand_fit = check_brand_fit(profile['username'], profile['sec_user_id'],
['dance', 'fashion', 'lifestyle', 'beauty'])
print(f"Brand Fit Analysis:")
print(f" Relevance: {brand_fit['relevance_score']:.0%}")
print(f" Brand safe: {brand_fit['brand_safe']}")
print(f" Sponsored posts: {brand_fit['sponsored_posts']} ({brand_fit['sponsored_rate']:.0%})")
print(f" Keywords: {brand_fit['keyword_relevance']}")Step 4: Generate the creator vetting report
Combine all signals into a vetting score and recommendation. The report gives a clear go/no-go for each creator.
def vet_creator(username: str, brand_keywords: list) -> dict:
"""Full creator vetting report."""
profile = get_creator_profile(username)
time.sleep(0.3)
if not profile.get('sec_user_id'):
return {'username': username, 'verdict': 'ERROR', 'reason': 'Profile not found'}
content = analyze_content(username, profile['sec_user_id'])
time.sleep(0.3)
brand_fit = check_brand_fit(username, profile['sec_user_id'], brand_keywords)
# Scoring (0-100)
scores = {
'engagement': min(content.get('avg_engagement_rate', 0) * 1000, 30), # Max 30
'consistency': content.get('view_consistency', 0) * 20, # Max 20
'relevance': brand_fit.get('relevance_score', 0) * 25, # Max 25
'safety': 15 if brand_fit.get('brand_safe') else 0, # Max 15
'authenticity': min(profile.get('follower_ratio', 0) / 10, 10), # Max 10
}
total = sum(scores.values())
verdict = 'APPROVE' if total >= 60 else 'REVIEW' if total >= 40 else 'REJECT'
report = {
'username': username,
'followers': profile['followers'],
'avg_engagement': content.get('avg_engagement_rate', 0),
'avg_views': content.get('avg_views', 0),
'brand_safe': brand_fit.get('brand_safe'),
'relevance': brand_fit.get('relevance_score', 0),
'scores': scores,
'total_score': total,
'verdict': verdict,
'credits_used': 3,
'cost': 0.015,
}
print(f"\nCreator Report: @{username}")
print(f"Verdict: {verdict} (Score: {total:.0f}/100)")
print(f" Followers: {profile['followers']:,}")
print(f" Engagement: {content.get('avg_engagement_rate', 0):.1%}")
print(f" Brand safe: {brand_fit.get('brand_safe')}")
print(f" Relevance: {brand_fit.get('relevance_score', 0):.0%}")
for k, v in scores.items():
print(f" {k:15s}: {v:.1f}")
return report
report = vet_creator('charlidamelio', ['dance', 'fashion', 'lifestyle'])Python Example
import os, requests, time
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
TT_H = {'Authorization': f'Bearer {SCAVIO_KEY}', 'Content-Type': 'application/json'}
def vet_creator(username):
# Get profile
resp = requests.post('https://api.scavio.dev/api/v1/tiktok/profile', headers=TT_H,
json={'username': username})
data = resp.json().get('data', {})
stats = data.get('stats', {})
uid = data.get('user', {}).get('secUid', '')
time.sleep(0.3)
# Get posts
resp2 = requests.post('https://api.scavio.dev/api/v1/tiktok/user/posts', headers=TT_H,
json={'sec_user_id': uid, 'count': 10})
posts = resp2.json().get('data', {}).get('videos', [])
eng_rates = []
for p in posts:
s = p.get('stats', {})
views = s.get('playCount', 1)
eng_rates.append((s.get('diggCount', 0) + s.get('commentCount', 0)) / views)
avg_eng = sum(eng_rates) / len(eng_rates) if eng_rates else 0
print(f'@{username}: {stats.get("followerCount",0):,} followers, {avg_eng:.1%} engagement')
vet_creator('charlidamelio')JavaScript Example
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
const TT_H = { Authorization: `Bearer ${SCAVIO_KEY}`, 'Content-Type': 'application/json' };
async function vetCreator(username) {
const profile = await fetch('https://api.scavio.dev/api/v1/tiktok/profile', {
method: 'POST', headers: TT_H, body: JSON.stringify({ username })
}).then(r => r.json());
const stats = profile.data?.stats || {};
const uid = profile.data?.user?.secUid || '';
const posts = await fetch('https://api.scavio.dev/api/v1/tiktok/user/posts', {
method: 'POST', headers: TT_H, body: JSON.stringify({ sec_user_id: uid, count: 10 })
}).then(r => r.json());
const videos = posts.data?.videos || [];
const engRates = videos.map(v => {
const s = v.stats || {};
return (s.diggCount + s.commentCount) / (s.playCount || 1);
});
const avgEng = engRates.length ? engRates.reduce((a,b) => a+b, 0) / engRates.length : 0;
console.log(`@${username}: ${(stats.followerCount || 0).toLocaleString()} followers, ${(avgEng * 100).toFixed(1)}% engagement`);
}
vetCreator('charlidamelio');Expected Output
Creator: @charlidamelio (Charli D'Amelio)
Followers: 155,200,000
Total likes: 11,800,000,000
Videos: 2,340
Follower ratio: 4250:1
Verified: True
Content Analysis: @charlidamelio
Posts analyzed: 20
Avg engagement: 5.2%
Avg views: 8,450,000
View consistency: 45%
Quality: good
Creator Report: @charlidamelio
Verdict: APPROVE (Score: 72/100)
engagement : 30.0
consistency : 9.0
relevance : 18.8
safety : 15.0
authenticity : 10.0