<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ThumbnailsGrabber</title>
    <description>The latest articles on DEV Community by ThumbnailsGrabber (@cdwm).</description>
    <link>https://dev.to/cdwm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4119886%2F5ff8e6cf-4740-499c-a609-8cb1c90612de.png</url>
      <title>DEV Community: ThumbnailsGrabber</title>
      <link>https://dev.to/cdwm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cdwm"/>
    <language>en</language>
    <item>
      <title>YouTube's WebP thumbnails are half the bytes of the JPEG at 1280x720. At 480x360 they are often bigger. 1,000 videos measured.</title>
      <dc:creator>ThumbnailsGrabber</dc:creator>
      <pubDate>Wed, 23 Sep 2026 20:20:33 +0000</pubDate>
      <link>https://dev.to/cdwm/youtubes-webp-thumbnails-are-half-the-bytes-of-the-jpeg-at-1280x720-at-480x360-they-are-often-6hl</link>
      <guid>https://dev.to/cdwm/youtubes-webp-thumbnails-are-half-the-bytes-of-the-jpeg-at-1280x720-at-480x360-they-are-often-6hl</guid>
      <description>&lt;p&gt;YouTube serves every video thumbnail in two formats. The JPEG lives under &lt;code&gt;https://i.ytimg.com/vi/VIDEO_ID/&lt;/code&gt; and the WebP under &lt;code&gt;https://i.ytimg.com/vi_webp/VIDEO_ID/&lt;/code&gt;, same file names (&lt;code&gt;maxresdefault&lt;/code&gt;, &lt;code&gt;sddefault&lt;/code&gt;, &lt;code&gt;hqdefault&lt;/code&gt;, and so on). The usual advice is "use the WebP, it is smaller." yt-dlp's issue tracker has a long thread arguing the opposite, titled "jpg thumbnails are actually better than webp?", and nobody in it had per-size numbers.&lt;/p&gt;

&lt;p&gt;So I measured it. Short version: WebP is about half the bytes at 1280x720 and 640x480. At 480x360, the size most embeds and players actually use, WebP is the heavier file for 56% of recent uploads and 72% of videos uploaded in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was measured
&lt;/h2&gt;

&lt;p&gt;On 19 September 2026 I took 1,000 videos from the dataset behind an earlier availability study (8,664 videos): 500 from a sample of recent uploads pulled from channel RSS feeds, 500 from videos referenced on Wikidata, which skew older. Every one of the 1,000 has a &lt;code&gt;maxresdefault.jpg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For each video I sent a HEAD request to &lt;code&gt;maxresdefault.webp&lt;/code&gt;, &lt;code&gt;sddefault.webp&lt;/code&gt; and &lt;code&gt;hqdefault.webp&lt;/code&gt; under &lt;code&gt;/vi_webp/&lt;/code&gt; and recorded the status and &lt;code&gt;Content-Length&lt;/code&gt;. The JPEG sizes come from the 7 September measurements of the same videos. 3,000 requests, zero errors. No API key, no page scraping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-size results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;WebP exists&lt;/th&gt;
&lt;th&gt;WebP missing (JPEG exists)&lt;/th&gt;
&lt;th&gt;Median JPEG&lt;/th&gt;
&lt;th&gt;Median WebP&lt;/th&gt;
&lt;th&gt;Median WebP / JPEG&lt;/th&gt;
&lt;th&gt;WebP larger than JPEG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;maxresdefault (1280x720)&lt;/td&gt;
&lt;td&gt;965&lt;/td&gt;
&lt;td&gt;35 (3.5%)&lt;/td&gt;
&lt;td&gt;96.7 KB&lt;/td&gt;
&lt;td&gt;50.8 KB&lt;/td&gt;
&lt;td&gt;0.53&lt;/td&gt;
&lt;td&gt;10 (1.0%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sddefault (640x480)&lt;/td&gt;
&lt;td&gt;970&lt;/td&gt;
&lt;td&gt;30 (3.0%)&lt;/td&gt;
&lt;td&gt;41.3 KB&lt;/td&gt;
&lt;td&gt;23.3 KB&lt;/td&gt;
&lt;td&gt;0.55&lt;/td&gt;
&lt;td&gt;14 (1.4%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hqdefault (480x360)&lt;/td&gt;
&lt;td&gt;970&lt;/td&gt;
&lt;td&gt;30 (3.0%)&lt;/td&gt;
&lt;td&gt;19.0 KB&lt;/td&gt;
&lt;td&gt;15.6 KB&lt;/td&gt;
&lt;td&gt;0.75&lt;/td&gt;
&lt;td&gt;324 (33.4%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Medians are over videos where both files exist. The ratio column is the median of the per-video ratios, not the ratio of the medians.&lt;/p&gt;

&lt;p&gt;At the two larger sizes the saving is consistent. Summed over all 965 maxresdefault pairs, the WebP files total 59.1% of the JPEG bytes, and the 90th percentile drops from 192.8 KB to 132.1 KB. At sddefault the sum is 59.5%.&lt;/p&gt;

&lt;h2&gt;
  
  
  hqdefault splits by upload age
&lt;/h2&gt;

&lt;p&gt;The 33% figure at 480x360 hides two very different populations.&lt;/p&gt;

&lt;p&gt;For the Wikidata-referenced videos (older, on the whole) the median ratio is 0.65 and WebP is larger for 10.4%. For the recent uploads the median ratio is 1.06 and WebP is larger for 56.1%.&lt;/p&gt;

&lt;p&gt;By upload year, the share of recent uploads whose WebP hqdefault is bigger than the JPEG:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Upload year&lt;/th&gt;
&lt;th&gt;WebP larger&lt;/th&gt;
&lt;th&gt;Median ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;td&gt;0.69&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;td&gt;32%&lt;/td&gt;
&lt;td&gt;0.79&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;43%&lt;/td&gt;
&lt;td&gt;0.89&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025&lt;/td&gt;
&lt;td&gt;55%&lt;/td&gt;
&lt;td&gt;1.05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026 (n = 217)&lt;/td&gt;
&lt;td&gt;72%&lt;/td&gt;
&lt;td&gt;1.13&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Vertical Shorts are the extreme case. Their pillarboxed hqdefault WebP is larger than the JPEG 85.3% of the time (n = 116, median ratio 1.17), while their maxresdefault WebP is only 0.40 of the JPEG.&lt;/p&gt;

&lt;p&gt;Summed over every hqdefault pair, WebP still totals 82.7% of the JPEG bytes, because the older videos save a lot and the newer ones lose a little (median JPEG 16.7 KB against WebP 16.5 KB in the recent sample). If your traffic is mostly new videos, that average will not describe what you serve.&lt;/p&gt;

&lt;p&gt;Two things this does not say. Bytes are not quality: the two files are separate encodes of the same frame, YouTube does not publish its encoder settings, and this dataset has no fidelity measure, so a heavier WebP may just be a higher-quality encode. And it does not say why the hqdefault ratio has drifted upward for newer uploads. The data shows the trend, not the cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  A missing WebP is a 404 with an image body
&lt;/h2&gt;

&lt;p&gt;30 of the 1,000 videos had no WebP at any of the three sizes, and 5 more had &lt;code&gt;sddefault.webp&lt;/code&gt; and &lt;code&gt;hqdefault.webp&lt;/code&gt; but no &lt;code&gt;maxresdefault.webp&lt;/code&gt;. Every miss came back as HTTP 404 with a 552-byte &lt;code&gt;image/webp&lt;/code&gt; body. That is the WebP twin of the JPEG behaviour, where a missing rendition is a 404 carrying a 1,097-byte grey 120x90 JPEG.&lt;/p&gt;

&lt;p&gt;Browsers decode that body and fire &lt;code&gt;load&lt;/code&gt;. So an &lt;code&gt;onerror&lt;/code&gt; handler never runs, and the "fallback" you wrote quietly displays a grey rectangle. Recent uploads lacked the WebP maxresdefault less often (12 of 500) than the Wikidata videos (23 of 500).&lt;/p&gt;

&lt;h2&gt;
  
  
  Fallback that actually works
&lt;/h2&gt;

&lt;p&gt;Server side, check the status. Browser side, check the decoded width, because the placeholder is 120 pixels wide and every real rendition is wider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Browser: try WebP first, then JPEG, at one size.&lt;/span&gt;
&lt;span class="c1"&gt;// Resolves to a URL whose decoded width proves it is a real thumbnail.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadReal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;naturalWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onerror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bestThumbnail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;maxresdefault&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://i.ytimg.com/vi_webp/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.webp`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jpg&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://i.ytimg.com/vi/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jpg`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadReal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;webp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;loadReal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jpg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// let the caller step down to sddefault / hqdefault&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Node 18+: HEAD the file, trust the status code, not the body.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HEAD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I would do with this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;At 1280x720 and 640x480, serve WebP with a JPEG fallback. Roughly half the bytes, and the WebP exists for 96.5% of videos that have the JPEG maxresdefault.&lt;/li&gt;
&lt;li&gt;At 480x360, measure before switching. For 2026 uploads the WebP was the larger file 72% of the time. If your pages show new videos, the JPEG hqdefault is the smaller download more often than not.&lt;/li&gt;
&lt;li&gt;Never trust &lt;code&gt;onerror&lt;/code&gt; or the &lt;code&gt;load&lt;/code&gt; event for a missing rendition. Check the status or the width.&lt;/li&gt;
&lt;li&gt;Do not promise 1080p. The realistic ceiling is 1280x720; a small set of legacy videos serves 1920x1080 at the maxres URL, and none of the recent uploads did.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;The sample is restricted to videos that have a JPEG maxresdefault, so it says nothing about WebP for the roughly 12% of videos that lack one. JPEG and WebP were measured twelve days apart; a thumbnail replaced in between would compare two different frames. WebP pixel dimensions were not read, only status and size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data
&lt;/h2&gt;

&lt;p&gt;The full section, with the cohort tables, lives at &lt;a href="https://thumbnailsgrabber.com/youtube-thumbnail-statistics#webp-2026-09" rel="noopener noreferrer"&gt;thumbnailsgrabber.com/youtube-thumbnail-statistics#webp-2026-09&lt;/a&gt;. The per-video rows (video ID, cohort, and per size the WebP status, WebP bytes and JPEG bytes) are in &lt;a href="https://thumbnailsgrabber.com/assets/data/youtube-thumbnail-webp-vs-jpeg-2026.csv" rel="noopener noreferrer"&gt;youtube-thumbnail-webp-vs-jpeg-2026.csv&lt;/a&gt;, CC BY 4.0. The file names and URL patterns for every rendition are in the &lt;a href="https://thumbnailsgrabber.com/youtube-thumbnail-url-guide" rel="noopener noreferrer"&gt;YouTube thumbnail URL guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have a fidelity measure (SSIM or similar) on a matched set of pairs, I would like to see it. The byte question is settled; the quality question is not.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>youtube</category>
      <category>performance</category>
    </item>
    <item>
      <title>maxresdefault.jpg is missing for 1 in 9 YouTube videos, and the 404 still renders. What 8,664 videos showed.</title>
      <dc:creator>ThumbnailsGrabber</dc:creator>
      <pubDate>Thu, 10 Sep 2026 21:25:42 +0000</pubDate>
      <link>https://dev.to/cdwm/maxresdefaultjpg-is-missing-for-1-in-9-youtube-videos-and-the-404-still-renders-what-8664-40p4</link>
      <guid>https://dev.to/cdwm/maxresdefaultjpg-is-missing-for-1-in-9-youtube-videos-and-the-404-still-renders-what-8664-40p4</guid>
      <description>&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://img.youtube.com/vi/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/maxresdefault.jpg`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And at some point a tiny grey box showed up where the poster should be.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I measured
&lt;/h2&gt;

&lt;p&gt;On 7 September 2026 I requested eight thumbnail files for each of 9,055 public YouTube videos straight from &lt;code&gt;i.ytimg.com&lt;/code&gt;, 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).&lt;/p&gt;

&lt;p&gt;Two independent samples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Recent uploads (5,154 videos):&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wikidata-referenced videos (3,510 videos):&lt;/strong&gt; video IDs stored in Wikidata's "YouTube video ID" property. These skew toward notable, often older uploads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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 &lt;code&gt;Content-Range&lt;/code&gt;. 72,440 requests, zero network errors. The per-video CSV is published under CC BY 4.0 at the end.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  How often each size exists
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Pixels&lt;/th&gt;
&lt;th&gt;Exists for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;default.jpg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;120x90&lt;/td&gt;
&lt;td&gt;100.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mqdefault.jpg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;320x180&lt;/td&gt;
&lt;td&gt;100.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hqdefault.jpg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;480x360&lt;/td&gt;
&lt;td&gt;100.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sddefault.jpg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;640x480&lt;/td&gt;
&lt;td&gt;95.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hq720.jpg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1280x720&lt;/td&gt;
&lt;td&gt;88.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;maxresdefault.jpg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1280x720&lt;/td&gt;
&lt;td&gt;88.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vi_webp/.../maxresdefault.webp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1280x720&lt;/td&gt;
&lt;td&gt;85.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;oar2.jpg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;original aspect ratio&lt;/td&gt;
&lt;td&gt;29.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;Age is the main driver. Among recent uploads, the share with a &lt;code&gt;maxresdefault&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Upload year&lt;/th&gt;
&lt;th&gt;Videos&lt;/th&gt;
&lt;th&gt;Have maxresdefault&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;33&lt;/td&gt;
&lt;td&gt;48.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;td&gt;65.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;67&lt;/td&gt;
&lt;td&gt;59.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;281&lt;/td&gt;
&lt;td&gt;81.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022&lt;/td&gt;
&lt;td&gt;303&lt;/td&gt;
&lt;td&gt;80.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;319&lt;/td&gt;
&lt;td&gt;91.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;520&lt;/td&gt;
&lt;td&gt;92.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025&lt;/td&gt;
&lt;td&gt;803&lt;/td&gt;
&lt;td&gt;92.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026&lt;/td&gt;
&lt;td&gt;2,205&lt;/td&gt;
&lt;td&gt;94.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a missing maxresdefault really returns
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

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

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

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

&lt;p&gt;So "you can't detect it" is wrong. You can't detect it &lt;strong&gt;from the image load event&lt;/strong&gt;. The status code and the decoded width both tell the truth.&lt;/p&gt;

&lt;p&gt;Try it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code} %{size_download}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://i.ytimg.com/vi/tPEE9ZwTmy0/maxresdefault.jpg
&lt;span class="c"&gt;# 404 1097&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code} %{size_download}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://i.ytimg.com/vi/tPEE9ZwTmy0/sddefault.jpg
&lt;span class="c"&gt;# 200 55898&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The fallback chain that covers 100% of videos
&lt;/h2&gt;

&lt;p&gt;The largest file that actually existed per video was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;maxresdefault&lt;/code&gt; (1280x720) for 88.3%&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sddefault&lt;/code&gt; (640x480) for 7.5%&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hqdefault&lt;/code&gt; (480x360) for the remaining 4.2%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So &lt;code&gt;maxresdefault -&amp;gt; sddefault -&amp;gt; hqdefault&lt;/code&gt; resolved to a real image for every single one of the 8,664 videos, with at most two fallbacks.&lt;/p&gt;

&lt;p&gt;Two things to skip:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;hq720.jpg&lt;/code&gt;&lt;/strong&gt; adds nothing. It never existed for a video that lacked &lt;code&gt;maxresdefault&lt;/code&gt; (0 of 8,664), and in 93.6% of videos it was byte-for-byte the same size as &lt;code&gt;maxresdefault.jpg&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trying for 1080p.&lt;/strong&gt; Of the 7,650 videos with a &lt;code&gt;maxresdefault&lt;/code&gt;, 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.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Browser (check the width)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadThumb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;maxresdefault&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sddefault&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hqdefault&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tryNext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no thumbnail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;naturalWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;tryNext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onerror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;tryNext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://i.ytimg.com/vi/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;.jpg`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nf"&gt;tryNext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;naturalWidth &amp;gt; 120&lt;/code&gt; is the whole trick. The placeholder is exactly 120 wide; every real file is wider.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server (check the status)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bestThumbnail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;maxresdefault&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sddefault&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hqdefault&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://i.ytimg.com/vi/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jpg`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HEAD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no thumbnail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Or use the package
&lt;/h3&gt;

&lt;p&gt;I put the parser, the URL builder and the fallback into a zero-dependency package so I stop copy-pasting it: &lt;a href="https://www.npmjs.com/package/youtube-thumbnail-url" rel="noopener noreferrer"&gt;&lt;code&gt;youtube-thumbnail-url&lt;/code&gt;&lt;/a&gt; (MIT, ~6 KB, Node 18+ and browsers, ESM + CJS + types, &lt;a href="https://github.com/cdwm/youtube-thumbnail-url" rel="noopener noreferrer"&gt;source on GitHub&lt;/a&gt;). It checks the HTTP status &lt;strong&gt;and&lt;/strong&gt; reads the real pixel width from the first 4 KB of the file, so a 404 with a picture inside cannot fool it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolveThumbnail&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;youtube-thumbnail-url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;resolveThumbnail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.youtube.com/watch?v=tPEE9ZwTmy0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// { size: 'sddefault', url: 'https://i.ytimg.com/vi/tPEE9ZwTmy0/sddefault.jpg',&lt;/span&gt;
&lt;span class="c1"&gt;//   width: 640, height: 480, status: 206,&lt;/span&gt;
&lt;span class="c1"&gt;//   checked: [ { size: 'maxresdefault', ok: false, status: 404 }, { size: 'sddefault', ok: true } ] }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Three more things the data showed
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;WebP is half the bytes.&lt;/strong&gt; &lt;code&gt;https://i.ytimg.com/vi_webp/ID/maxresdefault.webp&lt;/code&gt; 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 &lt;code&gt;maxresdefault.jpg&lt;/code&gt; is 95.3 KB (90th percentile 185.4 KB, largest seen 490.3 KB), so a &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; with a WebP source saves roughly 47 KB per poster at the median.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shorts have a separate vertical file.&lt;/strong&gt; &lt;code&gt;oar2.jpg&lt;/code&gt; ("original aspect ratio") is where the 9:16 image lives: 1,031 vertical videos served 1080x1920 and 123 served 720x1280. Their &lt;code&gt;maxresdefault.jpg&lt;/code&gt; was still a 1280x720 landscape frame in 98.1% of cases, with the vertical picture pillarboxed into it. Do not assume &lt;code&gt;oar2&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vertical is climbing fast.&lt;/strong&gt; Among recent uploads, the share with a vertical &lt;code&gt;oar2&lt;/code&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  Data
&lt;/h2&gt;

&lt;p&gt;Full write-up with charts, methodology and limitations: &lt;a href="https://thumbnailsgrabber.com/youtube-thumbnail-statistics" rel="noopener noreferrer"&gt;YouTube Thumbnail Statistics 2026&lt;/a&gt;. 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 &lt;a href="https://thumbnailsgrabber.com/youtube-thumbnail-url-guide" rel="noopener noreferrer"&gt;YouTube thumbnail URL guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you find a video where the fallback chain fails, open an issue on the &lt;a href="https://github.com/cdwm/youtube-thumbnail-url/issues" rel="noopener noreferrer"&gt;package repo&lt;/a&gt; with the ID. I would like to see it.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>youtube</category>
      <category>node</category>
    </item>
  </channel>
</rss>
