DEV Community

ahmet gedik
ahmet gedik

Posted on

How to Track Video Virality Metrics with YouTube API

Introduction

The YouTube Data API v3 provides everything you need to track how videos perform over time. In this tutorial, I'll show how to collect and store virality metrics — the approach used at ViralVidVault to monitor trending videos across 7 European regions.

Setting Up the API Client

First, get an API key from the Google Cloud Console. Then create a lightweight client:

<?php

class YouTubeClient
{
    private const BASE_URL = 'https://www.googleapis.com/youtube/v3';

    public function __construct(
        private readonly string $apiKey,
    ) {}

    public function getVideoStats(array $videoIds): array
    {
        $ids = implode(',', array_slice($videoIds, 0, 50)); // API max 50
        $url = self::BASE_URL . '/videos?' . http_build_query([
            'part' => 'statistics,snippet,contentDetails',
            'id' => $ids,
            'key' => $this->apiKey,
        ]);

        $response = file_get_contents($url);
        if ($response === false) {
            throw new \RuntimeException('YouTube API request failed');
        }

        return json_decode($response, true)['items'] ?? [];
    }

    public function getTrending(string $regionCode = 'US', int $maxResults = 25): array
    {
        $url = self::BASE_URL . '/videos?' . http_build_query([
            'part' => 'snippet,statistics,contentDetails',
            'chart' => 'mostPopular',
            'regionCode' => $regionCode,
            'maxResults' => min($maxResults, 50),
            'key' => $this->apiKey,
        ]);

        $response = file_get_contents($url);
        return json_decode($response, true)['items'] ?? [];
    }
}
Enter fullscreen mode Exit fullscreen mode

Storing Metrics Over Time

To detect virality, you need to compare metrics at different points in time. SQLite works great for this:

<?php

class MetricsStore
{
    private \PDO $db;

    public function __construct(string $dbPath)
    {
        $this->db = new \PDO("sqlite:{$dbPath}");
        $this->db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
        $this->db->exec('PRAGMA journal_mode=WAL');
        $this->migrate();
    }

    private function migrate(): void
    {
        $this->db->exec('
            CREATE TABLE IF NOT EXISTS video_metrics (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                video_id TEXT NOT NULL,
                region TEXT NOT NULL DEFAULT "US",
                views INTEGER NOT NULL,
                likes INTEGER NOT NULL DEFAULT 0,
                comments INTEGER NOT NULL DEFAULT 0,
                checked_at DATETIME DEFAULT CURRENT_TIMESTAMP
            );
            CREATE INDEX IF NOT EXISTS idx_metrics_video_region 
                ON video_metrics(video_id, region, checked_at);
        ');
    }

    public function record(string $videoId, string $region, array $stats): void
    {
        $stmt = $this->db->prepare('
            INSERT INTO video_metrics (video_id, region, views, likes, comments)
            VALUES (:vid, :region, :views, :likes, :comments)
        ');
        $stmt->execute([
            ':vid' => $videoId,
            ':region' => $region,
            ':views' => (int)($stats['viewCount'] ?? 0),
            ':likes' => (int)($stats['likeCount'] ?? 0),
            ':comments' => (int)($stats['commentCount'] ?? 0),
        ]);
    }

    public function getVelocity(string $videoId, string $region, int $windowSeconds = 3600): ?float
    {
        $stmt = $this->db->prepare('
            SELECT views, checked_at FROM video_metrics
            WHERE video_id = :vid AND region = :region
            ORDER BY checked_at DESC LIMIT 2
        ');
        $stmt->execute([':vid' => $videoId, ':region' => $region]);
        $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);

        if (count($rows) < 2) return null;

        $viewDelta = $rows[0]['views'] - $rows[1]['views'];
        $timeDelta = strtotime($rows[0]['checked_at']) - strtotime($rows[1]['checked_at']);

        if ($timeDelta <= 0) return null;
        return ($viewDelta / $timeDelta) * 3600; // views per hour
    }
}
Enter fullscreen mode Exit fullscreen mode

Fetching Multi-Region Trending Data

At ViralVidVault, we fetch trending videos from multiple regions in each cron run:

<?php

$regions = ['US', 'GB', 'PL', 'NL', 'SE', 'NO', 'AT'];
$client = new YouTubeClient($apiKey);
$store = new MetricsStore(__DIR__ . '/data/metrics.db');

foreach ($regions as $region) {
    $trending = $client->getTrending($region, 25);

    foreach ($trending as $video) {
        $store->record(
            videoId: $video['id'],
            region: $region,
            stats: $video['statistics']
        );

        $velocity = $store->getVelocity($video['id'], $region);
        if ($velocity !== null && $velocity > 5000) {
            echo "[{$region}] FAST MOVER: {$video['snippet']['title']} ({$velocity} views/hr)\n";
        }
    }

    // Respect rate limits
    usleep(200000); // 200ms between regions
}
Enter fullscreen mode Exit fullscreen mode

API Quota Management

YouTube API v3 gives you 10,000 quota units per day. A videos.list call costs 1 unit per request (not per video). Fetching 50 videos in one call is much cheaper than 50 individual calls.

// Efficient: 1 quota unit for 50 videos
$stats = $client->getVideoStats($fiftyVideoIds);

// Wasteful: 50 quota units
foreach ($fiftyVideoIds as $id) {
    $stats = $client->getVideoStats([$id]); // Don't do this
}
Enter fullscreen mode Exit fullscreen mode

With 7 regions and 25 videos per region, each cron run uses roughly 14 quota units (7 trending calls + 7 batch stat calls). Running every 7 hours gives us about 3 runs per day, totaling ~42 units. Plenty of headroom.

Key Takeaways

  • Batch your API calls (up to 50 video IDs per request)
  • Store metrics over time to calculate velocity
  • Use SQLite WAL mode for safe concurrent reads during cron
  • Normalize velocity per region for fair comparison
  • Rate limit your requests to stay within quota

This metrics tracking system is what powers the trending feeds at viralvidvault.com. The combination of multi-region data collection and velocity calculation lets us surface genuinely viral European content.


Part of the "Building ViralVidVault" series. Visit ViralVidVault to see these techniques in action.

Top comments (0)