DEV Community

ahmet gedik
ahmet gedik

Posted on

How to Use YouTube Data API for Regional Trending Videos

YouTube Data API v3 lets you fetch trending videos for specific countries. Here's a practical guide based on building TopVideoHub, which fetches trending data from 9 Asia-Pacific regions.

Getting Started

You need a Google Cloud project with YouTube Data API v3 enabled and an API key.

# Test with curl first
curl "https://www.googleapis.com/youtube/v3/videos?part=snippet,statistics&chart=mostPopular&regionCode=JP&maxResults=10&key=YOUR_KEY"
Enter fullscreen mode Exit fullscreen mode

PHP Implementation

Here's a clean PHP 8.3 implementation:

<?php
declare(strict_types=1);

enum Region: string {
    case US = 'US';
    case GB = 'GB';
    case JP = 'JP';
    case KR = 'KR';
    case TW = 'TW';
    case SG = 'SG';
    case VN = 'VN';
    case TH = 'TH';
    case HK = 'HK';
}

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

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

    /**
     * Fetch trending videos for a region.
     * API cost: 1 quota unit per call (videos.list with chart=mostPopular)
     */
    public function fetchTrending(
        Region $region,
        int $maxResults = 50,
        ?string $categoryId = null,
    ): array {
        $params = [
            'part'       => 'snippet,statistics,contentDetails',
            'chart'      => 'mostPopular',
            'regionCode' => $region->value,
            'maxResults'  => min($maxResults, 50),
            'key'        => $this->apiKey,
        ];

        if ($categoryId !== null) {
            $params['videoCategoryId'] = $categoryId;
        }

        $url = self::BASE_URL . '/videos?' . http_build_query($params);
        $response = $this->request($url);
        $this->quota->record(cost: 1);

        return array_map(
            fn(array $item) => $this->normalizeVideo($item, $region),
            $response['items'] ?? []
        );
    }

    private function normalizeVideo(array $item, Region $region): array {
        $snippet = $item['snippet'];
        $stats = $item['statistics'] ?? [];

        return [
            'video_id'      => $item['id'],
            'title'         => $snippet['title'],
            'channel_title' => $snippet['channelTitle'],
            'channel_id'    => $snippet['channelId'],
            'description'   => mb_substr($snippet['description'] ?? '', 0, 500),
            'thumbnail_url' => $this->bestThumbnail($snippet['thumbnails']),
            'published_at'  => $snippet['publishedAt'],
            'category_id'   => (int)($snippet['categoryId'] ?? 0),
            'view_count'    => (int)($stats['viewCount'] ?? 0),
            'like_count'    => (int)($stats['likeCount'] ?? 0),
            'comment_count' => (int)($stats['commentCount'] ?? 0),
            'region'        => $region->value,
            'duration'      => $item['contentDetails']['duration'] ?? '',
        ];
    }

    private function bestThumbnail(array $thumbs): string {
        foreach (['maxres', 'standard', 'high', 'medium', 'default'] as $key) {
            if (isset($thumbs[$key]['url'])) {
                return $thumbs[$key]['url'];
            }
        }
        return '';
    }

    private function request(string $url): array {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 30,
            CURLOPT_SSL_VERIFYPEER => true,
        ]);

        $body = curl_exec($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($code !== 200 || $body === false) {
            throw new \RuntimeException("API request failed: HTTP {$code}");
        }

        return json_decode($body, true, 512, JSON_THROW_ON_ERROR);
    }
}
Enter fullscreen mode Exit fullscreen mode

Quota Management

YouTube gives you 10,000 quota units per day. Here's the cost breakdown:

Operation Cost
videos.list 1 unit
search.list 100 units
channels.list 1 unit
commentThreads.list 1 unit

For trending videos, always use videos.list with chart=mostPopular (1 unit) instead of search.list (100 units). This is 100x more efficient.

With 9 regions at 1 unit each, you can fetch all regions 1,111 times per day before hitting the quota. At TopVideoHub, we fetch every 2-4 hours, which uses roughly 100-200 units per day — well within limits.

Handling Categories

YouTube has a fixed set of video categories. Fetch them once and cache:

public function fetchCategories(Region $region): array {
    $url = self::BASE_URL . '/videoCategories?' . http_build_query([
        'part'       => 'snippet',
        'regionCode' => $region->value,
        'key'        => $this->apiKey,
    ]);

    $response = $this->request($url);
    $this->quota->record(cost: 1);

    $categories = [];
    foreach ($response['items'] as $item) {
        if ($item['snippet']['assignable'] ?? false) {
            $categories[$item['id']] = $item['snippet']['title'];
        }
    }
    return $categories;
}
Enter fullscreen mode Exit fullscreen mode

Note: available categories vary by region. Japan has categories that don't exist in the US, and vice versa.

Fetching Multiple Regions Efficiently

$api = new YouTubeApi($apiKey, $quota);
$allVideos = [];

foreach (Region::cases() as $region) {
    try {
        $videos = $api->fetchTrending($region, maxResults: 50);
        $allVideos[$region->value] = $videos;
        echo "[OK] {$region->value}: " . count($videos) . " videos\n";
    } catch (\Throwable $e) {
        echo "[ERR] {$region->value}: {$e->getMessage()}\n";
    }
}
Enter fullscreen mode Exit fullscreen mode

Pro Tips

  1. Cache responses aggressively — Trending data doesn't change every minute. Fetch every 2-4 hours.
  2. Use videos.list not search.list — 100x cheaper in quota.
  3. Handle rate limits gracefully — YouTube returns HTTP 429 when you exceed quotas. Implement exponential backoff.
  4. Store raw responses — Keep the full API response in your cache so you can extract additional fields later without re-fetching.

This approach powers TopVideoHub, serving trending video data from 9 regions reliably within free API quota limits.

Top comments (0)