Threads Analytics 2026: Track Any Profile's Growth Without Meta's API
Threads hit 300 million monthly active users in early 2026. Brands finally care about it. But Meta's API access for Threads is still limited — you need approval, it's read-only for your own account, and there's no competitor data.
That leaves a gap: how do you track someone else's Threads performance? How do you benchmark your own growth against competitors? How do you know if Threads is even worth your time compared to X or Bluesky?
You can't answer those questions staring at the Threads app.
This guide shows you how to build Threads analytics using API data — track any profile's metrics, analyze their content performance, and decide whether your brand should invest more in Threads.
What Threads Metrics Can You Track?
Here's what's available from any public Threads profile:
Profile Data
- Username and display name
- Bio text and bio links
- Follower count
- Verification status
- Profile picture URL
Post Data
- Full post text
- Like count per post
- Reply count per post
- Repost/reshare count per post
- Timestamp of each post
- Post URL (pk identifier)
That's enough to calculate engagement rates, posting frequency, content analysis, and growth tracking.
Track Any Profile's Performance
Start with the basics — pull a profile and their recent posts:
const axios = require('axios');
const API_KEY = process.env.SOCIAVAULT_API_KEY;
const BASE_URL = 'https://api.sociavault.com';
async function analyzeThreadsProfile(handle) {
// Get profile data
const profileResponse = await axios.get(`${BASE_URL}/v1/scrape/threads/profile`, {
params: { handle },
headers: { 'X-API-Key': API_KEY }
});
const profile = profileResponse.data.data;
// Get recent posts
const postsResponse = await axios.get(`${BASE_URL}/v1/scrape/threads/user-posts`, {
params: { handle },
headers: { 'X-API-Key': API_KEY }
});
const posts = postsResponse.data.data?.posts || [];
console.log(`\n=== THREADS ANALYTICS: @${profile.username} ===`);
console.log(`Name: ${profile.full_name}`);
console.log(`Bio: ${profile.biography}`);
console.log(`Followers: ${profile.follower_count?.toLocaleString()}`);
console.log(`Verified: ${profile.is_verified ? 'Yes' : 'No'}`);
// Analyze post performance
const postMetrics = posts.map(post => ({
text: post.caption?.text?.substring(0, 80) || '[no text]',
likes: post.like_count || 0,
replies: post.text_post_app_info?.direct_reply_count || 0,
reposts: post.text_post_app_info?.reshare_count || 0,
timestamp: post.taken_at,
engagement: (post.like_count || 0) +
(post.text_post_app_info?.direct_reply_count || 0) +
(post.text_post_app_info?.reshare_count || 0)
}));
// Calculate averages
const avgLikes = postMetrics.reduce((s, p) => s + p.likes, 0) / Math.max(postMetrics.length, 1);
const avgReplies = postMetrics.reduce((s, p) => s + p.replies, 0) / Math.max(postMetrics.length, 1);
const avgReposts = postMetrics.reduce((s, p) => s + p.reposts, 0) / Math.max(postMetrics.length, 1);
const avgEngagement = postMetrics.reduce((s, p) => s + p.engagement, 0) / Math.max(postMetrics.length, 1);
// Engagement rate (per follower)
const engagementRate = profile.follower_count > 0
? ((avgEngagement / profile.follower_count) * 100).toFixed(2)
: 'N/A';
console.log(`\nPost Performance (${postMetrics.length} recent posts):`);
console.log(` Avg likes: ${Math.round(avgLikes).toLocaleString()}`);
console.log(` Avg replies: ${Math.round(avgReplies).toLocaleString()}`);
console.log(` Avg reposts: ${Math.round(avgReposts).toLocaleString()}`);
console.log(` Avg engagement: ${Math.round(avgEngagement).toLocaleString()}`);
console.log(` Engagement rate: ${engagementRate}%`);
// Find top performing post
postMetrics.sort((a, b) => b.engagement - a.engagement);
if (postMetrics.length > 0) {
const top = postMetrics[0];
console.log(`\nTop post: "${top.text}..."`);
console.log(` ${top.likes} likes | ${top.replies} replies | ${top.reposts} reposts`);
}
return { profile, posts: postMetrics, engagementRate };
}
await analyzeThreadsProfile('zuck');
Cost: 2 credits (1 profile + 1 posts).
Benchmark Against Competitors
Running the same analysis against multiple accounts in your niche gives you a competitive benchmark:
import requests
import os
API_KEY = os.getenv('SOCIAVAULT_API_KEY')
BASE_URL = 'https://api.sociavault.com'
headers = {'X-API-Key': API_KEY}
def benchmark_threads_accounts(handles):
"""Compare Threads performance across multiple accounts"""
benchmarks = []
for handle in handles:
# Get profile
profile_resp = requests.get(
f'{BASE_URL}/v1/scrape/threads/profile',
params={'handle': handle},
headers=headers
)
profile = profile_resp.json().get('data', {})
# Get posts
posts_resp = requests.get(
f'{BASE_URL}/v1/scrape/threads/user-posts',
params={'handle': handle},
headers=headers
)
posts = posts_resp.json().get('data', {}).get('posts', [])
# Calculate metrics
total_likes = sum(p.get('like_count', 0) for p in posts)
total_replies = sum(
p.get('text_post_app_info', {}).get('direct_reply_count', 0) for p in posts
)
total_reposts = sum(
p.get('text_post_app_info', {}).get('reshare_count', 0) for p in posts
)
post_count = len(posts)
followers = profile.get('follower_count', 0)
avg_engagement = (total_likes + total_replies + total_reposts) / max(post_count, 1)
er = (avg_engagement / max(followers, 1)) * 100
benchmarks.append({
'handle': handle,
'followers': followers,
'posts_analyzed': post_count,
'avg_likes': round(total_likes / max(post_count, 1)),
'avg_replies': round(total_replies / max(post_count, 1)),
'avg_reposts': round(total_reposts / max(post_count, 1)),
'avg_engagement': round(avg_engagement),
'engagement_rate': round(er, 2),
'verified': profile.get('is_verified', False)
})
# Sort by engagement rate
benchmarks.sort(key=lambda x: x['engagement_rate'], reverse=True)
print('\n=== THREADS BENCHMARK REPORT ===\n')
print(f'{"Handle":<20} {"Followers":>12} {"Avg Likes":>10} {"Avg Replies":>12} {"ER%":>8}')
print('-' * 70)
for b in benchmarks:
badge = ' ✓' if b['verified'] else ''
print(f"@{b['handle']:<19}{badge} {b['followers']:>10,} {b['avg_likes']:>10,} "
f"{b['avg_replies']:>12,} {b['engagement_rate']:>7.2f}%")
# Calculate benchmark averages
avg_er = sum(b['engagement_rate'] for b in benchmarks) / len(benchmarks)
print(f'\nBenchmark average ER: {avg_er:.2f}%')
print(f'Top performer: @{benchmarks[0]["handle"]} ({benchmarks[0]["engagement_rate"]}%)')
return benchmarks
# Benchmark tech company Threads accounts
benchmark_threads_accounts([
'zuck',
'maboroshi.exe',
'garyvee',
'mkbhd'
])
Cost: 2 credits per account (profile + posts).
Content Performance Analysis
Not all Threads posts perform equally. Analyze what types of content get the most engagement:
async function contentAnalysis(handle) {
const postsResponse = await axios.get(`${BASE_URL}/v1/scrape/threads/user-posts`, {
params: { handle },
headers: { 'X-API-Key': API_KEY }
});
const posts = postsResponse.data.data?.posts || [];
// Classify posts by content type
const categories = {
questions: { posts: [], pattern: /\?/ },
links: { posts: [], pattern: /https?:\/\// },
short: { posts: [], check: (text) => text.length < 100 },
long: { posts: [], check: (text) => text.length >= 280 },
medium: { posts: [], check: (text) => text.length >= 100 && text.length < 280 }
};
posts.forEach(post => {
const text = post.caption?.text || '';
const engagement = (post.like_count || 0) +
(post.text_post_app_info?.direct_reply_count || 0) +
(post.text_post_app_info?.reshare_count || 0);
const item = { text: text.substring(0, 60), engagement, likes: post.like_count || 0 };
if (categories.questions.pattern.test(text)) categories.questions.posts.push(item);
if (categories.links.pattern.test(text)) categories.links.posts.push(item);
if (text.length < 100) categories.short.posts.push(item);
else if (text.length >= 280) categories.long.posts.push(item);
else categories.medium.posts.push(item);
});
console.log(`\n=== CONTENT ANALYSIS: @${handle} ===\n`);
Object.entries(categories).forEach(([type, data]) => {
if (data.posts.length === 0) return;
const avgEng = data.posts.reduce((s, p) => s + p.engagement, 0) / data.posts.length;
console.log(`${type.toUpperCase()} (${data.posts.length} posts): avg engagement ${Math.round(avgEng).toLocaleString()}`);
});
// Find optimal content patterns
const allPosts = posts.map(p => ({
text: p.caption?.text || '',
engagement: (p.like_count || 0) +
(p.text_post_app_info?.direct_reply_count || 0) +
(p.text_post_app_info?.reshare_count || 0),
likes: p.like_count || 0,
replies: p.text_post_app_info?.direct_reply_count || 0,
reposts: p.text_post_app_info?.reshare_count || 0,
length: (p.caption?.text || '').length,
hasQuestion: /\?/.test(p.caption?.text || ''),
hasLink: /https?:\/\//.test(p.caption?.text || '')
}));
// Engagement by characteristics
const withQuestions = allPosts.filter(p => p.hasQuestion);
const withoutQuestions = allPosts.filter(p => !p.hasQuestion);
if (withQuestions.length > 0 && withoutQuestions.length > 0) {
const qAvg = withQuestions.reduce((s, p) => s + p.engagement, 0) / withQuestions.length;
const noQAvg = withoutQuestions.reduce((s, p) => s + p.engagement, 0) / withoutQuestions.length;
const diff = ((qAvg / noQAvg - 1) * 100).toFixed(0);
console.log(`\nPosts with questions get ${diff}% ${parseInt(diff) > 0 ? 'more' : 'less'} engagement`);
}
return allPosts;
}
await contentAnalysis('garyvee');
Weekly Growth Tracking
Track follower growth and engagement changes over time:
import json
from datetime import datetime
def track_threads_growth(handles):
"""Snapshot Threads metrics for weekly tracking"""
timestamp = datetime.now().strftime('%Y-%m-%d')
snapshots = []
for handle in handles:
profile_resp = requests.get(
f'{BASE_URL}/v1/scrape/threads/profile',
params={'handle': handle},
headers=headers
)
profile = profile_resp.json().get('data', {})
posts_resp = requests.get(
f'{BASE_URL}/v1/scrape/threads/user-posts',
params={'handle': handle},
headers=headers
)
posts = posts_resp.json().get('data', {}).get('posts', [])
avg_likes = sum(p.get('like_count', 0) for p in posts) / max(len(posts), 1)
snapshots.append({
'date': timestamp,
'handle': handle,
'followers': profile.get('follower_count', 0),
'avg_likes': round(avg_likes),
'posts_sampled': len(posts)
})
# Save snapshots
log_file = 'threads-growth-tracking.json'
history = []
if os.path.exists(log_file):
with open(log_file, 'r') as f:
history = json.load(f)
history.extend(snapshots)
with open(log_file, 'w') as f:
json.dump(history, f, indent=2)
# Show week-over-week changes
for snap in snapshots:
prev = [h for h in history
if h['handle'] == snap['handle'] and h['date'] != snap['date']]
if prev:
last = prev[-1]
follower_change = snap['followers'] - last['followers']
likes_change = snap['avg_likes'] - last['avg_likes']
print(f"@{snap['handle']}: {snap['followers']:,} followers "
f"({'+' if follower_change >= 0 else ''}{follower_change:,}) | "
f"avg likes: {snap['avg_likes']:,} "
f"({'+' if likes_change >= 0 else ''}{likes_change:,})")
else:
print(f"@{snap['handle']}: {snap['followers']:,} followers | "
f"avg likes: {snap['avg_likes']:,} (first snapshot)")
return snapshots
# Track weekly
track_threads_growth(['zuck', 'garyvee', 'mkbhd'])
Threads Engagement Benchmarks (2026)
Based on publicly available data across accounts of different sizes:
| Follower Range | Average ER | Avg Likes | Avg Replies |
|---|---|---|---|
| <10K | 3-8% | 50-200 | 5-20 |
| 10K-50K | 1.5-4% | 200-800 | 10-40 |
| 50K-200K | 0.8-2% | 500-2,000 | 20-80 |
| 200K-1M | 0.3-1% | 1,000-5,000 | 30-150 |
| 1M+ | 0.1-0.5% | 2,000-20,000 | 50-500 |
Threads engagement rates are currently higher than Twitter/X — mostly because the algorithm still pushes content to followers and the platform is less saturated.
Should Your Brand Invest in Threads?
Use this framework to decide:
Threads is worth it if:
- Your audience is 18-35 and text-savvy
- You already have an Instagram presence (cross-follow effect)
- Your content is conversational (takes, opinions, tips)
- You're in tech, media, fashion, or lifestyle
Skip Threads if:
- Your audience is primarily professional (LinkedIn is better)
- Your content is visual-first (stay on Instagram)
- You're in B2B SaaS (Threads audience is B2C-heavy)
- You don't have bandwidth for another platform
The data will tell you. Run the benchmark analysis above. If your competitors are getting solid engagement on Threads, you should be there too.
Get Started
Sign up free — track any Threads profile's performance in minutes.
Full Threads API docs: docs.sociavault.com/api-reference/threads
Related Reading
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.