DEV Community

ahmet gedik
ahmet gedik

Posted on

How We Handle YouTube API Quota Limits Efficiently

YouTube Data API v3 gives you 10,000 quota units per day. When you're fetching trending videos from 9 regions, quota management becomes critical. Here's how TopVideoHub handles it.

Understanding Quota Costs

Operation Cost Notes
videos.list 1 unit Per request, up to 50 results
search.list 100 units Expensive! Avoid when possible
channels.list 1 unit Per request
videoCategories.list 1 unit Fetch once, cache forever
playlistItems.list 1 unit Per request

The key insight: videos.list with chart=mostPopular costs 1 unit and returns trending videos. Never use search.list for trending — it costs 100x more.

Quota Tracking System

class QuotaTracker {
    private const int DAILY_LIMIT = 10000;
    private const int WARNING_THRESHOLD = 8000;
    private const int CRITICAL_THRESHOLD = 9000;

    public function __construct(private readonly \PDO $db) {
        $this->ensureTable();
    }

    private function ensureTable(): void {
        $this->db->exec("
            CREATE TABLE IF NOT EXISTS api_quota (
                id INTEGER PRIMARY KEY,
                key_id TEXT NOT NULL,
                date TEXT NOT NULL,
                operation TEXT NOT NULL,
                cost INTEGER NOT NULL,
                region TEXT,
                timestamp TEXT DEFAULT CURRENT_TIMESTAMP
            );
            CREATE INDEX IF NOT EXISTS idx_quota_date 
                ON api_quota(key_id, date);
        ");
    }

    public function record(string $keyId, string $operation, int $cost, ?string $region = null): void {
        $this->db->prepare(
            "INSERT INTO api_quota (key_id, date, operation, cost, region)
             VALUES (?, ?, ?, ?, ?)"
        )->execute([$keyId, date('Y-m-d'), $operation, $cost, $region]);
    }

    public function usedToday(string $keyId): int {
        return (int) $this->db->prepare(
            "SELECT COALESCE(SUM(cost), 0) FROM api_quota
             WHERE key_id = ? AND date = ?"
        )->execute([$keyId, date('Y-m-d')])
         ->fetchColumn();
    }

    public function remainingToday(string $keyId): int {
        return self::DAILY_LIMIT - $this->usedToday($keyId);
    }

    public function status(string $keyId): string {
        $used = $this->usedToday($keyId);
        return match(true) {
            $used >= self::CRITICAL_THRESHOLD => 'CRITICAL',
            $used >= self::WARNING_THRESHOLD  => 'WARNING',
            default                           => 'OK',
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

Multi-Key Rotation

Multiple API keys spread the quota load:

class KeyRotator {
    public function __construct(
        private readonly \PDO $db,
        private readonly QuotaTracker $quota,
    ) {}

    public function getBestKey(): ?string {
        $keys = $this->db->query(
            "SELECT key_value FROM api_keys WHERE active = 1"
        )->fetchAll(\PDO::FETCH_COLUMN);

        if (empty($keys)) return null;

        // Find key with most remaining quota
        $best = null;
        $bestRemaining = -1;

        foreach ($keys as $key) {
            $remaining = $this->quota->remainingToday($key);
            if ($remaining > $bestRemaining) {
                $best = $key;
                $bestRemaining = $remaining;
            }
        }

        // Don't return a key that's critically low
        if ($bestRemaining < 500) {
            return null; // All keys exhausted
        }

        return $best;
    }
}
Enter fullscreen mode Exit fullscreen mode

Staggered Cron Schedules

Different deployment targets fetch at different intervals to spread load:

# Site 1 (dwv): Every 2 hours — 12 fetches/day
20 */2 * * * php /path/to/fetch_videos.php

# Site 2 (tvh): Every 4 hours — 6 fetches/day
28 */4 * * * php /path/to/fetch_videos.php

# Site 3 (vvv): Every 7 hours — ~3 fetches/day
35 */7 * * * php /path/to/fetch_videos.php
Enter fullscreen mode Exit fullscreen mode

Smart Fetching: Prioritize What Changed

Don't re-fetch regions where nothing changed:

function shouldFetchRegion(string $region, \PDO $db): bool {
    $lastFetch = $db->prepare(
        "SELECT MAX(fetched_at) FROM video_regions WHERE region = ?"
    )->execute([$region])->fetchColumn();

    if ($lastFetch === null) return true;

    $elapsed = time() - strtotime($lastFetch);

    // High-traffic regions: fetch more often
    $interval = match($region) {
        'US', 'JP', 'KR' => 7200,   // 2 hours
        'GB', 'TW', 'SG' => 14400,  // 4 hours
        'VN', 'TH', 'HK' => 25200,  // 7 hours
    };

    return $elapsed >= $interval;
}
Enter fullscreen mode Exit fullscreen mode

Quota Budget Calculator

Before fetching, calculate if we have enough quota:

function calculateFetchBudget(array $regions, QuotaTracker $quota, string $keyId): array {
    $remaining = $quota->remainingToday($keyId);

    // Each region fetch costs:
    // - 1 unit for videos.list (trending)
    // - Optionally 1 unit per category (up to 10)
    $costPerRegion = 1; // Just trending, no category breakdown
    $totalCost = count($regions) * $costPerRegion;

    if ($totalCost > $remaining) {
        // Prioritize: fetch high-traffic regions first
        $priority = ['JP', 'KR', 'US', 'TW', 'GB', 'SG', 'TH', 'VN', 'HK'];
        $affordable = (int) floor($remaining / $costPerRegion);
        return array_slice(
            array_intersect($priority, $regions),
            0,
            $affordable
        );
    }

    return $regions;
}
Enter fullscreen mode Exit fullscreen mode

Monitoring Dashboard Output

At the end of each fetch, output a quota report:

function printQuotaReport(QuotaTracker $quota, string $keyId): void {
    $used = $quota->usedToday($keyId);
    $remaining = $quota->remainingToday($keyId);
    $status = $quota->status($keyId);
    $pct = round(($used / 10000) * 100, 1);

    echo "\n=== Quota Report ===";
    echo "\nKey: ..." . substr($keyId, -8);
    echo "\nUsed: {$used} / 10000 ({$pct}%)";
    echo "\nRemaining: {$remaining}";
    echo "\nStatus: {$status}";
    echo "\n===================\n";
}
Enter fullscreen mode Exit fullscreen mode

With this system, TopVideoHub stays well within quota limits while keeping content fresh across all 9 regions. The key is being smart about what you fetch and when.

Top comments (0)