---
title: "Get Youtube video thumbnail image"
author: "Yaroslav Shmarov"
date: "2025-01-24"
canonical_url: "https://blog.superails.com/oembed"
markdown_url: "https://blog.superails.com/oembed.md"
description: "Get video data Video thumbnail: Makes request to: Result: I often google \"get youtube video thumbnail\"."
tags: ["oembed"]
license: "GNU AGPL v3. Credit Yaroslav Shmarov and link the canonical URL."
---

# Get Youtube video thumbnail image

#### Paste Youtube, Vimeo or Wistia video url to get the thumbnail image and oembed data

<div id="video-preview-container">
  <div>
    <input 
      type="text" 
      style="width: 100%;"
      value="https://www.youtube.com/watch?v=vS-F1PlLyTk"
      class="video-input" 
      placeholder="Enter video URL (YouTube, Vimeo, or Wistia)"
    >
    <button onclick="videoPreview.fetchThumbnail()">
      Get video data
    </button>
  </div>
  <br>
  Video thumbnail:
  <div class="video-thumbnail-output"></div>
  <br>
  Makes request to:
  <div class="video-url-output"></div>
  <br>
  Result:
  <pre class="video-data-output"></pre>
</div>

#### How it works

I often google "get youtube video thumbnail".

[Oembed](https://oembed.com/) let's you get metadata and embed code for a media asset.

If a web page has `<link rel="alternate" type="application/json+oembed" href="...">`, it means that the page supports oembed. The href is the oembed url. The first part of the url is the oembed API endpoint.

For example

```html
<link rel="alternate" type="application/json+oembed" href="https://www.youtube.com/oembed?format=json&url=https://www.youtube.com/watch?v=vS-F1PlLyTk" title="Typesense search with Ruby on Rails #225">
```

means that the **Youtube** API endpoint is

```
https://www.youtube.com/oembed?format=json&url=
```

**Vimeo** (example url: `https://vimeo.com/158115405`)

```
https://vimeo.com/api/oembed.json?url=
```

**Wistia** (example url: `https://clickfunnels-28.wistia.com/medias/661x8p4j6u`)

```
https://fast.wistia.com/oembed?url=
```

At the end of the API endpoint append the url to the video to get the oembed data.

<script>
class VideoThumbnailPreview {
  constructor(container) {
    this.container = container;
    this.input = container.querySelector('.video-input');
    this.thumbnailOutput = container.querySelector('.video-thumbnail-output');
    this.dataOutput = container.querySelector('.video-data-output');
    this.urlOutput = container.querySelector('.video-url-output');
    
    // Bind event listeners
    this.input.addEventListener('input', () => this.fetchThumbnail());
    
    // Initial fetch if there's a value
    this.fetchThumbnail();
  }

  async fetchThumbnail() {
    const url = this.input.value;
    if (!url) {
      this.clearOutput();
      return;
    }

    const videoProvider = this.detectVideoProvider(url);
    if (!videoProvider) {
      this.clearOutput();
      return;
    }

    try {
      const data = await this.fetchOembedData(url, videoProvider);
      if (data) {
        this.thumbnailOutput.innerHTML = `<img src="${data.thumbnail_url}" alt="Video thumbnail">`;
        this.dataOutput.textContent = JSON.stringify(data, null, 2);
        this.urlOutput.textContent = url;
      } else {
        this.clearOutput();
      }
    } catch (error) {
      this.clearOutput();
    }
  }

  clearOutput() {
    this.thumbnailOutput.innerHTML = '';
    this.dataOutput.innerHTML = '';
  }

  detectVideoProvider(url) {
    if (url.match(/youtu/)) return 'youtube';
    if (url.match(/vimeo/)) return 'vimeo';
    if (url.match(/wistia/)) return 'wistia';
    return null;
  }

  async fetchOembedData(url, provider) {
    const endpoints = {
      youtube: `https://www.youtube.com/oembed?url=${encodeURIComponent(url)}&format=json`,
      vimeo: `https://vimeo.com/api/oembed.json?url=${encodeURIComponent(url)}`,
      wistia: `https://fast.wistia.com/oembed?url=${encodeURIComponent(url)}&format=json`
    };

    const endpoint = endpoints[provider];
    if (!endpoint) return null;

    const response = await fetch(endpoint);
    if (!response.ok) return null;

    const data = await response.json();
    return data;
  }
}

document.addEventListener('DOMContentLoaded', () => {
  const container = document.querySelector('#video-preview-container');
  window.videoPreview = new VideoThumbnailPreview(container);
});
</script>

#### StimulusJS implementation

```js
// video_thumbnail_preview_controller.js
import { Controller } from '@hotwired/stimulus'

// <div data-controller="video-thumbnail-preview">
//   <input
//     data-video-thumbnail-preview-target="input"
//     type="text"
//     data-action="input->video-thumbnail-preview#fetchThumbnail"
//     value="https://www.youtube.com/watch?v=RNaaODDtTLw"
//   >
//   <div data-video-thumbnail-preview-target="output"></div>
// </div>

// public vimeo video:
// https://vimeo.com/158115405
// https://vimeo.com/api/oembed.json?url=https://vimeo.com/158115405

// public youtube video:
// www.youtube.com/watch?v=944lk4JAdyg
// https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=944lk4JAdyg

// https://docs.wistia.com/docs/wistia-and-oembed
// public wistia video:
// https://clickfunnels-28.wistia.com/medias/661x8p4j6u
// https://fast.wistia.com/oembed?url=https://clickfunnels-28.wistia.com/medias/661x8p4j6u

export default class extends Controller {
  static targets = ['input', 'output']

  connect() {
    this.fetchThumbnail()
  }

  outputTargetConnected() {
    this.fetchThumbnail()
  }

  inputTargetConnected() {
    this.fetchThumbnail()
  }

  inputTargetChanged() {
    this.fetchThumbnail()
  }

  async fetchThumbnail() {
    const url = this.inputTarget.value
    if (!url) {
      this.clearOutputs()
      return
    }

    const videoProvider = this.detectVideoProvider(url)
    if (!videoProvider) {
      this.clearOutputs()
      return
    }

    try {
      const thumbnailUrl = await this.fetchOembedThumbnail(url, videoProvider)
      if (thumbnailUrl) {
        this.outputTargets.forEach((target) => {
          target.innerHTML = `<img src="${thumbnailUrl}" alt="Video thumbnail">`
        })
      } else {
        this.clearOutputs()
      }
    } catch (error) {
      this.clearOutputs()
    }
  }

  clearOutputs() {
    this.outputTargets.forEach((target) => {
      target.innerHTML = ''
    })
  }

  detectVideoProvider(url) {
    if (url.match(/youtu/)) return 'youtube'
    if (url.match(/vimeo/)) return 'vimeo'
    if (url.match(/wistia/)) return 'wistia'
    return null
  }

  async fetchOembedThumbnail(url, provider) {
    const endpoints = {
      youtube: `https://www.youtube.com/oembed?url=${encodeURIComponent(
        url
      )}&format=json`,
      vimeo: `https://vimeo.com/api/oembed.json?url=${encodeURIComponent(url)}`,
      wistia: `https://fast.wistia.com/oembed?url=${encodeURIComponent(
        url
      )}&format=json`
    }

    const endpoint = endpoints[provider]
    if (!endpoint) return null

    const response = await fetch(endpoint)
    if (!response.ok) return null

    const data = await response.json()
    return data.thumbnail_url
  }
}
```

That's it!
