DEV Community

ThumbnailsGrabber
ThumbnailsGrabber

Posted on Originally published at thumbnailsgrabber.com

maxresdefault.jpg is missing for 1 in 9 YouTube videos, and the 404 still renders. What 8,664 videos showed.

If you have ever built a YouTube embed, a lazy-load facade, a link preview or a CMS "featured image" feature, you have written this line:

const src = `https://img.youtube.com/vi/${id}/maxresdefault.jpg`;
Enter fullscreen mode Exit fullscreen mode

And at some point a tiny grey box showed up where the poster should be.

Most write-ups explain this with folklore: "maxresdefault only exists for HD uploads", "YouTube returns a placeholder at 200 so you can't detect it", "check for a 120px image". Some of that is right, some of it is wrong, and none of it comes with a number attached. So I measured it.

What I measured

On 7 September 2026 I requested eight thumbnail files for each of 9,055 public YouTube videos straight from i.ytimg.com, no Data API, no page scraping. 8,664 videos were still live (the rest had been deleted or made private and are excluded from every percentage).

Two independent samples:

  • Recent uploads (5,154 videos): the latest videos from the public RSS feeds of 394 channels that have a Wikidata entry, up to 15 per channel. Upload dates run from 2008 to the day of measurement; 2,205 were uploaded in 2026. This sample is Shorts-heavy.
  • Wikidata-referenced videos (3,510 videos): video IDs stored in Wikidata's "YouTube video ID" property. These skew toward notable, often older uploads.

For each video and each file I recorded whether it exists, the HTTP status, the pixel dimensions from the JPEG header, and the byte size from Content-Range. 72,440 requests, zero network errors. The per-video CSV is published under CC BY 4.0 at the end.

Caveat up front: neither sample is a uniform random draw of all of YouTube, both over-represent established channels, so the maxresdefault rate for the long tail of casual uploads is probably lower than what follows.

How often each size exists

File Pixels Exists for
default.jpg 120x90 100.0%
mqdefault.jpg 320x180 100.0%
hqdefault.jpg 480x360 100.0%
sddefault.jpg 640x480 95.8%
hq720.jpg 1280x720 88.2%
maxresdefault.jpg 1280x720 88.3%
vi_webp/.../maxresdefault.webp 1280x720 85.7%
oar2.jpg original aspect ratio 29.9%

So the three small sizes are universal, sddefault nearly so, and the two 1280x720 files are where it drops: 11.7% of videos have no maxresdefault.jpg. That is 1 in 9 in a sample that favours established channels.

Age is the main driver. Among recent uploads, the share with a maxresdefault:

Upload year Videos Have maxresdefault
2012 33 48.5%
2016 35 65.7%
2018 67 59.7%
2020 281 81.9%
2022 303 80.9%
2023 319 91.5%
2024 520 92.5%
2025 803 92.8%
2026 2,205 94.6%

Uploads from 2008 and 2009 in the sample had one 0% of the time. Anything from 2023 on has one better than nine times out of ten. YouTube does not back-fill old videos.

What a missing maxresdefault really returns

This is the part the folklore gets wrong. The claim you will find repeated in blog posts and even in some package READMEs is that YouTube serves the grey placeholder "at HTTP 200", which is why you supposedly cannot detect it.

Every one of the 1,014 missing maxresdefault.jpg requests in this study came back as HTTP 404. All 1,014. I re-checked a sample against img.youtube.com and i3.ytimg.com with a desktop browser user agent: identical 404s.

What is true is that the 404 body is a valid image: the same 1,097-byte, 120x90 grey JPEG every time. Browsers happily decode it. So:

  • <img src=".../maxresdefault.jpg"> renders a tiny grey box and fires load, not error.
  • new Image() fires onload with naturalWidth === 120.
  • fetch() gives you response.ok === false and status === 404. The status is there if you look at it.
  • curl -O and wget save the placeholder to disk unless you pass --fail (curl) or check the exit code.
  • requests.get(url).content in Python saves the placeholder unless you check status_code.

So "you can't detect it" is wrong. You can't detect it from the image load event. The status code and the decoded width both tell the truth.

Try it yourself:

curl -s -o /dev/null -w "%{http_code} %{size_download}\n" https://i.ytimg.com/vi/tPEE9ZwTmy0/maxresdefault.jpg
# 404 1097
curl -s -o /dev/null -w "%{http_code} %{size_download}\n" https://i.ytimg.com/vi/tPEE9ZwTmy0/sddefault.jpg
# 200 55898
Enter fullscreen mode Exit fullscreen mode

The fallback chain that covers 100% of videos

The largest file that actually existed per video was:

  • maxresdefault (1280x720) for 88.3%
  • sddefault (640x480) for 7.5%
  • hqdefault (480x360) for the remaining 4.2%

So maxresdefault -> sddefault -> hqdefault resolved to a real image for every single one of the 8,664 videos, with at most two fallbacks.

Two things to skip:

  • hq720.jpg adds nothing. It never existed for a video that lacked maxresdefault (0 of 8,664), and in 93.6% of videos it was byte-for-byte the same size as maxresdefault.jpg.
  • Trying for 1080p. Of the 7,650 videos with a maxresdefault, 7,571 (99.0%) served exactly 1280x720. 69 legacy videos, all in the older Wikidata cohort, served a genuine 1920x1080 at that URL; none of the recent uploads did. 1280x720 is the ceiling for practically every video and there is no 4K thumbnail file.

Browser (check the width)

function loadThumb(id, sizes = ['maxresdefault', 'sddefault', 'hqdefault']) {
  return new Promise((resolve, reject) => {
    const tryNext = (i) => {
      if (i >= sizes.length) return reject(new Error('no thumbnail'));
      const img = new Image();
      img.onload = () => (img.naturalWidth > 120 ? resolve(img) : tryNext(i + 1));
      img.onerror = () => tryNext(i + 1);
      img.src = `https://i.ytimg.com/vi/${id}/${sizes[i]}.jpg`;
    };
    tryNext(0);
  });
}
Enter fullscreen mode Exit fullscreen mode

naturalWidth > 120 is the whole trick. The placeholder is exactly 120 wide; every real file is wider.

Server (check the status)

async function bestThumbnail(id, sizes = ['maxresdefault', 'sddefault', 'hqdefault']) {
  for (const size of sizes) {
    const url = `https://i.ytimg.com/vi/${id}/${size}.jpg`;
    const res = await fetch(url, { method: 'HEAD' });
    if (res.ok) return url;
  }
  throw new Error('no thumbnail');
}
Enter fullscreen mode Exit fullscreen mode

Or use the package

I put the parser, the URL builder and the fallback into a zero-dependency package so I stop copy-pasting it: youtube-thumbnail-url (MIT, ~6 KB, Node 18+ and browsers, ESM + CJS + types, source on GitHub). It checks the HTTP status and reads the real pixel width from the first 4 KB of the file, so a 404 with a picture inside cannot fool it.

import { resolveThumbnail } from 'youtube-thumbnail-url';

const best = await resolveThumbnail('https://www.youtube.com/watch?v=tPEE9ZwTmy0');
// { size: 'sddefault', url: 'https://i.ytimg.com/vi/tPEE9ZwTmy0/sddefault.jpg',
//   width: 640, height: 480, status: 206,
//   checked: [ { size: 'maxresdefault', ok: false, status: 404 }, { size: 'sddefault', ok: true } ] }
Enter fullscreen mode Exit fullscreen mode

Three more things the data showed

WebP is half the bytes. https://i.ytimg.com/vi_webp/ID/maxresdefault.webp existed for 85.7% of videos, and across 7,424 JPEG/WebP pairs its median size was 50.9% of the JPEG. It was smaller in 99.4% of pairs. Coverage is the catch: 226 videos had the JPEG but no WebP, only 4 had WebP without the JPEG. So request WebP first and fall back to JPEG, not the other way round. The median maxresdefault.jpg is 95.3 KB (90th percentile 185.4 KB, largest seen 490.3 KB), so a <picture> with a WebP source saves roughly 47 KB per poster at the median.

Shorts have a separate vertical file. oar2.jpg ("original aspect ratio") is where the 9:16 image lives: 1,031 vertical videos served 1080x1920 and 123 served 720x1280. Their maxresdefault.jpg was still a 1280x720 landscape frame in 98.1% of cases, with the vertical picture pillarboxed into it. Do not assume oar2 is always vertical, though. For landscape videos it is usually a duplicate 1280x720, sometimes 1920x1080 or a 1280x544 cinema crop. Check the height against the width.

Vertical is climbing fast. Among recent uploads, the share with a vertical oar2 went from 2.6% of 2022 uploads to 21.3% (2023), 26.0% (2024), 28.3% (2025) and 36.2% of 2026 uploads. Inside 2026 the monthly figure reached 41.7% in August and 44.4% in the first week of September. On these channels roughly two in five new uploads are now vertical, so if your embed code only knows landscape URLs it is already wrong for a big slice of new content.

Summary

  1. 88.3% of videos have maxresdefault.jpg; 1 in 9 does not, and old videos are far more likely to lack it.
  2. A missing file is a 404 whose body is a 120x90 grey JPEG. Check status or naturalWidth > 120, never the load event alone.
  3. maxresdefault -> sddefault -> hqdefault covers 100% of videos. Skip hq720.
  4. 1280x720 is the practical maximum. No 4K, and 1080p only on a legacy sliver.
  5. Prefer WebP with a JPEG fallback: about half the bytes, 85.7% coverage.
  6. For Shorts, fetch oar2.jpg and check that height > width.

Data

Full write-up with charts, methodology and limitations: YouTube Thumbnail Statistics 2026. The per-video CSV (8,664 rows: video ID, cohort, publish date, presence of each of the eight files, maxresdefault status/width/height/bytes, oar2 width/height) is linked from that page under CC BY 4.0. Every URL pattern YouTube's image servers accept is documented in the YouTube thumbnail URL guide.

If you find a video where the fallback chain fails, open an issue on the package repo with the ID. I would like to see it.

Top comments (0)