DEV Community

Truman
Truman

Posted on

YouTube thumbnails are just predictable URLs: how maxresdefault really works

YouTube thumbnails are just predictable URLs: how maxresdefault really works

Every YouTube video has a set of thumbnail images sitting on a public CDN, no API key required. If you've ever built a tool that shows link previews, embeds videos, or lets users download covers, you've probably used them. This post explains the URL scheme, the size ladder, and the one gotcha that breaks naive implementations: maxresdefault doesn't always exist.

The URL scheme

All thumbnails live under:

https://i.ytimg.com/vi/{VIDEO_ID}/{SIZE}.jpg
Enter fullscreen mode Exit fullscreen mode

{VIDEO_ID} is the 11-character ID from the watch URL. {SIZE} is one of:

Name Resolution Notes
default 120×90 Always exists, 4:3
mqdefault 320×180 Always exists, 16:9
hqdefault 480×360 Always exists, 4:3
sddefault 640×480 Usually exists, 4:3
maxresdefault 1280×720 Only if the video has an HD source

The pattern is simple enough to construct by hand. The catch is the last row: YouTube only generates maxresdefault when the uploaded video was HD. For older or low-resolution uploads, requesting it returns a 404 — or worse, a placeholder image with an HTTP 200, depending on the era of the upload. A robust fetcher must probe from largest to smallest and take the first that actually resolves.

Extracting the video ID

Users paste all kinds of URLs. Before you can build the thumbnail URL, you need the ID out of:

  • https://www.youtube.com/watch?v=dQw4w9WgXcQ
  • https://youtu.be/dQw4w9WgXcQ
  • https://www.youtube.com/shorts/dQw4w9WgXcQ
  • https://www.youtube.com/embed/dQw4w9WgXcQ
  • https://www.youtube.com/live/dQw4w9WgXcQ
  • https://m.youtube.com/watch?v=dQw4w9WgXcQ&feature=shared
  • just dQw4w9WgXcQ (11 chars)

A single regex handles most of these:

function extractVideoId(input) {
  const s = input.trim();
  if (/^[a-zA-Z0-9_-]{11}$/.test(s)) return s; // bare ID
  const m = s.match(
    /(?:youtube\.com\/(?:watch\?[^#]*v=|shorts\/|embed\/|live\/|v\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/
  );
  return m ? m[1] : null;
}
Enter fullscreen mode Exit fullscreen mode

Note the character class: YouTube IDs are base64url, so - and _ are valid. A regex limited to [a-zA-Z0-9] will silently fail on roughly a quarter of videos.

Picking the best available size

The reliable approach is a fallback chain. In the browser, the cheapest probe is an Image load with onerror:

const SIZES = ['maxresdefault', 'sddefault', 'hqdefault', 'mqdefault', 'default'];

function bestThumbnail(videoId) {
  return new Promise((resolve) => {
    let i = 0;
    const tryNext = () => {
      if (i >= SIZES.length) return resolve(null);
      const name = SIZES[i++];
      const url = `https://i.ytimg.com/vi/${videoId}/${name}.jpg`;
      const img = new Image();
      img.onload = () => {
        // Guard against the legacy 120x90 placeholder served with 200
        if (name === 'maxresdefault' && img.naturalWidth < 200) return tryNext();
        resolve({ name, url, width: img.naturalWidth, height: img.naturalHeight });
      };
      img.onerror = tryNext;
      img.src = url;
    };
    tryNext();
  });
}
Enter fullscreen mode Exit fullscreen mode

Two details worth noting. First, sddefault (640×480) is 4:3 while the rest of the HD ladder is 16:9 — if your UI expects 16:9, you may want to skip it or crop. Second, the placeholder guard: very old videos serve a tiny 120×90 "no thumbnail" image at the maxresdefault path with HTTP 200, so checking naturalWidth is more reliable than status codes alone.

Server-side, the same chain works with HEAD requests, but be polite: these are cheap CDN hits, not API calls, and there's no quota — but hammering them in a tight loop from one IP is still a good way to get throttled.

Why no API key is needed

This surprises people, but the thumbnail CDN is intentionally public. The images are referenced by every embed player on the web; YouTube can't put them behind auth without breaking embeds. The official Data API (thumbnails resource) returns the same URLs — you're just skipping the quota-limited middleman. What the API gives you that raw URLs don't is metadata (which sizes exist), but as shown above, probing is trivial.

WebP variants

Append .webp instead of .jpg and you get WebP versions of the same images:

https://i.ytimg.com/vi/{VIDEO_ID}/maxresdefault.webp
Enter fullscreen mode Exit fullscreen mode

Useful if you care about bytes on the wire. Availability mirrors the JPG ladder.

Wrapping up

The whole system is: extract 11-char ID → walk the size ladder → take the first real hit. No SDK, no key, no proxy server needed — the browser can do it all directly against i.ytimg.com.

If you'd rather not write the fallback chain yourself, 17NAS YouTube Thumbnail Downloader implements exactly this: paste any YouTube link (watch, youtu.be, Shorts, live, embed, mobile) or bare ID, it probes all sizes, auto-picks the best available, and gives you one-click JPG download or WebP in a new tab. Free, no sign-up, no watermark — everything is fetched by your browser straight from YouTube's image servers.

Top comments (0)