Back to Blog
Strategy

Social Media ROI Calculator: Measure What Actually Matters

May 13, 2026
7 min read
S
By SociaVault Team
Social Media ROIAnalyticsMarketing ROIDashboardAPI

Social Media ROI Calculator: Measure What Actually Matters

"What's the ROI of social media?" is a question that's been asked for 15 years, and most teams still can't answer it. Not because ROI doesn't exist — but because they're measuring the wrong things.

Here's how to build an ROI calculation system that connects social metrics to actual business outcomes.


The ROI Formula

Social Media ROI = (Revenue from Social - Cost of Social) / Cost of Social Ɨ 100

Simple formula. The hard part is measuring "Revenue from Social" — which is why you need data, not guesses.


Build a Multi-Platform ROI Dashboard

Start by collecting the social metrics that actually tie to revenue:

const API_KEY = process.env.SOCIAVAULT_API_KEY;
const BASE = 'https://api.sociavault.com/v1/scrape';
const headers = { 'X-API-Key': API_KEY };

async function calculateSocialROI(config) {
  const { accounts, monthlyCosts, monthlyRevenue } = config;
  const metrics = {};

  // Instagram
  if (accounts.instagram) {
    const profileRes = await fetch(
      `${BASE}/instagram/profile?username=${encodeURIComponent(accounts.instagram)}`,
      { headers }
    );
    const profile = (await profileRes.json()).data;

    const postsRes = await fetch(
      `${BASE}/instagram/posts?username=${encodeURIComponent(accounts.instagram)}`,
      { headers }
    );
    const posts = (await postsRes.json()).data || [];

    const followers = profile?.follower_count || 0;
    const avgLikes = posts.length > 0
      ? Math.round(posts.reduce((s, p) => s + (p.like_count || 0), 0) / posts.length)
      : 0;
    const avgComments = posts.length > 0
      ? Math.round(posts.reduce((s, p) => s + (p.comment_count || 0), 0) / posts.length)
      : 0;

    metrics.instagram = {
      followers,
      avgLikes,
      avgComments,
      engRate: followers > 0 ? ((avgLikes + avgComments) / followers * 100) : 0,
      estMonthlyImpressions: avgLikes * 12 * posts.length, // rough estimate
      postCount: posts.length
    };

    await new Promise(r => setTimeout(r, 1200));
  }

  // TikTok
  if (accounts.tiktok) {
    const profileRes = await fetch(
      `${BASE}/tiktok/profile?username=${encodeURIComponent(accounts.tiktok)}`,
      { headers }
    );
    const profile = (await profileRes.json()).data;

    const postsRes = await fetch(
      `${BASE}/tiktok/user/posts?username=${encodeURIComponent(accounts.tiktok)}`,
      { headers }
    );
    const videos = (await postsRes.json()).data || [];

    const followers = profile?.stats?.followerCount || 0;
    const totalViews = videos.reduce((s, v) => s + (v.stats?.playCount || 0), 0);
    const avgViews = videos.length > 0 ? Math.round(totalViews / videos.length) : 0;

    metrics.tiktok = {
      followers,
      totalViews,
      avgViews,
      videoCount: videos.length
    };

    await new Promise(r => setTimeout(r, 1200));
  }

  // Twitter
  if (accounts.twitter) {
    const profileRes = await fetch(
      `${BASE}/twitter/profile?username=${encodeURIComponent(accounts.twitter)}`,
      { headers }
    );
    const profile = (await profileRes.json()).data;

    const postsRes = await fetch(
      `${BASE}/twitter/user-tweets?username=${encodeURIComponent(accounts.twitter)}`,
      { headers }
    );
    const tweets = (await postsRes.json()).data || [];

    const followers = profile?.legacy?.followers_count || 0;
    const avgLikes = tweets.length > 0
      ? Math.round(tweets.reduce((s, t) => s + (t.legacy?.favorite_count || 0), 0) / tweets.length)
      : 0;

    metrics.twitter = {
      followers,
      avgLikes,
      tweetCount: tweets.length
    };

    await new Promise(r => setTimeout(r, 1200));
  }

  // Calculate ROI metrics
  const totalFollowers = Object.values(metrics).reduce((s, m) => s + (m.followers || 0), 0);
  const totalReach = (metrics.instagram?.estMonthlyImpressions || 0) + (metrics.tiktok?.totalViews || 0);
  const totalCost = Object.values(monthlyCosts).reduce((s, c) => s + c, 0);
  const roi = totalCost > 0 ? ((monthlyRevenue.social - totalCost) / totalCost * 100) : 0;

  // Cost per metrics
  const costPerFollower = totalFollowers > 0 ? (totalCost / totalFollowers) : 0;
  const costPerEngagement = totalReach > 0 ? (totalCost / totalReach * 1000) : 0;
  const revenuePerFollower = totalFollowers > 0 ? (monthlyRevenue.social / totalFollowers) : 0;

  console.log('\nšŸ“ˆ Social Media ROI Dashboard');
  console.log('═'.repeat(55));

  console.log('\n  Platform Metrics:');
  for (const [platform, data] of Object.entries(metrics)) {
    console.log(`    ${platform}: ${data.followers.toLocaleString()} followers`);
  }

  console.log(`\n  Total Reach: ${totalReach.toLocaleString()} impressions/views`);
  console.log(`  Total Followers: ${totalFollowers.toLocaleString()}`);

  console.log('\n  Costs:');
  for (const [category, amount] of Object.entries(monthlyCosts)) {
    console.log(`    ${category}: $${amount.toLocaleString()}`);
  }
  console.log(`    Total: $${totalCost.toLocaleString()}/month`);

  console.log('\n  Revenue:');
  console.log(`    Social-attributed: $${monthlyRevenue.social.toLocaleString()}/month`);

  console.log('\n  ROI Metrics:');
  console.log(`    ROI: ${roi.toFixed(1)}%`);
  console.log(`    Cost per follower: $${costPerFollower.toFixed(4)}`);
  console.log(`    CPM (cost per 1K reach): $${costPerEngagement.toFixed(2)}`);
  console.log(`    Revenue per follower: $${revenuePerFollower.toFixed(4)}`);

  const verdict = roi > 200 ? 'Excellent'
    : roi > 100 ? 'Strong'
    : roi > 0 ? 'Positive but room to improve'
    : 'Negative — review strategy';
  console.log(`\n  Verdict: ${verdict}`);

  return { metrics, roi, totalCost, totalFollowers, totalReach };
}

calculateSocialROI({
  accounts: {
    instagram: 'glossier',
    tiktok: 'glossier',
    twitter: 'glossier'
  },
  monthlyCosts: {
    'Content creation': 3000,
    'Social manager': 4500,
    'Tools & software': 500,
    'Paid promotion': 2000
  },
  monthlyRevenue: {
    social: 45000 // Revenue attributed to social channels
  }
});

Compare ROI Across Channels

See which platform gives you the best return:

import os
import time
import requests

API_KEY = os.environ["SOCIAVAULT_API_KEY"]
BASE = "https://api.sociavault.com/v1/scrape"
HEADERS = {"X-API-Key": API_KEY}

def compare_channel_roi(accounts, channel_costs, channel_revenue):
    """Compare ROI across social channels"""
    results = []

    for platform, username in accounts.items():
        if platform == "instagram":
            r = requests.get(f"{BASE}/instagram/profile", headers=HEADERS,
                           params={"username": username})
            profile = r.json().get("data", {})
            followers = profile.get("follower_count", 0)
            time.sleep(1)

            r = requests.get(f"{BASE}/instagram/posts", headers=HEADERS,
                           params={"username": username})
            posts = r.json().get("data", [])
            total_eng = sum(p.get("like_count", 0) + p.get("comment_count", 0) for p in posts)
            time.sleep(1)

        elif platform == "tiktok":
            r = requests.get(f"{BASE}/tiktok/profile", headers=HEADERS,
                           params={"username": username})
            profile = r.json().get("data", {})
            followers = (profile.get("stats") or {}).get("followerCount", 0)
            time.sleep(1)

            r = requests.get(f"{BASE}/tiktok/user/posts", headers=HEADERS,
                           params={"username": username})
            posts = r.json().get("data", [])
            total_eng = sum((v.get("stats") or {}).get("diggCount", 0) for v in posts)
            time.sleep(1)

        elif platform == "twitter":
            r = requests.get(f"{BASE}/twitter/profile", headers=HEADERS,
                           params={"username": username})
            profile = r.json().get("data", {})
            followers = (profile.get("legacy") or {}).get("followers_count", 0)
            time.sleep(1)

            r = requests.get(f"{BASE}/twitter/user-tweets", headers=HEADERS,
                           params={"username": username})
            posts = r.json().get("data", [])
            total_eng = sum((t.get("legacy") or {}).get("favorite_count", 0) for t in posts)
            time.sleep(1)
        else:
            continue

        cost = channel_costs.get(platform, 0)
        revenue = channel_revenue.get(platform, 0)
        roi = ((revenue - cost) / max(cost, 1)) * 100
        cost_per_eng = total_eng > 0 and cost / total_eng or 0

        results.append({
            "platform": platform,
            "followers": followers,
            "total_engagement": total_eng,
            "cost": cost,
            "revenue": revenue,
            "roi": round(roi, 1),
            "cost_per_engagement": round(cost_per_eng, 3),
            "revenue_per_follower": round(revenue / max(followers, 1), 4)
        })

    results.sort(key=lambda x: x["roi"], reverse=True)

    print("\nšŸ“Š Channel ROI Comparison")
    print("=" * 60)

    print(f"\n  {'Platform':<12} {'Cost':>10} {'Revenue':>10} {'ROI':>8} {'CPE':>8}")
    print(f"  {'─' * 50}")

    for r in results:
        print(f"  {r['platform']:<12} ${r['cost']:>9,} ${r['revenue']:>9,} "
              f"{r['roi']:>7.1f}% ${r['cost_per_engagement']:>6.3f}")

    best = results[0]
    print(f"\n  Best ROI: {best['platform']} ({best['roi']}%)")
    print(f"  Recommendation: Shift budget from "
          f"{results[-1]['platform']} to {best['platform']}")

    return results

compare_channel_roi(
    accounts={"instagram": "yourhandle", "tiktok": "yourhandle", "twitter": "yourhandle"},
    channel_costs={"instagram": 2000, "tiktok": 1500, "twitter": 500},
    channel_revenue={"instagram": 12000, "tiktok": 8000, "twitter": 1500}
)

Metrics That Actually Matter for ROI

MetricVanity VersionROI Version
FollowersTotal follower countActive follower growth rate
EngagementTotal likesCost per engagement (CPE)
ImpressionsTotal impressionsRevenue per 1K impressions
TrafficProfile viewsSocial → website conversion rate
ContentTotal postsRevenue per post
GrowthMonthly follower gainCustomer acquisition cost from social

ROI Benchmarks by Industry

IndustryAverage Social ROITop Performers
E-commerce200-400%800%+
SaaS100-300%500%+
D2C brands150-350%700%+
Professional services50-150%300%+
Local business100-250%400%+
NonprofitsHard to measure $Focus on cost-per-donation
B2B50-200%300%+

If your ROI is under 100%, you're losing money on social. But check your attribution first — most teams under-attribute revenue to social because they only count last-click.


Common Attribution Mistakes

MistakeImpactFix
Last-click onlyMiss 60-80% of social's influenceUse multi-touch attribution
Ignoring dark socialMiss DMs, screenshots, word-of-mouthAdd survey "how did you hear?"
No UTM parametersCan't track social → siteUTM every link
Counting all followers as customersInflated expectationsTrack conversion rate by channel
Not valuing brand awarenessUndervalue top-of-funnelInclude assisted conversions

Get Started

Sign up free — start measuring social media ROI with real performance data from every platform.


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.