🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsHeader and Footer not showing in Excel(14.09.2026 um 22:43 Uhr)
🕵️ SicherheitslückenBurn Out, Or Fade Away(14.09.2026 um 14:25 Uhr)
🪟 Windows TippsKB5129194 Windows 11 26H1 Out of Band Update - Deskmodder.de(14.09.2026 um 19:25 Uhr)
🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsHeader and Footer not showing in Excel(14.09.2026 um 22:43 Uhr)
🕵️ SicherheitslückenBurn Out, Or Fade Away(14.09.2026 um 14:25 Uhr)
🪟 Windows TippsKB5129194 Windows 11 26H1 Out of Band Update - Deskmodder.de(14.09.2026 um 19:25 Uhr)

🔧 Programmierung 🕛 vor 6 Monaten 10 Min Lesezeit
0

Build a Threads Growth Tracker to Monitor the Twitter/X Alternative

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Everyone launched on Threads. Most people forgot about it.



But here's the thing — Threads hit 300 million monthly active users in late 2025. The people who stayed are engaged. And the platform is still growing.



The problem? There are zero analytics tools for Threads. Instagram has hundreds. TikTok has dozens. Threads has... your own eyeballs.



Let's fix that. We're building a Threads Growth Tracker that:




  1. Tracks any account's follower and engagement trends

  2. Finds top-performing content patterns

  3. Identifies the best conversations to join for visibility

  4. Monitors keyword searches for brand mentions






Why Threads Matters Now



Meta is pumping Threads hard. They're integrating it with ActivityPub (Fediverse), pushing it in Instagram, and adding features weekly. For developers and marketers, it's an underserved platform with real opportunity.



The accounts growing fastest on Threads share a few traits:




  • They're conversational, not broadcast-style

  • They reply a lot (Threads rewards conversation)

  • They cross-post from Twitter/X with context



We can detect all of this programmatically.






The Stack





  • Node.js: Runtime


  • SociaVault API: Threads data endpoints


  • Chart.js (optional): Visualizations


  • lowdb: Simple JSON file database for tracking






Step 1: Setup






CODE
mkdir threads-tracker
cd threads-tracker
npm init -y
npm install axios lowdb dotenv chalk






Create .env:




CODE
SOCIAVAULT_API_KEY=your_key_here









Step 2: Track a Profile Over Time



The /v1/scrape/threads/profile endpoint returns follower counts, post counts, and bio info. We'll store snapshots daily.



Create tracker.js:




CODE
require('dotenv').config();
const axios = require('axios');
const { join } = require('path');
const fs = require('fs');

const API_BASE = 'https://api.sociavault.com';
const headers = { 'Authorization': `Bearer ${process.env.SOCIAVAULT_API_KEY}` };
const DATA_DIR = join(__dirname, 'data');

if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true });

function getDB(handle) {
const file = join(DATA_DIR, `${handle}.json`);
if (!fs.existsSync(file)) {
fs.writeFileSync(file, JSON.stringify({ handle, snapshots: [], posts: [] }));
}
return JSON.parse(fs.readFileSync(file, 'utf-8'));
}

function saveDB(handle, data) {
fs.writeFileSync(join(DATA_DIR, `${handle}.json`), JSON.stringify(data, null, 2));
}

async function getThreadsProfile(handle) {
console.log(`📥 Fetching @${handle}...`);

const { data } = await axios.get(`${API_BASE}/v1/scrape/threads/profile`, {
params: { handle },
headers
});

const profile = data.data;

return {
handle: profile.username || profile.handle || handle,
displayName: profile.full_name || profile.name || handle,
followers: profile.follower_count || profile.followers || 0,
following: profile.following_count || profile.following || 0,
bio: profile.biography || profile.bio || '',
verified: profile.is_verified || false,
profilePic: profile.profile_pic_url || null,
};
}

async function snapshotProfile(handle) {
const profile = await getThreadsProfile(handle);
const db = getDB(handle);

const snapshot = {
date: new Date().toISOString().split('T')[0],
timestamp: new Date().toISOString(),
followers: profile.followers,
following: profile.following,
};

// Don't add duplicate snapshots for same day
const today = snapshot.date;
const existingToday = db.snapshots.find(s => s.date === today);

if (existingToday) {
Object.assign(existingToday, snapshot);
} else {
db.snapshots.push(snapshot);
}

saveDB(handle, db);

return { profile, snapshot };
}









Step 3: Fetch and Analyze Posts



Let's pull their recent posts and figure out what's working:




CODE
async function getUserPosts(handle) {
console.log(`📥 Fetching posts from @${handle}...`);

const { data } = await axios.get(`${API_BASE}/v1/scrape/threads/user-posts`, {
params: { handle },
headers
});

const posts = data.data || [];

return posts.map(post => ({
id: post.id || post.pk,
text: post.text || post.caption || '',
likes: post.like_count || post.likes || 0,
replies: post.reply_count || post.replies || post.text_post_app_info?.direct_reply_count || 0,
reposts: post.repost_count || post.reposts || 0,
quotes: post.quote_count || post.quotes || 0,
timestamp: post.taken_at || post.created_at || post.timestamp,
hasImage: !!(post.image_versions2 || post.carousel_media || post.image),
hasVideo: !!(post.video_versions || post.video),
isReply: !!(post.replied_to || post.parent_id),
}));
}

function analyzePostPerformance(posts, followerCount) {
if (posts.length === 0) return null;

// Separate original posts from replies
const originals = posts.filter(p => !p.isReply);
const replies = posts.filter(p => p.isReply);

// Engagement calculations
const getEngagement = (post) => {
return post.likes + post.replies + post.reposts + post.quotes;
};

const getEngagementRate = (post) => {
return followerCount > 0
? (getEngagement(post) / followerCount) * 100
: 0;
};

// Top posts
const sortedByEngagement = [...originals].sort((a, b) =>
getEngagement(b) - getEngagement(a)
);

// Average stats
const avgLikes = originals.reduce((sum, p) => sum + p.likes, 0) / originals.length;
const avgReplies = originals.reduce((sum, p) => sum + p.replies, 0) / originals.length;
const avgEngagementRate = originals.reduce((sum, p) => sum + getEngagementRate(p), 0) / originals.length;

// Content type analysis
const textOnly = originals.filter(p => !p.hasImage && !p.hasVideo);
const withMedia = originals.filter(p => p.hasImage || p.hasVideo);

const textOnlyAvgEng = textOnly.length > 0
? textOnly.reduce((sum, p) => sum + getEngagement(p), 0) / textOnly.length
: 0;
const mediaAvgEng = withMedia.length > 0
? withMedia.reduce((sum, p) => sum + getEngagement(p), 0) / withMedia.length
: 0;

// Post length analysis
const shortPosts = originals.filter(p => p.text.length < 100);
const longPosts = originals.filter(p => p.text.length >= 100);

const shortAvgEng = shortPosts.length > 0
? shortPosts.reduce((sum, p) => sum + getEngagement(p), 0) / shortPosts.length
: 0;
const longAvgEng = longPosts.length > 0
? longPosts.reduce((sum, p) => sum + getEngagement(p), 0) / longPosts.length
: 0;

return {
totalPosts: posts.length,
originalPosts: originals.length,
replies: replies.length,
replyRatio: (replies.length / posts.length * 100).toFixed(1),

avgLikes: Math.round(avgLikes),
avgReplies: Math.round(avgReplies),
avgEngagementRate: avgEngagementRate.toFixed(2),

topPosts: sortedByEngagement.slice(0, 5).map(p => ({
text: p.text.substring(0, 100) + (p.text.length > 100 ? '...' : ''),
engagement: getEngagement(p),
likes: p.likes,
replies: p.replies,
})),

contentTypeWinner: mediaAvgEng > textOnlyAvgEng ? 'media' : 'text-only',
textOnlyAvgEngagement: Math.round(textOnlyAvgEng),
mediaAvgEngagement: Math.round(mediaAvgEng),

lengthWinner: longAvgEng > shortAvgEng ? 'long (100+ chars)' : 'short (<100 chars)',
shortPostAvgEngagement: Math.round(shortAvgEng),
longPostAvgEngagement: Math.round(longAvgEng),
};
}









Step 4: Monitor Keywords and Brand Mentions



Threads has search. Let's use it:




CODE
async function searchThreads(query) {
console.log(`🔍 Searching Threads for "${query}"...`);

const { data } = await axios.get(`${API_BASE}/v1/scrape/threads/search`, {
params: { query },
headers
});

const posts = data.data || [];

console.log(`Found ${posts.length} results\n`);

return posts.map(post => ({
id: post.id,
author: post.user?.username || post.author || 'unknown',
authorFollowers: post.user?.follower_count || 0,
text: post.text || post.caption || '',
likes: post.like_count || post.likes || 0,
replies: post.reply_count || post.replies || 0,
timestamp: post.taken_at || post.created_at,
}));
}

async function monitorBrandMentions(brand, previousMentions = new Set()) {
const results = await searchThreads(brand);

const newMentions = results.filter(r => !previousMentions.has(r.id));

if (newMentions.length > 0) {
console.log(`\n🔔 ${newMentions.length} new mentions of "${brand}":`);
console.log(''.repeat(50));

newMentions.forEach(mention => {
const sentiment = quickSentiment(mention.text);
console.log(`\n @${mention.author} (${mention.authorFollowers.toLocaleString()} followers)`);
console.log(` ${sentiment.emoji} ${mention.text.substring(0, 150)}`);
console.log(` ❤️ ${mention.likes} 💬 ${mention.replies}`);
});
}

// Return all IDs for next check
return new Set(results.map(r => r.id));
}

function quickSentiment(text) {
const lower = text.toLowerCase();
const positive = ['love', 'great', 'amazing', 'best', 'awesome', 'perfect', 'recommend', 'good'];
const negative = ['hate', 'terrible', 'worst', 'awful', 'bad', 'broken', 'disappointed', 'scam'];

const posCount = positive.filter(w => lower.includes(w)).length;
const negCount = negative.filter(w => lower.includes(w)).length;

if (posCount > negCount) return { sentiment: 'positive', emoji: '😀' };
if (negCount > posCount) return { sentiment: 'negative', emoji: '😠' };
return { sentiment: 'neutral', emoji: '😐' };
}









Step 5: Find Accounts to Engage With



Growing on Threads is about conversations. Let's find the right ones:




CODE
async function findEngagementOpportunities(query) {
console.log(`\n🎯 Finding engagement opportunities for "${query}"...\n`);

const posts = await searchThreads(query);

// Sort by engagement potential (high engagement + recent)
const opportunities = posts
.filter(p => p.replies < 50) // Not too crowded
.filter(p => p.authorFollowers > 100) // Author has some reach
.sort((a, b) => {
// Score: author followers * engagement, prefer recent
const scoreA = (a.likes + a.replies * 3) * Math.log10(a.authorFollowers + 1);
const scoreB = (b.likes + b.replies * 3) * Math.log10(b.authorFollowers + 1);
return scoreB - scoreA;
})
.slice(0, 10);

console.log('🏆 Top Threads to Reply To:');
console.log(''.repeat(60));

opportunities.forEach((post, i) => {
console.log(`\n${i + 1}. @${post.author} (${post.authorFollowers.toLocaleString()} followers)`);
console.log(` "${post.text.substring(0, 120)}${post.text.length > 120 ? '...' : ''}"`);
console.log(` ❤️ ${post.likes} | 💬 ${post.replies} replies | Opportunity: ${post.replies < 10 ? 'HIGH' : 'MEDIUM'}`);
});

return opportunities;
}









Step 6: Growth Dashboard



Put it all together into a comprehensive analysis:




CODE
async function growthDashboard(handle) {
console.log('\n📊 THREADS GROWTH DASHBOARD');
console.log(''.repeat(60));

// Get current profile
const { profile, snapshot } = await snapshotProfile(handle);
const db = getDB(handle);

console.log(`\n👤 @${profile.handle} ${profile.verified ? '' : ''}`);
console.log(` ${profile.displayName}`);
console.log(` ${profile.followers.toLocaleString()} followers | ${profile.following.toLocaleString()} following`);

// Growth trends
if (db.snapshots.length >= 2) {
const latest = db.snapshots[db.snapshots.length - 1];
const previous = db.snapshots[db.snapshots.length - 2];
const oldest = db.snapshots[0];

const dailyChange = latest.followers - previous.followers;
const totalChange = latest.followers - oldest.followers;
const daysCovered = db.snapshots.length;
const avgDailyGrowth = totalChange / daysCovered;

console.log(`\n📈 GROWTH`);
console.log(` Today: ${dailyChange >= 0 ? '+' : ''}${dailyChange.toLocaleString()} followers`);
console.log(` Total tracked: ${totalChange >= 0 ? '+' : ''}${totalChange.toLocaleString()} over ${daysCovered} days`);
console.log(` Avg daily: ${avgDailyGrowth >= 0 ? '+' : ''}${avgDailyGrowth.toFixed(0)} followers/day`);
console.log(` Growth rate: ${((totalChange / oldest.followers) * 100).toFixed(2)}% total`);

// Project future growth
const projectedMonthly = avgDailyGrowth * 30;
console.log(` Projected 30-day: ${projectedMonthly >= 0 ? '+' : ''}${projectedMonthly.toFixed(0)} followers`);
}

// Post analysis
const posts = await getUserPosts(handle);
const analysis = analyzePostPerformance(posts, profile.followers);

if (analysis) {
console.log(`\n📝 CONTENT PERFORMANCE (${analysis.totalPosts} posts analyzed)`);
console.log(` Original posts: ${analysis.originalPosts}`);
console.log(` Replies: ${analysis.replies} (${analysis.replyRatio}% of activity)`);
console.log(` Avg likes: ${analysis.avgLikes}`);
console.log(` Avg replies: ${analysis.avgReplies}`);
console.log(` Avg engagement rate: ${analysis.avgEngagementRate}%`);

console.log(`\n🏆 TOP PERFORMING POSTS:`);
analysis.topPosts.forEach((post, i) => {
console.log(` ${i + 1}. [${post.engagement} eng] ${post.text}`);
});

console.log(`\n💡 INSIGHTS:`);
console.log(` Content type: ${analysis.contentTypeWinner} posts perform better`);
console.log(` Text-only avg: ${analysis.textOnlyAvgEngagement} engagement`);
console.log(` Media avg: ${analysis.mediaAvgEngagement} engagement`);
console.log(` Post length: ${analysis.lengthWinner} performs better`);
console.log(` Short avg: ${analysis.shortPostAvgEngagement} engagement`);
console.log(` Long avg: ${analysis.longPostAvgEngagement} engagement`);
}

return { profile, analysis };
}









Step 7: Run the Tracker






CODE
async function main() {
const command = process.argv[2];
const target = process.argv[3];

switch (command) {
case 'track':
await growthDashboard(target);
break;
case 'posts':
const posts = await getUserPosts(target);
const profile = await getThreadsProfile(target);
const analysis = analyzePostPerformance(posts, profile.followers);
console.log(JSON.stringify(analysis, null, 2));
break;
case 'search':
await searchThreads(target);
break;
case 'monitor':
await monitorBrandMentions(target);
break;
case 'engage':
await findEngagementOpportunities(target);
break;
default:
console.log('Usage:');
console.log(' node tracker.js track <handle> - Full growth dashboard');
console.log(' node tracker.js posts <handle> - Analyze post performance');
console.log(' node tracker.js search "keyword" - Search Threads');
console.log(' node tracker.js monitor "brand" - Monitor brand mentions');
console.log(' node tracker.js engage "topic" - Find engagement opportunities');
}
}

main().catch(console.error);









Automate It



Add a daily cron job to track growth over time:




CODE
# Run every day at 9am
0 9 * * * cd /path/to/threads-tracker && node tracker.js track zuck >> logs/daily.log 2>&1






Or set up multiple accounts:




CODE
// multi-track.js
const accounts = ['zuck', 'mosseri', 'your_brand'];

async function trackAll() {
for (const handle of accounts) {
await snapshotProfile(handle);
console.log(`✅ Tracked @${handle}`);
await new Promise(r => setTimeout(r, 2000));
}
}

trackAll();









What Tools Exist for Threads Analytics?



Basically nothing. That's the opportunity:

































Tool Threads Support Price
Sprout Social Coming soon $249/mo
Hootsuite Limited $99/mo
Buffer Posting only $6/mo
Your tracker Full analytics ~$0.05/analysis


You're ahead of the enterprise tools. Enjoy it while it lasts.






Get Started




  1. Grab your API key at sociavault.com

  2. Start tracking accounts that matter to you

  3. Run it daily and watch the growth data accumulate



Threads is the one platform where building a custom analytics tool actually gives you an edge — because nobody else has one yet.






300 million users and zero analytics tools. That's not a gap — that's a canyon.






threads #javascript #analytics #socialmedia

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
The Gemini desktop app is now available for Windows
1 Quelle
Header and Footer not showing in Excel
1 Quelle
Burn Out, Or Fade Away
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Build a Threads Growth Tracker to Monitor the Twitter/X Alternative

Thematisch verwandte Begriffe: Build, Threads, Growth, Tracker · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...