The problem oEmbed solves
Imagine you're building a blog, CMS, or chat app. A user pastes a YouTube link. You want to show the embedded video, not just a raw URL. But YouTube has its own embed format. So does Vimeo. And Spotify. And Flickr. And hundreds of other services.
Without a standard, you'd need custom code for every provider. oEmbed eliminates that. It gives every provider a single, predictable API that any consumer can call to get embed information for a URL.
How it works
The oEmbed protocol defines two roles: a provider (the site that hosts content, like YouTube) and a consumer (the site that wants to embed it, like WordPress). The flow is simple:
A consumer encounters a URL, e.g. from user input or a feed.
The consumer sends an HTTP GET to the provider's oEmbed endpoint with the URL as a parameter.
The provider responds with JSON (or XML) containing titles, thumbnails, and embed HTML.
GET https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ&format=json
{
"type": "video",
"version": "1.0",
"title": "Rick Astley - Never Gonna Give You Up",
"author_name": "Rick Astley",
"author_url": "https://www.youtube.com/@RickAstleyYT",
"provider_name": "YouTube",
"provider_url": "https://www.youtube.com/",
"thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg",
"thumbnail_width": 480,
"thumbnail_height": 360,
"html": "<iframe src=\"https://www.youtube.com/embed/dQw4w9WgXcQ\" ...></iframe>",
"width": 200,
"height": 113
} The four response types
Every oEmbed response has a type field. The type determines which additional fields are available and how the consumer should render the content.
photo
A static image. The consumer displays it directly using the provided URL and dimensions.
video
A playable video. The response includes an HTML snippet (usually an iframe) the consumer can embed directly.
rich
Generic rich content (tweets, posts, maps, etc.). Like video, it provides an HTML snippet for embedding.
link
A simple link with metadata. No embed HTML — the consumer renders a card or link preview using the title and thumbnail.
Response fields
All oEmbed responses share a set of common fields. Only type and version are always required. Everything else is optional (unless the type demands it).
| Field | Type | Description |
|---|---|---|
| type | string | required One of: photo, video, rich, link |
| version | string | required Always "1.0" |
| title | string | Title of the resource |
| author_name | string | Name of the content author |
| author_url | string | URL of the author's profile |
| provider_name | string | Name of the provider (e.g. "YouTube") |
| provider_url | string | URL of the provider's homepage |
| thumbnail_url | string | URL of a thumbnail image |
| thumbnail_width | number | Width of the thumbnail in pixels |
| thumbnail_height | number | Height of the thumbnail in pixels |
| html | string | HTML embed snippet (required for video and rich types) |
| width | number | Width in pixels (required for photo, video, rich) |
| height | number | Height in pixels (required for photo, video, rich) |
| url | string | Source URL of the image (required for photo type) |
Discovery
How does a consumer know which endpoint to call? There are two mechanisms:
1. Provider registry
A central list of known providers, their endpoints, and the URL patterns they support. The consumer checks the URL against registered schemes (e.g. https://www.youtube.com/watch?v=*). This is the most common approach and what services like our provider directory catalog.
2. Link-tag discovery
Providers can also embed a <link> tag in their HTML pages that points to the oEmbed endpoint. The consumer fetches the page, finds the tag, and follows it.
<link rel="alternate" type="application/json+oembed" href="https://example.com/oembed?url=https://example.com/post/123" title="My Post Title" />
You can test both mechanisms using our Discovery Checker.
Request parameters
When a consumer calls a provider's oEmbed endpoint, it sends these query parameters:
| Parameter | Required | Description |
|---|---|---|
| url | required | The URL to retrieve embed information for |
| maxwidth | optional | Maximum width of the embed in pixels |
| maxheight | optional | Maximum height of the embed in pixels |
| format | optional | Response format: "json" or "xml" (default varies by provider) |
Implementing oEmbed
- 1. When you encounter a URL, check it against the provider registry to find a matching endpoint.
- 2. If no match, optionally fetch the page and look for
<link>discovery tags. - 3. Send a GET request to the endpoint with the URL (and optional maxwidth/maxheight).
- 4. Parse the JSON response and render based on the
typefield. - 5. Cache the response — oEmbed data rarely changes.
- 1. Create an endpoint (e.g.
/oembed) that accepts aurlquery parameter. - 2. Validate the URL belongs to your domain and maps to real content.
- 3. Return a JSON response with the required fields for your content type.
- 4. Add
<link>discovery tags to your HTML pages. - 5. Register your provider in the official oEmbed registry.
Security considerations
- ! Sanitize HTML. The
htmlfield can contain arbitrary markup. Always sanitize or sandbox it (e.g. in an iframe with restricted permissions). - ! Validate URLs. Ensure returned URLs use HTTPS and point to expected domains. Don't blindly trust provider responses.
- ! Respect maxwidth/maxheight. As a provider, honor dimension constraints. As a consumer, always set them to prevent oversized embeds.
- ! Use allowlists. Only fetch oEmbed data from known, trusted providers to prevent SSRF and data exfiltration.
Try it yourself
Use our developer tools to see oEmbed in action.