DEV Community

Cover image for Build a Content Performance Benchmarker: Are Your Numbers Good or Bad?
Olamide Olaniyan
Olamide Olaniyan

Posted on

Build a Content Performance Benchmarker: Are Your Numbers Good or Bad?

You got 5,000 likes on your last TikTok. Is that good?

If you have 10,000 followers — that's a 50% engagement rate. Incredible.
If you have 2,000,000 followers — that's 0.25%. Terrible.

But even knowing your engagement rate isn't enough. Is 3.2% good for a fitness creator on Instagram with 50K followers? What about a tech creator on TikTok with 200K?

The answer depends on your niche, your platform, and your follower tier. Without benchmarks, you're flying blind.

I built a benchmarker that compares any creator's performance against their actual peer group. Not global averages — niche-specific, tier-specific, platform-specific benchmarks computed from real data. Here's the build.

The Stack

  • Node.js – runtime
  • SociaVault API – fetch profiles and posts across niches
  • Statistics – percentile ranking, z-scores

Why Global Averages Are Useless

Every "engagement rate benchmark" article says the same thing:

"The average Instagram engagement rate is 1.5%."

This is useless because it mixes:

  • A food blogger with 5K followers (8% ER) with
  • Nike's brand account with 300M followers (0.05% ER)

You need benchmarks that compare apples to apples.

Step 1: Build a Peer Group

const axios = require('axios');

const API_BASE = 'https://api.sociavault.com/v1';
const API_KEY = process.env.SOCIAVAULT_API_KEY;

const api = axios.create({
  baseURL: API_BASE,
  headers: { 'x-api-key': API_KEY },
});

async function buildPeerGroup(platform, nicheKeywords, followerRange, size = 50) {
  const peers = [];

  for (const keyword of nicheKeywords) {
    const { data } = await api.get(`/${platform}/search/users`, {
      params: { keyword, limit: 30 },
    });

    for (const user of (data.users || [])) {
      // Filter by follower range
      if (user.followerCount >= followerRange.min && user.followerCount <= followerRange.max) {
        peers.push(user);
      }
    }
  }

  // Deduplicate
  const seen = new Set();
  const unique = peers.filter(p => {
    if (seen.has(p.username)) return false;
    seen.add(p.username);
    return true;
  });

  return unique.slice(0, size);
}
Enter fullscreen mode Exit fullscreen mode

Follower tiers:

const FOLLOWER_TIERS = {
  nano:   { min: 1000,    max: 10000,   label: 'Nano (1K-10K)' },
  micro:  { min: 10000,   max: 50000,   label: 'Micro (10K-50K)' },
  mid:    { min: 50000,   max: 200000,  label: 'Mid-tier (50K-200K)' },
  macro:  { min: 200000,  max: 1000000, label: 'Macro (200K-1M)' },
  mega:   { min: 1000000, max: Infinity, label: 'Mega (1M+)' },
};

function getTier(followerCount) {
  for (const [key, range] of Object.entries(FOLLOWER_TIERS)) {
    if (followerCount >= range.min && followerCount < range.max) {
      return { key, ...range };
    }
  }
  return { key: 'mega', ...FOLLOWER_TIERS.mega };
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Compute Peer Benchmarks

Fetch detailed metrics for every peer and build statistical benchmarks.

async function computeBenchmarks(platform, peers) {
  const peerMetrics = [];

  for (const peer of peers) {
    try {
      const { data: profile } = await api.get(`/${platform}/profile/${peer.username}`);
      const { data: postData } = await api.get(`/${platform}/posts/${peer.username}`, {
        params: { limit: 30 },
      });
      const posts = postData.posts;

      if (posts.length < 5) continue; // Need enough data

      const followers = profile.followerCount;

      const engagementRates = posts.map(p =>
        ((p.likeCount + p.commentCount) / followers) * 100
      );
      const avgER = engagementRates.reduce((a, b) => a + b, 0) / engagementRates.length;
      const avgLikes = posts.reduce((s, p) => s + p.likeCount, 0) / posts.length;
      const avgComments = posts.reduce((s, p) => s + p.commentCount, 0) / posts.length;
      const avgViews = posts.reduce((s, p) => s + (p.viewCount || 0), 0) / posts.length;

      // Comment-to-like ratio (quality signal)
      const commentLikeRatio = avgLikes > 0 ? avgComments / avgLikes : 0;

      peerMetrics.push({
        username: peer.username,
        followers,
        avgER: parseFloat(avgER.toFixed(2)),
        avgLikes: Math.round(avgLikes),
        avgComments: Math.round(avgComments),
        avgViews: Math.round(avgViews),
        commentLikeRatio: parseFloat(commentLikeRatio.toFixed(3)),
        postCount: posts.length,
      });
    } catch (err) {
      // Skip failed fetches
    }
  }

  if (peerMetrics.length < 5) {
    throw new Error('Not enough peer data. Try broader niche keywords.');
  }

  // Calculate statistical benchmarks
  return {
    sampleSize: peerMetrics.length,
    benchmarks: {
      engagementRate: computeStats(peerMetrics.map(p => p.avgER)),
      avgLikes: computeStats(peerMetrics.map(p => p.avgLikes)),
      avgComments: computeStats(peerMetrics.map(p => p.avgComments)),
      avgViews: computeStats(peerMetrics.map(p => p.avgViews)),
      commentLikeRatio: computeStats(peerMetrics.map(p => p.commentLikeRatio)),
    },
    peers: peerMetrics,
  };
}

function computeStats(values) {
  const sorted = [...values].sort((a, b) => a - b);
  const n = sorted.length;

  const mean = sorted.reduce((a, b) => a + b, 0) / n;
  const variance = sorted.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / n;
  const stdDev = Math.sqrt(variance);

  return {
    mean: parseFloat(mean.toFixed(2)),
    median: parseFloat(sorted[Math.floor(n / 2)].toFixed(2)),
    stdDev: parseFloat(stdDev.toFixed(2)),
    p25: parseFloat(sorted[Math.floor(n * 0.25)].toFixed(2)),
    p75: parseFloat(sorted[Math.floor(n * 0.75)].toFixed(2)),
    p90: parseFloat(sorted[Math.floor(n * 0.90)].toFixed(2)),
    min: parseFloat(sorted[0].toFixed(2)),
    max: parseFloat(sorted[n - 1].toFixed(2)),
  };
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Score the Creator Against Benchmarks

function benchmarkCreator(creatorMetrics, benchmarks) {
  const results = {};

  for (const [metric, stats] of Object.entries(benchmarks.benchmarks)) {
    const value = creatorMetrics[metric];
    if (value === undefined) continue;

    // Percentile rank: what % of peers does this creator beat?
    const belowCount = benchmarks.peers.filter(p => {
      const peerValue = metric === 'engagementRate' ? p.avgER :
        metric === 'avgLikes' ? p.avgLikes :
        metric === 'avgComments' ? p.avgComments :
        metric === 'avgViews' ? p.avgViews :
        p.commentLikeRatio;
      return peerValue < value;
    }).length;

    const percentile = Math.round((belowCount / benchmarks.peers.length) * 100);

    // Z-score: how many standard deviations from mean
    const zScore = stats.stdDev > 0 ? (value - stats.mean) / stats.stdDev : 0;

    // Grade
    let grade, emoji;
    if (percentile >= 90) { grade = 'A+'; emoji = '🏆'; }
    else if (percentile >= 75) { grade = 'A'; emoji = '🟢'; }
    else if (percentile >= 50) { grade = 'B'; emoji = '🟡'; }
    else if (percentile >= 25) { grade = 'C'; emoji = '🟠'; }
    else { grade = 'D'; emoji = '🔴'; }

    results[metric] = {
      value: parseFloat(value.toFixed(2)),
      peerMedian: stats.median,
      peerMean: stats.mean,
      percentile,
      zScore: parseFloat(zScore.toFixed(2)),
      grade,
      emoji,
    };
  }

  return results;
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Generate the Benchmark Report

async function runBenchmark(platform, username, nicheKeywords) {
  // Get creator's data
  const { data: profile } = await api.get(`/${platform}/profile/${username}`);
  const { data: postData } = await api.get(`/${platform}/posts/${username}`, {
    params: { limit: 30 },
  });
  const posts = postData.posts;
  const followers = profile.followerCount;
  const tier = getTier(followers);

  console.log(`\n═══ PERFORMANCE BENCHMARK REPORT ═══\n`);
  console.log(`Creator: @${username} on ${platform}`);
  console.log(`Followers: ${followers.toLocaleString()} (${tier.label})`);
  console.log(`Niche: ${nicheKeywords.join(', ')}\n`);

  // Build peer group in same tier and niche
  console.log(`Building peer group (${tier.label}, same niche)...`);
  const peers = await buildPeerGroup(platform, nicheKeywords, tier, 50);
  console.log(`Found ${peers.length} peers\n`);

  // Compute benchmarks
  const benchmarkData = await computeBenchmarks(platform, peers);
  console.log(`Analyzed ${benchmarkData.sampleSize} peers with sufficient data\n`);

  // Calculate creator's metrics
  const engagementRates = posts.map(p =>
    ((p.likeCount + p.commentCount) / followers) * 100
  );
  const creatorMetrics = {
    engagementRate: engagementRates.reduce((a, b) => a + b, 0) / engagementRates.length,
    avgLikes: posts.reduce((s, p) => s + p.likeCount, 0) / posts.length,
    avgComments: posts.reduce((s, p) => s + p.commentCount, 0) / posts.length,
    avgViews: posts.reduce((s, p) => s + (p.viewCount || 0), 0) / posts.length,
    commentLikeRatio: posts.reduce((s, p) => s + p.commentCount, 0) /
      Math.max(posts.reduce((s, p) => s + p.likeCount, 0), 1),
  };

  // Score against benchmarks
  const scores = benchmarkCreator(creatorMetrics, benchmarkData);

  // Print scorecard
  console.log('📊 SCORECARD');
  console.log(''.repeat(75));
  console.log(
    'Metric'.padEnd(22) +
    'You'.padStart(10) +
    'Peer Median'.padStart(14) +
    'Percentile'.padStart(14) +
    'Grade'.padStart(10)
  );
  console.log(''.repeat(75));

  const metricLabels = {
    engagementRate: 'Engagement Rate',
    avgLikes: 'Avg Likes',
    avgComments: 'Avg Comments',
    avgViews: 'Avg Views',
    commentLikeRatio: 'Comment/Like Ratio',
  };

  for (const [metric, data] of Object.entries(scores)) {
    const label = metricLabels[metric] || metric;
    const valueStr = metric === 'engagementRate' ? `${data.value}%` :
      metric === 'commentLikeRatio' ? `${data.value}` :
      data.value.toLocaleString();
    const medianStr = metric === 'engagementRate' ? `${data.peerMedian}%` :
      metric === 'commentLikeRatio' ? `${data.peerMedian}` :
      data.peerMedian.toLocaleString();

    console.log(
      `${data.emoji} ${label}`.padEnd(22) +
      valueStr.padStart(10) +
      medianStr.padStart(14) +
      `Top ${100 - data.percentile}%`.padStart(14) +
      data.grade.padStart(10)
    );
  }

  console.log(''.repeat(75));

  // Overall grade
  const avgPercentile = Object.values(scores).reduce((s, d) => s + d.percentile, 0) / Object.keys(scores).length;
  let overallGrade;
  if (avgPercentile >= 80) overallGrade = 'A — Outperforming peers';
  else if (avgPercentile >= 60) overallGrade = 'B — Above average';
  else if (avgPercentile >= 40) overallGrade = 'C — Average';
  else overallGrade = 'D — Below peer average';

  console.log(`\n🎯 Overall: ${overallGrade} (avg percentile: ${Math.round(avgPercentile)})`);

  // Insights
  console.log('\n💡 Insights:');
  const sorted = Object.entries(scores).sort((a, b) => b[1].percentile - a[1].percentile);
  console.log(`  Strongest: ${metricLabels[sorted[0][0]]} (top ${100 - sorted[0][1].percentile}% of peers)`);
  console.log(`  Weakest: ${metricLabels[sorted[sorted.length - 1][0]]} (top ${100 - sorted[sorted.length - 1][1].percentile}% of peers)`);

  return { creatorMetrics, scores, benchmarkData };
}

// Run it
runBenchmark('tiktok', 'target_creator', ['fitness', 'workout', 'gym']);
Enter fullscreen mode Exit fullscreen mode

Sample Output

═══ PERFORMANCE BENCHMARK REPORT ═══

Creator: @target_creator on tiktok
Followers: 67,200 (Mid-tier (50K-200K))
Niche: fitness, workout, gym

Building peer group (Mid-tier (50K-200K), same niche)...
Found 43 peers

Analyzed 38 peers with sufficient data

📊 SCORECARD
───────────────────────────────────────────────────────────────────────────
Metric                       You   Peer Median    Percentile     Grade
───────────────────────────────────────────────────────────────────────────
🟢 Engagement Rate          4.8%          3.2%       Top 18%         A
🏆 Avg Likes               3,225         2,100        Top 8%        A+
🟡 Avg Comments              142           138       Top 45%         B
🟠 Avg Views              41,200        52,800       Top 62%         C
🟢 Comment/Like Ratio      0.044         0.035       Top 22%         A
───────────────────────────────────────────────────────────────────────────

🎯 Overall: B — Above average (avg percentile: 69)

💡 Insights:
  Strongest: Avg Likes (top 8% of peers)
  Weakest: Avg Views (top 62% of peers)
Enter fullscreen mode Exit fullscreen mode

Engagement rate and likes are excellent. Views are below median — meaning the algorithm isn't distributing the content widely, but people who see it love it. That's a distribution problem, not a content problem.

Read the Full Guide

This is a condensed version. The full guide includes:

  • Historical benchmark tracking (are you improving vs. peers?)
  • Niche auto-detection from bio and hashtags
  • Competitive positioning matrix
  • Benchmark API endpoint for product integration

Read the complete guide on SociaVault →


Building analytics tools for creators or brands? SociaVault provides social media data APIs for TikTok, Instagram, YouTube, and 10+ platforms. Search niches, fetch profiles, and compute benchmarks with one unified API.

Discussion

How do you know if your content is actually performing well? Gut feeling? Peer comparison? What benchmarks matter most to you? 👇

webdev #api #nodejs #analytics #javascript

Top comments (0)