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:

FormExampleNotes
/medias/<hashed_id>https://your-subdomain.wistia.com/medias/e4a27b971dCanonical form. The ID is the media's hashed ID.
/medias/<hashed_id>/managehttps://your-subdomain.wistia.com/medias/e4a27b971d/manageThe trailing /manage is accepted and resolves identically — you don't have to strip it.
/m/<hashed_id>https://your-subdomain.wistia.com/m/e4a27b971dShort form. Uses the same hashed ID as /medias/.
/s/<slug>https://your-subdomain.wistia.com/s/a1b2c3d4Share link. The slug is a different identifier from the media hashed ID (see below).

/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 unlocked

A /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:

FormValid oEmbed input?What it is
https://fast.wistia.com/embed/channel/<hashed_id>YesThe 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>NoThe 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 URL

Paste the fast.wistia.com/embed/channel/<hashed_id> share link into the oEmbed endpoint. The your-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 url field 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's url value 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 dimensions

Unlike 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 width and height, or
  • pass responsive=true for a fluid-width embed.

If you request mode=inline without width/height and without responsive=true, the returned embed has empty width/height values (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=600

which 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=true

which 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=playlist

This 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 endpoint

If 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:

FormExample
/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

A 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 States

The 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

The 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

The 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-dependent

The 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 links

Only 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 embed

Detecting 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 honors mode), 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 a 404 or 501 — see Troubleshooting.


Parameters

The oEmbed endpoint follows the oEmbed spec, and reads these parameters at the top level of the request (alongside url):

NameTypeDescription
urlstringRequired. The Wistia URL to embed (URL-encoded).
formatstringjson (default) or xml. You can also set this via the endpoint path — oembed.json / oembed.xml.
callbackstringWraps the JSON response in a JavaScript function call for JSONP. JSON requests only.
maxwidthintegerMaximum width of the returned embed.
maxheightintegerMaximum height of the returned embed.
widthintegerRequested width of the returned embed.
heightintegerRequested 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.

📝

Note

Options 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 the url parameter. 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=myCallback

returns 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 safe

For 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

  1. If an invalid URL (one that doesn't match a recognized Wistia URL path) is given, the endpoint will return 404 Not Found.
  2. If an unparseable URL is given in the url param, the endpoint will return 404 Not Found.
  3. If a media is found, but failed to process, the endpoint will return 404 Not Found.
  4. 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!