<?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>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>
