Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungFliproom: a room changeover is a content problem(21.09.2026 um 04:10 Uhr)
Sichere ProgrammierungNova Adiutrix: My Second Agent Built My First Project's To-Do List(21.09.2026 um 04:11 Uhr)
IT Security ToolsAntiphishing v35456910988(21.09.2026 um 02:35 Uhr)
IT Security Toolsbrave-browser v1.98.12(21.09.2026 um 03:35 Uhr)
Sichere ProgrammierungFliproom: a room changeover is a content problem(21.09.2026 um 04:10 Uhr)
Sichere ProgrammierungNova Adiutrix: My Second Agent Built My First Project's To-Do List(21.09.2026 um 04:11 Uhr)
IT Security ToolsAntiphishing v35456910988(21.09.2026 um 02:35 Uhr)
IT Security Toolsbrave-browser v1.98.12(21.09.2026 um 03:35 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Build a Social Media ROI Calculator for Your Campaigns

Reagiere als Erste:r — dein Feedback zählt!

You spent $5,000 on influencer campaigns last month.

How much revenue did it generate? What's your cost per acquisition? Which platform performed best?

If you can't answer immediately, you're flying blind.

In this tutorial, we'll build a Social Media ROI Calculator that:

  1. Tracks campaign metrics across platforms
  2. Calculates real ROI, CAC, and ROAS
  3. Shows which creators and platforms deliver the best returns

Stop guessing if social media works. Start proving it with numbers.

Why ROI Tracking Matters

The harsh reality:

  • 60% of marketers can't prove social media ROI
  • Average influencer marketing ROAS is 5.2x (but varies wildly)
  • Brands that track ROI are 3x more likely to increase budgets

What you need to measure:

  • Cost per engagement (CPE)
  • Cost per acquisition (CAC)
  • Return on ad spend (ROAS)
  • Conversion rates by platform
  • Creator performance comparison

The Stack

  • Node.js: Runtime
  • SociaVault API: Fetch campaign content metrics
  • Express: Simple dashboard
  • SQLite: Store campaign data

Step 1: Setup

mkdir roi-calculator
cd roi-calculator
npm init -y
npm install axios dotenv express better-sqlite3

Create .env:

SOCIAVAULT_API_KEY=your_sociavault_key
PORT=3000

Step 2: Database Schema

Create database.js:

const Database = require('better-sqlite3');
const db = new Database('campaigns.db');

db.exec(`
  CREATE TABLE IF NOT EXISTS campaigns (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    platform TEXT,
    start_date TEXT,
    end_date TEXT,
    budget REAL DEFAULT 0,
    goal TEXT,
    status TEXT DEFAULT 'active',
    created_at TEXT DEFAULT CURRENT_TIMESTAMP
  );

  CREATE TABLE IF NOT EXISTS campaign_posts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    campaign_id INTEGER,
    creator_handle TEXT,
    platform TEXT,
    post_url TEXT UNIQUE,
    post_id TEXT,
    cost REAL DEFAULT 0,
    impressions INTEGER DEFAULT 0,
    reach INTEGER DEFAULT 0,
    views INTEGER DEFAULT 0,
    likes INTEGER DEFAULT 0,
    comments INTEGER DEFAULT 0,
    shares INTEGER DEFAULT 0,
    saves INTEGER DEFAULT 0,
    clicks INTEGER DEFAULT 0,
    conversions INTEGER DEFAULT 0,
    revenue REAL DEFAULT 0,
    tracked_at TEXT,
    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (campaign_id) REFERENCES campaigns(id)
  );

  CREATE TABLE IF NOT EXISTS tracking_links (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    campaign_id INTEGER,
    post_id INTEGER,
    original_url TEXT,
    tracking_url TEXT UNIQUE,
    clicks INTEGER DEFAULT 0,
    conversions INTEGER DEFAULT 0,
    revenue REAL DEFAULT 0,
    FOREIGN KEY (campaign_id) REFERENCES campaigns(id),
    FOREIGN KEY (post_id) REFERENCES campaign_posts(id)
  );

  CREATE INDEX IF NOT EXISTS idx_posts_campaign ON campaign_posts(campaign_id);
  CREATE INDEX IF NOT EXISTS idx_posts_platform ON campaign_posts(platform);
`);

module.exports = db;

Step 3: Metrics Fetcher

Create metrics.js:

require('dotenv').config();
const axios = require('axios');

const SOCIAVAULT_BASE = 'https://api.sociavault.com';
const headers = { 'Authorization': `Bearer ${process.env.SOCIAVAULT_API_KEY}` };

async function fetchTikTokMetrics(postUrl) {
  try {
    const response = await axios.get(`${SOCIAVAULT_BASE}/v1/scrape/tiktok/video-info`, {
      params: { url: postUrl },
      headers
    });

    const data = response.data.data;

    return {
      platform: 'tiktok',
      postId: data.id,
      views: data.playCount || data.stats?.playCount || 0,
      likes: data.diggCount || data.stats?.diggCount || 0,
      comments: data.commentCount || data.stats?.commentCount || 0,
      shares: data.shareCount || data.stats?.shareCount || 0,
      saves: data.collectCount || data.stats?.collectCount || 0,
      authorHandle: data.author?.uniqueId || data.authorMeta?.name,
      authorFollowers: data.author?.followerCount || 0
    };
  } catch (error) {
    console.error('TikTok metrics error:', error.message);
    return null;
  }
}

async function fetchInstagramMetrics(postUrl) {
  try {
    const response = await axios.get(`${SOCIAVAULT_BASE}/v1/scrape/instagram/post`, {
      params: { url: postUrl },
      headers
    });

    const data = response.data.data;

    return {
      platform: 'instagram',
      postId: data.id || data.pk,
      views: data.play_count || data.video_view_count || 0,
      likes: data.like_count || data.likes || 0,
      comments: data.comment_count || data.comments || 0,
      saves: data.save_count || 0,
      authorHandle: data.user?.username || data.owner?.username,
      authorFollowers: data.user?.follower_count || 0
    };
  } catch (error) {
    console.error('Instagram metrics error:', error.message);
    return null;
  }
}

async function fetchTwitterMetrics(postUrl) {
  try {
    const response = await axios.get(`${SOCIAVAULT_BASE}/v1/scrape/twitter/tweet`, {
      params: { url: postUrl },
      headers
    });

    const data = response.data.data;

    return {
      platform: 'twitter',
      postId: data.id || data.rest_id,
      views: data.views_count || data.views || 0,
      likes: data.favorite_count || data.likes || 0,
      retweets: data.retweet_count || 0,
      replies: data.reply_count || 0,
      quotes: data.quote_count || 0,
      authorHandle: data.user?.screen_name,
      authorFollowers: data.user?.followers_count || 0
    };
  } catch (error) {
    console.error('Twitter metrics error:', error.message);
    return null;
  }
}

async function fetchYouTubeMetrics(videoUrl) {
  try {
    const response = await axios.get(`${SOCIAVAULT_BASE}/v1/scrape/youtube/video`, {
      params: { url: videoUrl },
      headers
    });

    const data = response.data.data;

    return {
      platform: 'youtube',
      postId: data.id,
      views: data.viewCount || data.views || 0,
      likes: data.likeCount || data.likes || 0,
      comments: data.commentCount || data.comments || 0,
      authorHandle: data.channelTitle || data.channel?.name,
      authorSubscribers: data.channelSubscriberCount || 0
    };
  } catch (error) {
    console.error('YouTube metrics error:', error.message);
    return null;
  }
}

function detectPlatform(url) {
  if (url.includes('tiktok.com')) return 'tiktok';
  if (url.includes('instagram.com')) return 'instagram';
  if (url.includes('twitter.com') || url.includes('x.com')) return 'twitter';
  if (url.includes('youtube.com') || url.includes('youtu.be')) return 'youtube';
  return null;
}

async function fetchMetrics(postUrl) {
  const platform = detectPlatform(postUrl);

  switch (platform) {
    case 'tiktok': return fetchTikTokMetrics(postUrl);
    case 'instagram': return fetchInstagramMetrics(postUrl);
    case 'twitter': return fetchTwitterMetrics(postUrl);
    case 'youtube': return fetchYouTubeMetrics(postUrl);
    default: return null;
  }
}

module.exports = {
  fetchTikTokMetrics,
  fetchInstagramMetrics,
  fetchTwitterMetrics,
  fetchYouTubeMetrics,
  fetchMetrics,
  detectPlatform
};

Step 4: ROI Calculator Engine

Create calculator.js:

function calculateROI(campaign, posts) {
  const totalCost = posts.reduce((sum, p) => sum + (p.cost || 0), 0) || campaign.budget || 0;
  const totalRevenue = posts.reduce((sum, p) => sum + (p.revenue || 0), 0);
  const totalConversions = posts.reduce((sum, p) => sum + (p.conversions || 0), 0);
  const totalClicks = posts.reduce((sum, p) => sum + (p.clicks || 0), 0);

  // Engagement metrics
  const totalViews = posts.reduce((sum, p) => sum + (p.views || 0), 0);
  const totalLikes = posts.reduce((sum, p) => sum + (p.likes || 0), 0);
  const totalComments = posts.reduce((sum, p) => sum + (p.comments || 0), 0);
  const totalShares = posts.reduce((sum, p) => sum + (p.shares || 0), 0);
  const totalEngagement = totalLikes + totalComments + totalShares;

  // Core ROI metrics
  const roi = totalCost > 0 ? ((totalRevenue - totalCost) / totalCost) * 100 : 0;
  const roas = totalCost > 0 ? totalRevenue / totalCost : 0;

  // Cost metrics
  const cpm = totalViews > 0 ? (totalCost / totalViews) * 1000 : 0;
  const cpe = totalEngagement > 0 ? totalCost / totalEngagement : 0;
  const cpc = totalClicks > 0 ? totalCost / totalClicks : 0;
  const cpa = totalConversions > 0 ? totalCost / totalConversions : 0;

  // Conversion metrics
  const conversionRate = totalClicks > 0 ? (totalConversions / totalClicks) * 100 : 0;
  const engagementRate = totalViews > 0 ? (totalEngagement / totalViews) * 100 : 0;
  const clickThroughRate = totalViews > 0 ? (totalClicks / totalViews) * 100 : 0;

  return {
    overview: {
      totalCost,
      totalRevenue,
      profit: totalRevenue - totalCost,
      roi: roi.toFixed(2),
      roas: roas.toFixed(2)
    },
    engagement: {
      totalViews,
      totalLikes,
      totalComments,
      totalShares,
      totalEngagement,
      engagementRate: engagementRate.toFixed(2)
    },
    conversions: {
      totalClicks,
      totalConversions,
      clickThroughRate: clickThroughRate.toFixed(2),
      conversionRate: conversionRate.toFixed(2)
    },
    costMetrics: {
      cpm: cpm.toFixed(2),
      cpe: cpe.toFixed(2),
      cpc: cpc.toFixed(2),
      cpa: cpa.toFixed(2)
    }
  };
}

function calculateCreatorPerformance(posts) {
  const creatorMap = {};

  posts.forEach(post => {
    const creator = post.creator_handle || 'Unknown';

    if (!creatorMap[creator]) {
      creatorMap[creator] = {
        handle: creator,
        platform: post.platform,
        posts: 0,
        totalCost: 0,
        totalRevenue: 0,
        totalViews: 0,
        totalEngagement: 0,
        totalClicks: 0,
        totalConversions: 0
      };
    }

    creatorMap[creator].posts++;
    creatorMap[creator].totalCost += post.cost || 0;
    creatorMap[creator].totalRevenue += post.revenue || 0;
    creatorMap[creator].totalViews += post.views || 0;
    creatorMap[creator].totalEngagement += (post.likes || 0) + (post.comments || 0) + (post.shares || 0);
    creatorMap[creator].totalClicks += post.clicks || 0;
    creatorMap[creator].totalConversions += post.conversions || 0;
  });

  // Calculate performance metrics for each creator
  return Object.values(creatorMap).map(creator => ({
    ...creator,
    roas: creator.totalCost > 0 ? (creator.totalRevenue / creator.totalCost).toFixed(2) : 0,
    cpe: creator.totalEngagement > 0 ? (creator.totalCost / creator.totalEngagement).toFixed(2) : 0,
    cpa: creator.totalConversions > 0 ? (creator.totalCost / creator.totalConversions).toFixed(2) : 'N/A',
    conversionRate: creator.totalClicks > 0 ? ((creator.totalConversions / creator.totalClicks) * 100).toFixed(2) : 0,
    engagementRate: creator.totalViews > 0 ? ((creator.totalEngagement / creator.totalViews) * 100).toFixed(2) : 0
  })).sort((a, b) => parseFloat(b.roas) - parseFloat(a.roas));
}

function calculatePlatformPerformance(posts) {
  const platformMap = {};

  posts.forEach(post => {
    const platform = post.platform || 'Unknown';

    if (!platformMap[platform]) {
      platformMap[platform] = {
        platform,
        posts: 0,
        totalCost: 0,
        totalRevenue: 0,
        totalViews: 0,
        totalEngagement: 0,
        totalClicks: 0,
        totalConversions: 0
      };
    }

    platformMap[platform].posts++;
    platformMap[platform].totalCost += post.cost || 0;
    platformMap[platform].totalRevenue += post.revenue || 0;
    platformMap[platform].totalViews += post.views || 0;
    platformMap[platform].totalEngagement += (post.likes || 0) + (post.comments || 0) + (post.shares || 0);
    platformMap[platform].totalClicks += post.clicks || 0;
    platformMap[platform].totalConversions += post.conversions || 0;
  });

  return Object.values(platformMap).map(p => ({
    ...p,
    roas: p.totalCost > 0 ? (p.totalRevenue / p.totalCost).toFixed(2) : 0,
    cpm: p.totalViews > 0 ? ((p.totalCost / p.totalViews) * 1000).toFixed(2) : 0,
    cpa: p.totalConversions > 0 ? (p.totalCost / p.totalConversions).toFixed(2) : 'N/A'
  })).sort((a, b) => parseFloat(b.roas) - parseFloat(a.roas));
}

function generateBenchmarks(metrics) {
  // Industry benchmarks for comparison
  const benchmarks = {
    tiktok: { avgCPM: 10, avgCPE: 0.02, avgEngagement: 5, avgRoas: 5 },
    instagram: { avgCPM: 8, avgCPE: 0.05, avgEngagement: 3, avgRoas: 4 },
    youtube: { avgCPM: 20, avgCPE: 0.10, avgEngagement: 4, avgRoas: 6 },
    twitter: { avgCPM: 6, avgCPE: 0.03, avgEngagement: 2, avgRoas: 3 }
  };

  const comparisons = {};

  Object.keys(benchmarks).forEach(platform => {
    const benchmark = benchmarks[platform];
    const actual = metrics.platformPerformance?.find(p => p.platform === platform);

    if (actual) {
      comparisons[platform] = {
        cpm: {
          yours: parseFloat(actual.cpm),
          benchmark: benchmark.avgCPM,
          status: parseFloat(actual.cpm) < benchmark.avgCPM ? 'better' : 'worse'
        },
        roas: {
          yours: parseFloat(actual.roas),
          benchmark: benchmark.avgRoas,
          status: parseFloat(actual.roas) > benchmark.avgRoas ? 'better' : 'worse'
        },
        engagement: {
          yours: parseFloat(actual.totalEngagement / actual.totalViews * 100).toFixed(2),
          benchmark: benchmark.avgEngagement,
          status: (actual.totalEngagement / actual.totalViews * 100) > benchmark.avgEngagement ? 'better' : 'worse'
        }
      };
    }
  });

  return comparisons;
}

module.exports = {
  calculateROI,
  calculateCreatorPerformance,
  calculatePlatformPerformance,
  generateBenchmarks
};

Step 5: API Server

Create server.js:

require('dotenv').config();
const express = require('express');
const db = require('./database');
const { fetchMetrics, detectPlatform } = require('./metrics');
const { calculateROI, calculateCreatorPerformance, calculatePlatformPerformance, generateBenchmarks } = require('./calculator');

const app = express();
app.use(express.json());

// Create campaign
app.post('/api/campaigns', (req, res) => {
  const { name, platform, start_date, end_date, budget, goal } = req.body;

  const stmt = db.prepare(`
    INSERT INTO campaigns (name, platform, start_date, end_date, budget, goal)
    VALUES (?, ?, ?, ?, ?, ?)
  `);

  const result = stmt.run(name, platform, start_date, end_date, budget, goal);
  res.json({ success: true, id: result.lastInsertRowid });
});

// Get all campaigns
app.get('/api/campaigns', (req, res) => {
  const campaigns = db.prepare('SELECT * FROM campaigns ORDER BY created_at DESC').all();
  res.json(campaigns);
});

// Get campaign with full analytics
app.get('/api/campaigns/:id', (req, res) => {
  const campaign = db.prepare('SELECT * FROM campaigns WHERE id = ?').get(req.params.id);

  if (!campaign) {
    return res.status(404).json({ error: 'Campaign not found' });
  }

  const posts = db.prepare('SELECT * FROM campaign_posts WHERE campaign_id = ?').all(req.params.id);

  // Calculate all metrics
  const roi = calculateROI(campaign, posts);
  const creatorPerformance = calculateCreatorPerformance(posts);
  const platformPerformance = calculatePlatformPerformance(posts);
  const benchmarks = generateBenchmarks({ platformPerformance });

  res.json({
    campaign,
    posts,
    analytics: {
      ...roi,
      creatorPerformance,
      platformPerformance,
      benchmarks
    }
  });
});

// Add post to campaign
app.post('/api/campaigns/:id/posts', async (req, res) => {
  const { post_url, creator_handle, cost, clicks, conversions, revenue } = req.body;

  // Fetch metrics from social platform
  const metrics = await fetchMetrics(post_url);

  if (!metrics) {
    return res.status(400).json({ error: 'Could not fetch post metrics' });
  }

  const stmt = db.prepare(`
    INSERT INTO campaign_posts (
      campaign_id, creator_handle, platform, post_url, post_id,
      cost, views, likes, comments, shares, saves,
      clicks, conversions, revenue, tracked_at
    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    ON CONFLICT(post_url) DO UPDATE SET
      views = excluded.views,
      likes = excluded.likes,
      comments = excluded.comments,
      shares = excluded.shares,
      saves = excluded.saves,
      clicks = excluded.clicks,
      conversions = excluded.conversions,
      revenue = excluded.revenue,
      tracked_at = excluded.tracked_at
  `);

  const result = stmt.run(
    req.params.id,
    creator_handle || metrics.authorHandle,
    metrics.platform,
    post_url,
    metrics.postId,
    cost || 0,
    metrics.views || 0,
    metrics.likes || 0,
    metrics.comments || 0,
    metrics.shares || 0,
    metrics.saves || 0,
    clicks || 0,
    conversions || 0,
    revenue || 0,
    new Date().toISOString()
  );

  res.json({ success: true, id: result.lastInsertRowid, metrics });
});

// Update post conversion data
app.patch('/api/posts/:id', (req, res) => {
  const { clicks, conversions, revenue, cost } = req.body;

  const updates = [];
  const params = [];

  if (clicks !== undefined) {
    updates.push('clicks = ?');
    params.push(clicks);
  }
  if (conversions !== undefined) {
    updates.push('conversions = ?');
    params.push(conversions);
  }
  if (revenue !== undefined) {
    updates.push('revenue = ?');
    params.push(revenue);
  }
  if (cost !== undefined) {
    updates.push('cost = ?');
    params.push(cost);
  }

  if (updates.length === 0) {
    return res.status(400).json({ error: 'No updates provided' });
  }

  params.push(req.params.id);
  db.prepare(`UPDATE campaign_posts SET ${updates.join(', ')} WHERE id = ?`).run(...params);

  res.json({ success: true });
});

// Refresh post metrics
app.post('/api/posts/:id/refresh', async (req, res) => {
  const post = db.prepare('SELECT * FROM campaign_posts WHERE id = ?').get(req.params.id);

  if (!post) {
    return res.status(404).json({ error: 'Post not found' });
  }

  const metrics = await fetchMetrics(post.post_url);

  if (!metrics) {
    return res.status(400).json({ error: 'Could not refresh metrics' });
  }

  db.prepare(`
    UPDATE campaign_posts SET
      views = ?, likes = ?, comments = ?, shares = ?, saves = ?,
      tracked_at = ?
    WHERE id = ?
  `).run(
    metrics.views || 0,
    metrics.likes || 0,
    metrics.comments || 0,
    metrics.shares || 0,
    metrics.saves || 0,
    new Date().toISOString(),
    req.params.id
  );

  res.json({ success: true, metrics });
});

// Overall dashboard stats
app.get('/api/dashboard', (req, res) => {
  const campaigns = db.prepare('SELECT * FROM campaigns WHERE status = "active"').all();
  const allPosts = db.prepare('SELECT * FROM campaign_posts').all();

  const totalSpend = allPosts.reduce((sum, p) => sum + (p.cost || 0), 0);
  const totalRevenue = allPosts.reduce((sum, p) => sum + (p.revenue || 0), 0);
  const totalConversions = allPosts.reduce((sum, p) => sum + (p.conversions || 0), 0);
  const totalViews = allPosts.reduce((sum, p) => sum + (p.views || 0), 0);

  const overallRoas = totalSpend > 0 ? totalRevenue / totalSpend : 0;
  const overallRoi = totalSpend > 0 ? ((totalRevenue - totalSpend) / totalSpend) * 100 : 0;

  res.json({
    activeCampaigns: campaigns.length,
    totalPosts: allPosts.length,
    totalSpend,
    totalRevenue,
    profit: totalRevenue - totalSpend,
    totalConversions,
    totalViews,
    overallRoas: overallRoas.toFixed(2),
    overallRoi: overallRoi.toFixed(2),
    platformBreakdown: calculatePlatformPerformance(allPosts),
    topCreators: calculateCreatorPerformance(allPosts).slice(0, 5)
  });
});

// Dashboard HTML
app.get('/', (req, res) => {
  res.send(`
<!DOCTYPE html>
<html>
<head>
  <title>Social Media ROI Calculator</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font-family: -apple-system, sans-serif; background: #0f172a; color: #e2e8f0; }
    .container { max-width: 1400px; margin: 0 auto; padding: 20px; }
    h1 { font-size: 1.5rem; margin-bottom: 20px; }
    .grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 15px; margin-bottom: 30px; }
    .card { background: #1e293b; padding: 20px; border-radius: 12px; }
    .card-value { font-size: 2rem; font-weight: bold; }
    .card-label { color: #94a3b8; font-size: 0.875rem; }
    .positive { color: #22c55e; }
    .negative { color: #ef4444; }
    .section { margin-bottom: 30px; }
    .section-title { font-size: 1.25rem; margin-bottom: 15px; }
    table { width: 100%; border-collapse: collapse; background: #1e293b; border-radius: 8px; overflow: hidden; }
    th, td { padding: 12px 16px; text-align: left; border-bottom: 1px solid #334155; }
    th { background: #0f172a; font-weight: 600; }
    .form-row { display: flex; gap: 10px; margin-bottom: 20px; }
    input, select { padding: 10px; border: 1px solid #334155; border-radius: 6px; background: #1e293b; color: #fff; flex: 1; }
    button { padding: 10px 20px; background: #3b82f6; color: white; border: none; border-radius: 6px; cursor: pointer; }
    button:hover { background: #2563eb; }
  </style>
</head>
<body>
  <div class="container">
    <h1>📊 Social Media ROI Calculator</h1>

    <div class="grid" id="overviewCards"></div>

    <div class="section">
      <h2 class="section-title">➕ Track New Post</h2>
      <div class="form-row">
        <input type="text" id="postUrl" placeholder="Post URL (TikTok, Instagram, Twitter, YouTube)">
        <input type="number" id="postCost" placeholder="Cost ($)">
        <input type="number" id="postClicks" placeholder="Clicks">
        <input type="number" id="postConversions" placeholder="Conversions">
        <input type="number" id="postRevenue" placeholder="Revenue ($)">
        <button onclick="addPost()">Track Post</button>
      </div>
    </div>

    <div class="section">
      <h2 class="section-title">🏆 Top Performing Creators</h2>
      <table id="creatorsTable"></table>
    </div>

    <div class="section">
      <h2 class="section-title">📱 Platform Performance</h2>
      <table id="platformsTable"></table>
    </div>
  </div>

  <script>
    let currentCampaignId = 1;

    async function loadDashboard() {
      const res = await fetch('/api/dashboard');
      const data = await res.json();

      document.getElementById('overviewCards').innerHTML = \`
        <div class="card"><div class="card-value">\${formatMoney(data.totalSpend)}</div><div class="card-label">Total Spend</div></div>
        <div class="card"><div class="card-value">\${formatMoney(data.totalRevenue)}</div><div class="card-label">Total Revenue</div></div>
        <div class="card"><div class="card-value \${data.profit >= 0 ? 'positive' : 'negative'}">\${formatMoney(data.profit)}</div><div class="card-label">Profit</div></div>
        <div class="card"><div class="card-value">\${data.overallRoas}x</div><div class="card-label">ROAS</div></div>
        <div class="card"><div class="card-value \${data.overallRoi >= 0 ? 'positive' : 'negative'}">\${data.overallRoi}%</div><div class="card-label">ROI</div></div>
      \`;

      document.getElementById('creatorsTable').innerHTML = \`
        <tr><th>Creator</th><th>Platform</th><th>Posts</th><th>Cost</th><th>Revenue</th><th>ROAS</th><th>CPA</th></tr>
        \${data.topCreators.map(c => \`
          <tr>
            <td>@\${c.handle}</td>
            <td>\${c.platform}</td>
            <td>\${c.posts}</td>
            <td>\${formatMoney(c.totalCost)}</td>
            <td>\${formatMoney(c.totalRevenue)}</td>
            <td class="\${c.roas >= 1 ? 'positive' : 'negative'}">\${c.roas}x</td>
            <td>\${c.cpa}</td>
          </tr>
        \`).join('')}
      \`;

      document.getElementById('platformsTable').innerHTML = \`
        <tr><th>Platform</th><th>Posts</th><th>Views</th><th>Cost</th><th>Revenue</th><th>ROAS</th><th>CPM</th></tr>
        \${data.platformBreakdown.map(p => \`
          <tr>
            <td>\${p.platform}</td>
            <td>\${p.posts}</td>
            <td>\${formatNumber(p.totalViews)}</td>
            <td>\${formatMoney(p.totalCost)}</td>
            <td>\${formatMoney(p.totalRevenue)}</td>
            <td class="\${p.roas >= 1 ? 'positive' : 'negative'}">\${p.roas}x</td>
            <td>$\${p.cpm}</td>
          </tr>
        \`).join('')}
      \`;
    }

    async function addPost() {
      const data = {
        post_url: document.getElementById('postUrl').value,
        cost: parseFloat(document.getElementById('postCost').value) || 0,
        clicks: parseInt(document.getElementById('postClicks').value) || 0,
        conversions: parseInt(document.getElementById('postConversions').value) || 0,
        revenue: parseFloat(document.getElementById('postRevenue').value) || 0
      };

      await fetch('/api/campaigns/' + currentCampaignId + '/posts', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data)
      });

      document.getElementById('postUrl').value = '';
      document.getElementById('postCost').value = '';
      document.getElementById('postClicks').value = '';
      document.getElementById('postConversions').value = '';
      document.getElementById('postRevenue').value = '';

      loadDashboard();
    }

    function formatMoney(n) { return '$' + (n || 0).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}); }
    function formatNumber(n) { 
      if (n >= 1000000) return (n/1000000).toFixed(1) + 'M';
      if (n >= 1000) return (n/1000).toFixed(1) + 'K';
      return n || 0;
    }

    // Create default campaign
    fetch('/api/campaigns', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: 'Default Campaign', budget: 0 })
    }).then(() => loadDashboard());
  </script>
</body>
</html>
  `);
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`\n📊 ROI Calculator running at http://localhost:${PORT}\n`);
});

Step 6: Run It

node server.js

Open http://localhost:3000 and start tracking your campaigns!

Sample Output

═══════════════════════════════════════════════════════════════════
📊 CAMPAIGN ROI REPORT: Q4 Influencer Campaign
═══════════════════════════════════════════════════════════════════

💰 FINANCIAL OVERVIEW
───────────────────────────────────────────────────────────────────
Total Spend:        $15,000.00
Total Revenue:      $67,500.00
Profit:             $52,500.00
ROI:                350%
ROAS:               4.5x

📈 ENGAGEMENT METRICS
───────────────────────────────────────────────────────────────────
Total Views:        12,450,000
Total Likes:        892,000
Total Comments:     45,600
Total Shares:       23,400
Engagement Rate:    7.7%

🎯 CONVERSION METRICS
───────────────────────────────────────────────────────────────────
Total Clicks:       45,200
Conversions:        1,125
Click-Through Rate: 0.36%
Conversion Rate:    2.49%

💵 COST METRICS
───────────────────────────────────────────────────────────────────
CPM (per 1K views): $1.20
CPE (per engage):   $0.016
CPC (per click):    $0.33
CPA (per conv):     $13.33

🏆 TOP CREATORS BY ROAS
───────────────────────────────────────────────────────────────────
1. @skincare_sarah    ROAS: 8.2x   CPA: $8.50   Revenue: $16,400
2. @fitness_mike      ROAS: 6.1x   CPA: $11.20  Revenue: $12,200
3. @lifestyle_jen     ROAS: 4.8x   CPA: $14.60  Revenue: $9,600
4. @beauty_alex       ROAS: 3.5x   CPA: $18.90  Revenue: $7,000
5. @fashion_kim       ROAS: 2.9x   CPA: $22.50  Revenue: $5,800

📱 PLATFORM COMPARISON
───────────────────────────────────────────────────────────────────
Platform    | Spend    | Revenue  | ROAS  | CPM    | CPA
TikTok      | $6,000   | $32,400  | 5.4x  | $0.80  | $10.20
Instagram   | $5,500   | $22,000  | 4.0x  | $1.40  | $15.40
YouTube     | $3,500   | $13,100  | 3.7x  | $2.10  | $17.80

📊 BENCHMARK COMPARISON
───────────────────────────────────────────────────────────────────
                    Yours    Benchmark    Status
TikTok ROAS         5.4x     5.0x         ✓ Above average
TikTok CPM          $0.80    $10.00       ✓ 12x better
Instagram ROAS      4.0x     4.0x         ✓ At benchmark
YouTube ROAS        3.7x     6.0x         ⚠ Below average

What You Just Built

Marketing attribution tools cost a fortune:

  • Triple Whale: $100+/month
  • Rockerbox: $500+/month
  • Northbeam: $1000+/month

Your version tracks ROI across platforms for free (just API costs).

Get Started

  1. Get your SociaVault API Key
  2. Run the calculator
  3. Track every influencer post

Stop guessing. Start measuring.

If you can't measure it, you can't improve it.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Build a Social Media ROI Calculator for Your Campaigns

Thematisch verwandte Begriffe: Build, Social, Media, Calculator · 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 ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-93977 | A vulnerability was determined in code-projects Assessment Management 1.…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick