Les faux followers et la fraude à l'engagement gaspillent les budgets de marketing d'influence. Détecter les faux sur TikTok nécessite d'analyser plusieurs signaux : ratios engagement/abonnés, modèles de qualité des commentaires, pics de croissance des abonnés et cohérence du contenu. Ce tutoriel construit un pipeline automatisé de détection des fraudes utilisant l'API Scavio TikTok. Il récupère les données de profil et les vidéos récentes, effectue des tests statistiques pour détecter les anomalies et génère un score de risque de fraude. Le coût total est de 2 à 3 crédits (0,01-0,015 $) par créateur analysé.
Prérequis
- Python 3.9+ installé
- Bibliothèque requests installée
- Une clé API Scavio de scavio.dev
- Noms d'utilisateur TikTok à analyser
Parcours
Étape 1: Récupérer les données de profil et d'engagement
Obtenez les statistiques du profil du créateur et les performances des vidéos récentes. Les deux sont nécessaires pour détecter les incohérences signalant un engagement frauduleux.
import requests, os
API_KEY = os.environ['SCAVIO_API_KEY']
TIKTOK_URL = 'https://api.scavio.dev/api/v1/tiktok'
def fetch_creator_data(username: str) -> dict:
# Profile
profile_resp = requests.post(f'{TIKTOK_URL}/user/info',
headers={'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'},
json={'username': username})
profile_data = profile_resp.json().get('data', {})
# Recent videos
videos_resp = requests.post(f'{TIKTOK_URL}/user/posts',
headers={'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'},
json={'username': username, 'count': 30, 'cursor': 0})
videos = videos_resp.json().get('data', {}).get('videos', [])
stats = profile_data.get('stats', {})
return {
'username': username,
'followers': stats.get('followerCount', 0),
'following': stats.get('followingCount', 0),
'total_likes': stats.get('heartCount', 0),
'video_count': stats.get('videoCount', 0),
'videos': videos
}Étape 2: Vérifier les anomalies de ratio d'engagement
Les comptes légitimes ont des ratios engagement/abonnés prévisibles. Les comptes avec de faux abonnés montrent un engagement anormalement bas par rapport au nombre d'abonnés.
def check_engagement_ratio(data: dict) -> dict:
followers = max(data['followers'], 1)
videos = data['videos']
if not videos:
return {'flag': 'NO_VIDEOS', 'risk': 50}
avg_likes = sum(v.get('stats', {}).get('diggCount', 0) for v in videos) / len(videos)
avg_plays = sum(v.get('stats', {}).get('playCount', 0) for v in videos) / len(videos)
likes_to_followers = (avg_likes / followers) * 100
plays_to_followers = (avg_plays / followers) * 100
# Normal ranges for TikTok:
# Likes/followers: 1-15% is typical, <0.5% suspicious, >20% suspicious (bought likes)
# Plays/followers: 10-200% is typical, <5% suspicious
engagement_risk = 0
flags = []
if likes_to_followers < 0.5:
engagement_risk += 30
flags.append(f'Very low like ratio: {likes_to_followers:.2f}%')
elif likes_to_followers > 25:
engagement_risk += 20
flags.append(f'Abnormally high like ratio: {likes_to_followers:.2f}%')
if plays_to_followers < 5:
engagement_risk += 25
flags.append(f'Very low play ratio: {plays_to_followers:.2f}%')
return {
'likes_to_followers': round(likes_to_followers, 2),
'plays_to_followers': round(plays_to_followers, 2),
'risk_score': engagement_risk,
'flags': flags
}Étape 3: Analyser la cohérence de l'engagement
Les comptes réels présentent une variation naturelle de l'engagement. L'engagement frauduleux est souvent trop cohérent (likes de bots) ou présente des pics extrêmes (engagement acheté).
import statistics
def check_engagement_consistency(data: dict) -> dict:
videos = data['videos']
if len(videos) < 5:
return {'risk_score': 10, 'flags': ['Too few videos to analyze']}
like_counts = [v.get('stats', {}).get('diggCount', 0) for v in videos]
play_counts = [v.get('stats', {}).get('playCount', 0) for v in videos]
flags = []
risk = 0
# Check if engagement is suspiciously uniform
if like_counts and statistics.mean(like_counts) > 0:
cv = statistics.stdev(like_counts) / statistics.mean(like_counts) # coefficient of variation
if cv < 0.1: # Less than 10% variation = suspiciously uniform
risk += 25
flags.append(f'Suspiciously uniform likes (CV={cv:.3f})')
# Check for sudden engagement spikes
if play_counts:
median_plays = statistics.median(play_counts)
spikes = sum(1 for p in play_counts if p > median_plays * 10)
spike_ratio = spikes / len(play_counts)
if spike_ratio > 0.3: # More than 30% of videos have 10x spikes
risk += 20
flags.append(f'{spikes}/{len(play_counts)} videos have 10x play spikes')
# Check likes-to-comments ratio (bots rarely comment)
for v in videos:
s = v.get('stats', {})
likes = s.get('diggCount', 0)
comments = s.get('commentCount', 0)
if likes > 1000 and comments < likes * 0.005: # Less than 0.5% comment rate
risk += 5
flags.append(f'Very low comment ratio on video with {likes} likes')
break # Only flag once
return {'risk_score': min(risk, 50), 'flags': flags}Étape 4: Vérifier le ratio abonnements/abonnés
Les comptes qui suivent un nombre massif de comptes par rapport à leurs abonnés ont souvent participé à des systèmes de follow-for-follow ou utilisé des bots de suivi.
def check_follow_ratio(data: dict) -> dict:
followers = max(data['followers'], 1)
following = data['following']
ratio = following / followers
flags = []
risk = 0
if followers > 10000 and ratio > 1.0:
risk += 30
flags.append(f'Following > followers ({following:,} / {followers:,})')
elif followers > 10000 and ratio > 0.5:
risk += 15
flags.append(f'High follow ratio: {ratio:.2f}')
# Check if total likes seem inflated relative to video count
if data['video_count'] > 0:
likes_per_video = data['total_likes'] / data['video_count']
expected_likes = followers * 0.05 # 5% of followers per video is generous
if likes_per_video > expected_likes * 5:
risk += 15
flags.append(f'Inflated total likes: {likes_per_video:,.0f}/video vs {expected_likes:,.0f} expected')
return {'follow_ratio': round(ratio, 3), 'risk_score': risk, 'flags': flags}Étape 5: Générer le rapport complet de fraude
Combinez tous les signaux de détection en une évaluation complète des risques de fraude avec un score de risque global et une ventilation détaillée.
def fraud_report(username: str) -> dict:
data = fetch_creator_data(username)
engagement = check_engagement_ratio(data)
consistency = check_engagement_consistency(data)
follow = check_follow_ratio(data)
total_risk = engagement['risk_score'] + consistency['risk_score'] + follow['risk_score']
all_flags = engagement.get('flags', []) + consistency.get('flags', []) + follow.get('flags', [])
verdict = 'LOW RISK' if total_risk < 20 else 'MEDIUM RISK' if total_risk < 50 else 'HIGH RISK'
report = {
'username': username,
'followers': data['followers'],
'total_risk_score': min(total_risk, 100),
'verdict': verdict,
'engagement_check': engagement,
'consistency_check': consistency,
'follow_check': follow,
'all_flags': all_flags,
'credits_used': 2
}
print(f'@{username}: {verdict} ({total_risk}/100)')
print(f' Followers: {data["followers"]:,}')
for flag in all_flags:
print(f' - {flag}')
return report
# report = fraud_report('suspicious_creator')Exemple Python
import os, requests, statistics
API_KEY = os.environ['SCAVIO_API_KEY']
TT = 'https://api.scavio.dev/api/v1/tiktok'
def tt(endpoint, body):
return requests.post(f'{TT}/{endpoint}',
headers={'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json'},
json=body).json()
def detect_fake(username):
profile = tt('user/info', {'username': username}).get('data', {})
stats = profile.get('stats', {})
followers = stats.get('followerCount', 1)
posts = tt('user/posts', {'username': username, 'count': 20, 'cursor': 0})
videos = posts.get('data', {}).get('videos', [])
avg_likes = sum(v.get('stats', {}).get('diggCount', 0) for v in videos) / max(len(videos), 1)
ratio = (avg_likes / followers) * 100
risk = 'HIGH' if ratio < 0.5 else 'MEDIUM' if ratio < 1.0 else 'LOW'
print(f'@{username}: {risk} RISK (like ratio: {ratio:.2f}%, {followers:,} followers)')
detect_fake('example_creator')Exemple JavaScript
const API_KEY = process.env.SCAVIO_API_KEY;
const TT = 'https://api.scavio.dev/api/v1/tiktok';
async function tt(endpoint, body) {
const r = await fetch(`${TT}/${endpoint}`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
return r.json();
}
async function detectFake(username) {
const profile = await tt('user/info', { username });
const followers = profile.data?.stats?.followerCount || 1;
const posts = await tt('user/posts', { username, count: 20, cursor: 0 });
const videos = posts.data?.videos || [];
const avgLikes = videos.reduce((s, v) => s + (v.stats?.diggCount || 0), 0) / Math.max(videos.length, 1);
const ratio = (avgLikes / followers) * 100;
console.log(`@${username}: ${ratio < 0.5 ? 'HIGH' : ratio < 1 ? 'MED' : 'LOW'} RISK (${ratio.toFixed(2)}%)`);
}
detectFake('example_creator').catch(console.error);Sortie attendue
@suspicious_creator: HIGH RISK (72/100)
Followers: 500,000
- Very low like ratio: 0.31%
- Suspiciously uniform likes (CV=0.082)
- Following > followers (520,000 / 500,000)
@legitimate_creator: LOW RISK (8/100)
Followers: 85,000
(no flags)
Credits used: 2 per creator ($0.01)