Wistia and oEmbed
oEmbed is a simple API to get information about embedded content on a URL. For Wistia, it's a great way to programmatically get embed codes for videos if you have their URLs.
Grab embed codes using your video's URL with oEmbed, an easy API to implement with Wistia.
The Endpoint
Our oEmbed endpoint is: https://fast.wistia.com/oembed
Pass a Wistia URL as the url parameter and the endpoint returns an embed code (plus metadata) for that media. The endpoint is host-agnostic — it matches on the URL path, not the domain — so a URL on your own Wistia subdomain (your-subdomain.wistia.com/...), on home.wistia.com, or on a wi.st short domain all work the same way.
Media URLs
These are the URL forms that point at a single piece of media. All four resolve to the same embed code for that media:
| Form | Example | Notes |
|---|---|---|
/medias/<hashed_id> | https://your-subdomain.wistia.com/medias/e4a27b971d | Canonical form. The ID is the media's hashed ID. |
/medias/<hashed_id>/manage | https://your-subdomain.wistia.com/medias/e4a27b971d/manage | The trailing /manage is accepted and resolves identically — you don't have to strip it. |
/m/<hashed_id> | https://your-subdomain.wistia.com/m/e4a27b971d | Short form. Uses the same hashed ID as /medias/. |
/s/<slug> | https://your-subdomain.wistia.com/s/a1b2c3d4 | Share link. The slug is a different identifier from the media hashed ID (see below). |
/m/ and /medias/ share an ID — /s/ does not
/m/ and /medias/ share an ID — /s/ does not/m/<id> and /medias/<id> use the same hashed ID, so you can convert between them with a path-only swap.
A /s/<slug> share link is its own identifier space — the slug is not the media hashed ID. The endpoint maps the slug back to the underlying media, but a find-and-replace from /medias/ to /s/ (or vice versa) will not produce a working link, because the slug and the hashed ID are different values.
Share links only resolve while unlockedA
/s/<slug>URL resolves through oEmbed only when the share link is unlocked. A locked share link returns an empty response. Treat/s/as "valid input, but only resolves for unlocked share links" — don't assume every/s/link will return an embed.
An Example
Get the embed code and metadata for a media at https://home.wistia.com/medias/e4a27b971d in JSON format. The url parameter must be URL-encoded:
curl "https://fast.wistia.com/oembed?url=https%3A%2F%2Fhome.wistia.com%2Fmedias%2Fe4a27b971d"
This returns:
{
"version": "1.0",
"type": "video",
"html": "<iframe src=\"https://fast.wistia.net/embed/iframe/e4a27b971d\" title=\"Brendan - Make It Clap Video\" allow=\"autoplay; fullscreen\" allowtransparency=\"true\" frameborder=\"0\" scrolling=\"no\" class=\"wistia_embed\" name=\"wistia_embed\" msallowfullscreen width=\"960\" height=\"540\"></iframe>\n<script src=\"https://fast.wistia.net/assets/external/E-v1.js\" async></script>",
"width": 960,
"height": 540,
"provider_name": "Wistia, Inc.",
"provider_url": "https://wistia.com",
"title": "Brendan - Make It Clap",
"thumbnail_url": "https://embed-ssl.wistia.com/deliveries/2d2c14e15face1e0cc7aac98ebd5b6f040b950b5.jpg?image_crop_resized=960x540",
"thumbnail_width": 960,
"thumbnail_height": 540,
"player_color": "174bd2",
"duration": 16.51
}If you're looking for XML instead of JSON, use: https://fast.wistia.com/oembed.xml
For all the fine details about the options supported, see the official oEmbed spec.
Channel URLs
A channel is embedded through a single URL form. Unlike media, one of the two channel URLs you'll encounter is not a valid oEmbed input:
| Form | Valid oEmbed input? | What it is |
|---|---|---|
https://fast.wistia.com/embed/channel/<hashed_id> | Yes | The channel share link — the URL the channel's share affordance and the Customize modal hand you. Use this one. |
https://<your-subdomain>.wistia.com/channels/<hashed_id> | No | The channel's page URL (the logged-in, account-gated destination you see in the address bar). Returns an empty response from the oEmbed endpoint. |
Use the share link, not the page URLPaste the
fast.wistia.com/embed/channel/<hashed_id>share link into the oEmbed endpoint. Theyour-subdomain.wistia.com/channels/<hashed_id>page URL is not a valid input — it returns an empty response.Note one asymmetry: when the share link resolves, the canonical
urlfield in the response points back at the/channels/page URL. The endpoint emits that/channels/URL but won't accept it as input — don't feed the response'surlvalue back in.
The response is a script embed, not an iframe
A channel oEmbed returns "type": "rich". The html is a channel.js <script> tag plus a <div class="wistia_channel ..."> container — not an <iframe>. This is by design: channels are not iframe-compatible, so they use a script-based embed rather than the iframe embed you get for a single media.
By default the embed renders as a popover at 360×360.
For example:
curl "https://fast.wistia.com/oembed?url=https%3A%2F%2Ffast.wistia.com%2Fembed%2Fchannel%2F9c5u5vvz1d"returns:
{
"version": "1.0",
"type": "rich",
"html": "<script src=\"https://fast.wistia.com/assets/external/channel.js\" async></script><link rel=\"stylesheet\" href=\"https://fast.wistia.com/embed/channel/project/9c5u5vvz1d/font.css\" /><div class=\"wistia_channel wistia_async_9c5u5vvz1d mode=popover\" style=\"height:360px;position:relative;width:360px;\"></div>",
"provider_name": "Wistia, Inc.",
"provider_url": "https://wistia.com",
"title": "Talking Too Loud with Chris Savage",
"url": "https://home.wistia.com/channels/9c5u5vvz1d",
"thumbnail_url": "https://embed-ssl.wistia.com/deliveries/0d2f9fb0f948e4c6dd6bae96299e0a65/file.?image_crop_resized=200x200",
"thumbnail_width": 200,
"thumbnail_height": 200
}Embedding a channel inline
To render the channel inline (in the page flow) instead of as a popover, pass mode=inline on the channel URL. As with media parameters, this travels with the url value, so it must be URL-encoded:
https://fast.wistia.com/embed/channel/<hashed_id>?mode=inline
Inline embeds need explicit dimensionsUnlike the popover (which defaults to 360×360), an inline embed has no default size. You must give it dimensions one of two ways:
- pass both
widthandheight, or- pass
responsive=truefor a fluid-width embed.If you request
mode=inlinewithout width/height and withoutresponsive=true, the returned embed has emptywidth/heightvalues (height:px;width:px;) and won't lay out correctly.
So pass dimensions explicitly:
https://fast.wistia.com/embed/channel/<hashed_id>?mode=inline&width=960&height=600which returns a container styled height:600px; width:960px;. Or go fluid with responsive=true:
https://fast.wistia.com/embed/channel/<hashed_id>?mode=inline&responsive=truewhich returns a container styled min-height:100vh; width:100%;. You can also use maxwidth / maxheight to cap dimensions.
Embedding a channel as a playlist
A channel's videos can also be embedded as a playlist by passing mode=playlist. There's no separate playlist URL — a playlist is a mode of the channel embed, so you use the same channel share link:
https://fast.wistia.com/embed/channel/<hashed_id>?mode=playlistThis returns "type": "rich" — a playlist.js <script> plus a <wistia-playlist> web component bound to the channel:
{
"version": "1.0",
"type": "rich",
"html": "<script src=\"https://fast.wistia.com/playlist.js\" async></script><wistia-playlist channel-id=\"9c5u5vvz1d\"></wistia-playlist>",
"provider_name": "Wistia, Inc.",
"provider_url": "https://wistia.com",
"title": "Talking Too Loud with Chris Savage"
}Pass responsive=true for a fluid-width playlist (which adds a responsive="true" attribute and drops fixed width/height); width / height / maxwidth / maxheight work here too.
Domain Restrictions apply at render time, not at the endpointIf a channel has Domain Restrictions enabled, the oEmbed endpoint still resolves and returns the embed code normally. The restriction is enforced when the embed renders: on a domain that isn't on the channel's approved list, the channel shows "This channel is not authorized to be here" instead of playing. So a successful oEmbed response does not guarantee the channel will display — the page it's embedded on must be on an approved domain.
Live Event URLs
A live event is embedded through either of two URL forms — they behave identically:
| Form | Example |
|---|---|
/live/events/<hashed_id> | https://your-subdomain.wistia.com/live/events/u3l2wffdc9 (audience share link) |
/live/<hashed_id> | https://your-subdomain.wistia.com/live/u3l2wffdc9 (address-bar form) |
Both resolve the same event. The URL form is not what determines the embed — the embedType parameter is.
embedType is required
embedType is requiredA live event URL has no default embed type. You must pass an embedType parameter, URL-encoded onto the url value, or the endpoint returns an error:
curl "https://fast.wistia.com/oembed?url=https%3A%2F%2Fyour-subdomain.wistia.com%2Flive%2Fevents%2Fu3l2wffdc9"Returns:
{"error": "Live event embed code type is required."}This is expected behavior, not a broken URL — the fix is to add an embedType, not to change the URL. There are exactly two valid values: webinar and registration. Any other value returns an enum error:
{"error": "Unknown live event embed code type 'foo'. Expected one of the following: registration, webinar."}
Event StatesThe endpoint doesn't gate on event state. The oEmbed endpoint returns an embed regardless of whether the event is upcoming, live, or ended — a webinar embed resolves normally for an event that already finished. The live/replay/ended experience is resolved by the embed component when it renders, not by the endpoint (the same render-time pattern as channel Domain Restrictions).
embedType=webinar
embedType=webinarThe webinar type embeds the event experience itself — the room, with the registration → live → replay progression handled inline by the component. It returns "type": "rich": a webinar-v1.js <script> plus a <wistia-webinar> web component.
curl "https://fast.wistia.com/oembed?url=https%3A%2F%2Fyour-subdomain.wistia.com%2Flive%2Fevents%2Fu3l2wffdc9%3FembedType%3Dwebinar"{
"version": "1.0",
"type": "rich",
"html": "<script src=\"https://fast.wistia.net/assets/external/webinar-v1.js\" async></script><wistia-webinar event-id=\"u3l2wffdc9\"></wistia-webinar>",
"provider_name": "Wistia, Inc.",
"provider_url": "https://wistia.com",
"title": "Live Event - Jun 1st, 2026"
}Pass a width parameter to set a fixed pixel width on the component (e.g. &width=960 renders <wistia-webinar ... style="width: 960px;">).
embedType=registration
embedType=registrationThe registration type returns the event's registration entry point — a form.js <script> plus a bare <wistia-form> web component bound to the event:
curl "https://fast.wistia.com/oembed?url=https%3A%2F%2Fyour-subdomain.wistia.com%2Flive%2Fevents%2Fu3l2wffdc9%3FembedType%3Dregistration"{
"version": "1.0",
"type": "rich",
"html": "<script src=\"https://fast.wistia.net/assets/external/form.js\"></script><wistia-form live-event-id=\"u3l2wffdc9\"></wistia-form>",
"provider_name": "Wistia, Inc.",
"provider_url": "https://wistia.com",
"title": "Live Event - Jun 1st, 2026 Registration Form"
}
What this renders is state-dependentThe oEmbed registration output is always this bare
<wistia-form live-event-id="...">element. What it displays is decided at render time by the component, based on the event's state — it shows the sign-up form while registration is open, and a state-aware entry point (for example, a "the event is happening now — go take a look" call-to-action) once the event is live or has ended. A registration embed therefore does not guarantee a fillable form; treat it as "the registration entry point for this event, whatever that is right now."
Never embed host or panelist linksOnly the audience links (
/live/events/<id>and/live/<id>) are meant to be embedded. The host link (/live/events/<id>/join) and panelist links (/live/events/<id>/join/<uuid>) are role-grant credentials — they grant host or panelist access to whoever opens them. They are not valid oEmbed inputs and must never be embedded or shared publicly. Treat them like access tokens, not like share URLs.
Detecting Wistia URLs
If you want to automatically detect Wistia URLs on a page and run them against the endpoint, match on the path, not the host — the endpoint accepts a URL on any host as long as the path begins with one of its recognized prefixes (/embed/, /medias/, /m/, /s/, /live/). A reasonable detection pattern:
https?:\/\/[^\/]+\/(embed|medias|m|s|live)\/.+Or, if you don't speak regex, match any URL whose path starts with:
/embed/... (iframe, channel, transcripts)/medias/... (public media)/m/... (short media link)/s/... (media share link)/live/... (live events)
A match isn't a guarantee of an embedDetecting a URL only means it's shaped like a valid oEmbed input — it doesn't guarantee the endpoint will return an embed. Some inputs need an extra parameter (a live event requires an
embedType; a channel honorsmode), and some only resolve in a particular state (a/s/share link resolves only while unlocked). When an input doesn't resolve, the endpoint returns a404or501— see Troubleshooting.
Parameters
The oEmbed endpoint follows the oEmbed spec, and reads these parameters at the top level of the request (alongside url):
| Name | Type | Description |
|---|---|---|
url | string | Required. The Wistia URL to embed (URL-encoded). |
format | string | json (default) or xml. You can also set this via the endpoint path — oembed.json / oembed.xml. |
callback | string | Wraps the JSON response in a JavaScript function call for JSONP. JSON requests only. |
maxwidth | integer | Maximum width of the returned embed. |
maxheight | integer | Maximum height of the returned embed. |
width | integer | Requested width of the returned embed. |
height | integer | Requested height of the returned embed. |
If you pass width, height, maxwidth, or maxheight (or any combination), the other dimensions may be adjusted to preserve the media's aspect ratio.
Anything that controls the kind of embed you get back — embedType for live events, mode for channels, and the per-embed options for media — is not a top-level parameter. Those travel inside the url value's own query string (see the per-family sections above), and the full set of media embed options is documented in the Embed Options Documentation.
NoteOptions that change the kind of embed (
embedType,mode, and other embed options) are attached to the Wistia media URL, not to the oEmbed call itself. So they must be URL-encoded to travel along with theurlparameter. The top-level parameters in the table above (maxwidth,maxheight,width,height,format,callback) are passed directly to the oEmbed endpoint.
Using JSONP
JSONP lets you call the oEmbed endpoint from a page on a different origin without running into cross-domain restrictions. Add a callback parameter naming a JavaScript function, and the endpoint wraps its JSON response in a call to that function instead of returning raw JSON.
For example, requesting:
https://fast.wistia.com/oembed.json?url=https%3A%2F%2Fhome.wistia.com%2Fmedias%2Fe4a27b971d&callback=myCallbackreturns the response wrapped like this:
/**/myCallback({"version":"1.0","type":"video", ...});So you can load it with a <script> tag and have myCallback receive the data:
<script>
function myCallback(data) {
// data is the parsed oEmbed response
console.log(data.thumbnail_url);
}
</script>
<script src="https://fast.wistia.com/oembed.json?url=https%3A%2F%2Fhome.wistia.com%2Fmedias%2Fe4a27b971d&callback=myCallback"></script>
Callback names must be safeFor security, the callback value must be a plain JavaScript function reference — letters, digits, _, $, ., and [ ] only (e.g. myCallback or window.myCallback). If the callback name contains anything else, the endpoint ignores it and returns plain JSON instead of a JSONP wrapper.
Working With The Thumbnail
Part of the JSON returned by the oEmbed is the thumbnail_url. This URL is a direct link to the thumbnail image asset. If your implementation involves using the thumbnail image (i.e. building your own 'popover' embeds, displaying your own play button, etc.) you should use this thumbnail image, which by default has no Wistia play button overlaid on it.
See our working with Wistia images guide for more info!
Troubleshooting
- If an invalid URL (one that doesn't match a recognized Wistia URL path) is given, the endpoint will return
404 Not Found. - If an unparseable URL is given in the url param, the endpoint will return
404 Not Found. - If a media is found, but failed to process, the endpoint will return
404 Not Found. - If a media is found but has no available embed code, the endpoint will return
501 Not Implemented. Video, Image, Audio, and Document files all currently implement oembeds. This will also occur if the video is not finished processing.
Make Your Life Easier
If you're contemplating doing an oEmbed implementation with Wistia (or any oEmbed provider for that matter), we strongly recommend checking out Embedly. By integrating with them you'll have immediate access to over 100 oEmbed providers. They also have great documentation and ready-made libraries for every popular language, plus they're just nice folks!