ScreenshotNeo

BlogHow-to

How to Make a Thumbnail from a YouTube Link

Get a YouTube thumbnail from any video link using a direct URL, fallback sizes, or the official Data API—with runnable code.

By the ScreenshotNeo team1 October 20266 min read

For a standard YouTube link, copy the video ID after v= and place it in this URL:

https://i.ytimg.com/vi/VIDEO_ID/maxresdefault.jpg

Example:

https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg
  1. Copy the YouTube URL.
  2. Extract its video ID.
  3. Replace VIDEO_ID in the thumbnail pattern.
  4. Open the completed URL and save the image.

This retrieves YouTube’s existing associated thumbnail. It does not create a new design or extract an arbitrary frame from the video.

Link format Example Video ID
Watch page https://www.youtube.com/watch?v=dQw4w9WgXcQ Value of v
Short link https://youtu.be/dQw4w9WgXcQ First path segment
Shorts https://www.youtube.com/shorts/dQw4w9WgXcQ Segment after /shorts/
Embed https://www.youtube.com/embed/dQw4w9WgXcQ Segment after /embed/

Remove surrounding whitespace and ignore extra query parameters such as &list=.... A YouTube video ID is normally an 11-character value, but your parser should use the URL structure rather than assume a fixed length.

Thumbnail URL sizes and fallbacks

maxresdefault.jpg is the largest commonly requested direct variant, but it is not guaranteed to exist. Try these names when it returns an error or an unusable image:

Variant Documented dimensions or typical use
maxresdefault.jpg Best case for the direct URL method
hqdefault.jpg High-quality fallback
mqdefault.jpg Medium-quality fallback
default.jpg Smallest fallback

The official API exposes more precise availability information. Google documents these thumbnail objects and their usual dimensions:

API object Dimensions
default 120×90
medium 320×180
high 480×360
standard 640×480, available for some videos
maxres 1280×720, available for some videos

Google notes that dimensions can vary and width or height may be omitted. Treat maxres as optional and fall back to the largest object returned.

Download a thumbnail with cURL

curl -L "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" -o thumbnail.jpg

If that file is unavailable, repeat with hqdefault.jpg, mqdefault.jpg, or default.jpg.

Download a thumbnail with Python

from pathlib import Path
from urllib.parse import urlparse, parse_qs
import requests

VIDEO_URL = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"


def video_id(url: str) -> str:
    parsed = urlparse(url)
    host = parsed.netloc.lower().split(":")[0]
    parts = [part for part in parsed.path.split("/") if part]

    if host in {"youtu.be", "www.youtu.be"} and parts:
        return parts[0]
    if parsed.path == "/watch":
        value = parse_qs(parsed.query).get("v", [""])[0]
        if value:
            return value
    for marker in ("shorts", "embed", "live"):
        if marker in parts:
            index = parts.index(marker)
            if index + 1 < len(parts):
                return parts[index + 1]
    raise ValueError("Could not find a YouTube video ID")

vid = video_id(VIDEO_URL)
output = Path("thumbnail.jpg")
for variant in ("maxresdefault", "hqdefault", "mqdefault", "default"):
    response = requests.get(
        f"https://i.ytimg.com/vi/{vid}/{variant}.jpg",
        timeout=30,
    )
    content_type = response.headers.get("content-type", "")
    if response.ok and content_type.startswith("image/"):
        output.write_bytes(response.content)
        print(f"Saved {variant} to {output}")
        break
else:
    raise RuntimeError("No thumbnail variant was available")

Download a thumbnail with Node.js

import { writeFile } from "node:fs/promises";

const input = "https://youtu.be/dQw4w9WgXcQ";
const url = new URL(input);
let id;

if (url.hostname === "youtu.be") {
  id = url.pathname.split("/").filter(Boolean)[0];
} else if (url.pathname === "/watch") {
  id = url.searchParams.get("v");
} else {
  const parts = url.pathname.split("/").filter(Boolean);
  for (const marker of ["shorts", "embed", "live"]) {
    const index = parts.indexOf(marker);
    if (index !== -1) id = parts[index + 1];
    if (id) break;
  }
}

if (!id) throw new Error("Could not find a YouTube video ID");

for (const variant of ["maxresdefault", "hqdefault", "mqdefault", "default"]) {
  const response = await fetch(`https://i.ytimg.com/vi/${id}/${variant}.jpg`);
  const type = response.headers.get("content-type") || "";
  if (response.ok && type.startsWith("image/")) {
    await writeFile("thumbnail.jpg", Buffer.from(await response.arrayBuffer()));
    console.log(`Saved ${variant} thumbnail`);
    break;
  }
}

Use the official YouTube Data API

The direct URL is convenient for one-off downloads. For an application or bulk retrieval, request the video’s snippet resource and read snippet.thumbnails. The response contains structured URLs and, when available, dimensions for default, medium, high, standard, and maxres.

You need an API key and must account for YouTube Data API quota. Replace YOUR_API_KEY and the sample ID:

curl "https://www.googleapis.com/youtube/v3/videos?part=snippet&id=dQw4w9WgXcQ&key=YOUR_API_KEY"
import requests

response = requests.get(
    "https://www.googleapis.com/youtube/v3/videos",
    params={"part": "snippet", "id": "dQw4w9WgXcQ", "key": "YOUR_API_KEY"},
    timeout=30,
)
response.raise_for_status()
data = response.json()
video = data.get("items", [])[0]
thumbs = video["snippet"]["thumbnails"]
chosen = thumbs.get("maxres") or thumbs.get("standard") or thumbs.get("high") or thumbs["default"]
print(chosen["url"], chosen.get("width"), chosen.get("height"))
const params = new URLSearchParams({
  part: "snippet",
  id: "dQw4w9WgXcQ",
  key: "YOUR_API_KEY"
});
const response = await fetch(`https://www.googleapis.com/youtube/v3/videos?${params}`);
if (!response.ok) throw new Error(`YouTube API error: ${response.status}`);
const data = await response.json();
const thumbs = data.items?.[0]?.snippet?.thumbnails;
if (!thumbs) throw new Error("Video not found or has no thumbnail metadata");
const chosen = thumbs.maxres || thumbs.standard || thumbs.high || thumbs.default;
console.log(chosen.url, chosen.width, chosen.height);

Or skip the browser setup

ScreenshotNeo can capture the completed thumbnail URL with one GET request. See the API documentation for all options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg' }); const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie banners, popups and chat widgets are removed before the shot.
  • Bot checks, blank pages and failed loads are never billed.
  • An MCP server lets AI agents take screenshots.
  • 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000.

Create a free ScreenshotNeo account.

Troubleshooting

maxresdefault.jpg returns an error or placeholder

That variant is optional. Try hqdefault.jpg, then mqdefault.jpg or default.jpg. The Data API is the reliable way to discover which returned variant exists.

The URL produces the wrong image

Check that you extracted the video ID, not the playlist ID, channel ID or an entire query string. For watch links, use only the value of v.

Handle youtu.be/ID as the first path segment and /shorts/ID as the segment after shorts. Also support /embed/ID when users paste embed URLs.

The API returns no video

The ID may be misspelled, or the video may be unavailable to the API. Check the response’s items array before reading snippet.

The image looks stretched

Read the API’s width and height fields when available, preserve the returned aspect ratio, and avoid forcing every thumbnail into a fixed box without cropping rules.

Downloads are slow or unreliable

Set a request timeout, check the HTTP status and content type, and retry only transient failures. Cache a downloaded thumbnail when your application requests the same video repeatedly.

Performance, reliability and cost notes

  • The direct URL method has no API-key setup and is usually the shortest path for a single thumbnail.
  • The Data API adds quota management but returns structured availability and dimensions, which is useful for bulk jobs.
  • Use a fallback sequence so missing high-resolution variants do not fail the whole task.
  • Validate that the response is an image before saving it; an error page should not be written as .jpg.
  • Downloading a thumbnail does not by itself establish permission to republish it. Check rights for your intended use.

FAQ

What do I replace VIDEO_ID with?

Use the value after v= in a watch URL, or the relevant path segment in a youtu.be, Shorts or embed URL.

Can I get a frame from the middle of the video this way?

No. These URLs and the API return YouTube’s associated thumbnail. Extracting an arbitrary frame requires a separate video-processing workflow.

Do I need an API key?

No for the direct image URL. Yes for the YouTube Data API.

Which size should I choose?

Use the largest available variant that fits your output. Prefer API metadata when you need to know the exact returned dimensions.