Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Youtube Api Laravel Package

madcoda/php-youtube-api

A lightweight PHP wrapper for the YouTube Data API v3 (non-OAuth) that makes it easy to fetch public video, channel, and playlist data. No third-party runtime dependencies, and it returns decoded JSON so you can follow the Google docs directly.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require madcoda/php-youtube-api:^1.2

Grab a Server key (or browser key if you only call from the client, which you shouldn't) from Google Cloud Console → APIs & Services → Credentials, with YouTube Data API v3 enabled. Then either use the class directly or wire the bundled Laravel provider:

// config/app.php (packages this old usually aren't auto-discovered)
'providers' => [Madcoda\Youtube\Laravel\YoutubeServiceProvider::class],
'aliases'   => ['Youtube' => Madcoda\Youtube\Facades\Youtube::class],
php artisan vendor:publish --provider="Madcoda\Youtube\Laravel\YoutubeServiceProvider" --tag=config
// config/youtube.php
'key'     => env('YOUTUBE_API_KEY'),
'referer' => env('YOUTUBE_REFERER', ''), // set if the key is HTTP-referrer restricted

First call — verify the key, quota, and payload shape in one hit:

$video = Youtube::getVideoInfo('dQw4w9WgXcQ', 'snippet,statistics,contentDetails');
dump($video->snippet->title, $video->statistics->viewCount);

Where to look first:

  1. The package's Youtube.php class — it is ~500 lines and every method is a thin part/params passthrough to a cURL call. Reading it once takes ten minutes and removes all mystery.
  2. The API Reference wiki for the method→endpoint map.
  3. Google's own videos.list / search.list docs for the accepted part and filter parameters — the library deliberately does not abstract these, so Google's docs are your API docs from here on.

A good first use case is metadata hydration: take a YouTube ID or URL already in your app, call getVideoInfo, cache the result, and render title/thumbnail/duration instead of a bare iframe.

Implementation Patterns

Wrap it, don't sprinkle it. The library is a concrete class with no interface, so put it behind one of your own. You get caching, logging, error normalisation, and a swap path to googleapis/google-api-php-client later:

// app/Services/YouTube/YouTubeClient.php
interface YouTubeClient {
    public function video(string $id): ?object;
    public function search(string $q, int $max = 10): array;
}

// app/Services/YouTube/MadcodaYouTubeClient.php
class MadcodaYouTubeClient implements YouTubeClient
{
    public function __construct(private \Madcoda\Youtube\Youtube $api) {}

    public function video(string $id): ?object
    {
        return Cache::remember("yt:video:$id", now()->addDay(), function () use ($id) {
            $v = $this->api->getVideoInfo($id, 'snippet,statistics,contentDetails');
            if (!$v || isset($v->error)) { Log::warning('YT video failed', ['id' => $id, 'res' => $v]); return null; }
            return $v;
        });
    }
}
// app/Providers/AppServiceProvider.php
$this->app->singleton(\Madcoda\Youtube\Youtube::class, fn () => new \Madcoda\Youtube\Youtube([
    'key'     => config('youtube.key'),
    'referer' => config('youtube.referer'),
]));
$this->app->bind(YouTubeClient::class, MadcodaYouTubeClient::class);

Keep the Youtube facade for tinker/one-off scripts, but inject YouTubeClient in controllers, jobs and commands.

Two-step search → hydrate. search.list costs 100 quota units; videos.list costs 1. Search once to get IDs, then hydrate in batches of 50 in a single videos.list call. This is the single biggest quota lever you have:

$ids = collect(Youtube::search($q, 25))
    ->pluck('id.videoId')->filter()->take(50)->all();

$full = Youtube::getVideosByParameters('snippet,statistics', ['id' => implode(',', $ids)]);

Pagination. List endpoints cap at 50 (search at 50, videos at 50). Loop on nextPageToken; the advanced methods accept arbitrary params:

$params = ['playlistId' => $playlistId, 'maxResults' => 50, 'part' => 'snippet,contentDetails'];
do {
    $res = Youtube::getPlaylistItemsByPlaylistIdAdvanced($params, true);
    foreach ($res->items ?? [] as $item) { /* ... */ }
    $params['pageToken'] = $res->nextPageToken ?? null;
} while ($params['pageToken']);

Push bulk work to a queue. Channel/playlist backfills are dozens of calls — do them in a ShouldQueue job with RateLimited/WithoutOverlapping middleware, and cache aggressively. Trending data: 5–15 min TTL. Channel metadata: 24 h. Video statistics: hourly.

Testing. Because there's no interface, bind a fake of your interface:

$this->instance(YouTubeClient::class, new class implements YouTubeClient {
    public function video(string $id): ?object { return json_decode(file_get_contents(base_path('tests/fixtures/video.json'))); }
    public function search(string $q, int $max = 10): array { return []; }
});

Alternatively point the library's public $apis URL map at a local stub server for a real end-to-end cURL test.

Gotchas and Tips

  • Return shapes are inconsistent. getVideoInfo() returns a single decoded object (or false when nothing matches); search(), getPlaylistItemsByPlaylistId() etc. return an array of items (or a decoded response object for the advanced *Advanced() variants). Don't assume one shape across the API.
  • HTTP errors are not exceptions. The library returns the decoded JSON body regardless of status. A quota-exceeded or bad-key response arrives as $result->error->errors[0]->reason (quotaExceeded, keyInvalid, ipRefererBlocked, rateLimitExceeded). Always guard isset($result->error) before touching ->items, and translate to your own exception/report().
  • Missing part = missing data. The default part is usually just snippet. If ->statistics or ->contentDetails->duration is undefined, you forgot to request it. Pass 'snippet,statistics,contentDetails' explicitly.
  • Numbers and durations are strings. viewCount is "1234"; duration is ISO-8601 ("PT4M13S"). Cast numerics and use CarbonInterval::make($duration) or a small PTnHnMnS parser — don't hand-roll regexes.
  • Quota math is your budget. 10,000 units/day by default. A search.list is 100 units — ten searches and you're done. Cache keys should be deterministic (yt:search:{sha1($q.$filters)}) and you should log unit estimates in the job that spends them.
  • The facade is a singleton with one key. If you need multiple keys (per-tenant quotas, prod vs. staging), new \Madcoda\Youtube\Youtube([...]) per instance rather than resolving the facade.
  • Construction fails fast. Building the object with an empty key throws — so don't resolve it during config bootstrap before .env/config cache is available. Run php artisan config:clear after changing YOUTUBE_API_KEY, and never commit the key.
  • Key restrictions. A key restricted to HTTP referrers will 403 from server-side cURL unless you set referer. A key restricted to IPs breaks the moment you deploy to a new host/queue worker without an elastic IP.
  • Search filters aren't all exposed. getChannelByName, searchVideos etc. wrap a subset of parameters. For anything else (regionCode, relevanceLanguage, publishedAfter, channelId filters), use searchAdvanced($params) and read the Google docs — you're passing raw query params.
  • The package is effectively frozen (last meaningful release years ago, no OAuth, no Guzzle, no retries). Pin it in composer.json, keep the adapter layer thin, and accept that the migration to googleapis/google-api-php-client may eventually be a 200-line rewrite of one class, not a project.
  • PHP 8.x smoke test. The code targets PHP ≥ 5.3 and predates typed properties; exercise every method you rely on under your runtime and check for deprecation notices. No built-in retry/backoff exists — add exponential backoff in your wrapper for 5xx and rateLimitExceeded rather than retrying blind.
  • Extension point: override the public $apis array of endpoint URLs to route through an HTTP caching proxy or a test stub — the cheapest way to test without burning quota.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console