Free embed API · no key · no signup

Movies, TV and anime, in one iframe.

Put one iframe on your site and it plays any movie, series or anime episode by TMDB or AniList id. Adaptive HLS, subtitles, auto next and an episode list are already in the player. The look is yours from the query string.

Build your URL

Pick a title or type a TMDB id, set the options, copy the snippet. The player below is the real one, as your visitors will see it.

https://embed.vidrift.net/embed/movie/

OptionsEvery change rewrites the URL above and reloads the player. All of them are optional.

Accent brandColor
UI size uiScale
Control background controlBg
Watermark watermark=1
Start muted muted=1
Wait for a click autoplay=0
Exit button exit=1
Title bar showTitle=1
Hide controls hide
Cosmetic: keyboard shortcuts, tap to seek and the phone gestures keep working.

In the playerNothing to switch on.

Adaptive HLS
hls.js ships inside the iframe. Quality steps down on a bad connection and back up when it recovers.
Subtitles
30+ languages picked up per title, with size, colour, background and timing controls. Viewers can upload their own file.
Episodes
On TV and anime an Episodes button opens the seasons and episodes in the player. A pick swaps the iframe and keeps your parameters.
Auto next
An Up Next card with the next episode's still appears as the credits start, from The Intro DB (TV) or AniSkip (anime), and plays on after five seconds.
Skip intro
A Skip button over intros, recaps and openings, from the same two sources.
Servers
Several sources per title with automatic failover. A server the viewer picks is remembered for that title, so a dub stays a dub.
Viewer controls
Screen lock, Fit / Fill / Stretch, volume boost, playback speed, picture in picture, Chromecast.
Pause card
Three seconds into a pause, the title, year, rating, genres and overview fade in over the frame.
Messages
Progress, pause, episode changes and more arrive by postMessage, so a page can save and resume position. See the messages table.

InstallOne iframe is the integration.

Three URL shapes. Anime takes the TV shape with the show's TMDB id, or its AniList id if that is what your site has.

https://embed.vidrift.net/embed/movie/{tmdb_id}
https://embed.vidrift.net/embed/tv/{tmdb_id}/{season}/{episode}
https://embed.vidrift.net/embed/anilist/{anilist_id}/{episode}
Responsive embed
<div style="position:relative;padding-bottom:56.25%;height:0">
  <iframe src="https://embed.vidrift.net/embed/movie/1440098"
    style="position:absolute;inset:0;width:100%;height:100%;border:0"
    allowfullscreen allow="autoplay; fullscreen"></iframe>
</div>

Parameters, messages, the mobile sheet and page-owned fullscreen are in the docs.

Questions

Is it free?
Yes. Public embeds carry a single ad, which pays for the bandwidth. Nothing is metered, nothing expires, and there is no card to enter.
Do I need a key or an account?
No. There is no signup, no key and no domain allowlist. Build the URL, put it in an iframe, done. The only ids involved are TMDB's and AniList's, which are public.
Where does the content come from?
VidRift does not own or store the media. It indexes and relays what is already publicly reachable; metadata comes from TMDB. Takedown requests reach a human on Discord and are handled per title.
What breaks it?
A sandbox attribute on the iframe, and fetching an embed URL with a script instead of framing it. Everything else that is wrong in a URL is ignored rather than treated as an error.
Documentation

Everything the embed can do

The player is a single cross-origin iframe. Nothing to install, nothing to authenticate. Everything below is a URL parameter or a postMessage. That is the entire API surface.

Quick start

Paste this where you want the player. It is a complete, working, responsive embed.

Complete embed
<div style="position:relative;padding-bottom:56.25%;height:0">
  <iframe src="https://embed.vidrift.net/embed/movie/1440098"
    style="position:absolute;inset:0;width:100%;height:100%;border:0"
    allowfullscreen allow="autoplay; fullscreen"></iframe>
</div>

Keep referrerpolicy at its default or strict-origin-when-cross-origin. Do not add a sandbox attribute: a sandboxed iframe cannot play. Fetching an /embed/* URL with a script instead of framing it returns 403.

URL structure

Three shapes. Anime is TV content under the hood, so it takes the TV shape with the show's TMDB id, or its AniList id if that is what your site has.

Already embedding embed.vidrift.in? Nothing to change: it serves the same player and sends the same messages, from the https://embed.vidrift.in origin. Switch to embed.vidrift.net whenever convenient, and update any event.origin check at the same time.

Routes
https://embed.vidrift.net/embed/movie/{tmdb_id}
https://embed.vidrift.net/embed/tv/{tmdb_id}/{season}/{episode}
https://embed.vidrift.net/embed/anilist/{anilist_id}/{episode}

https://embed.vidrift.net/embed/movie/1440098     Drawn Together
https://embed.vidrift.net/embed/tv/1399/1/1       Game of Thrones S01E01
https://embed.vidrift.net/embed/tv/1429/1/1       Attack on Titan S01E01
https://embed.vidrift.net/embed/anilist/16498/1   Attack on Titan ep 1, by AniList id
https://embed.vidrift.net/embed/anilist/21/150    One Piece ep 150 (absolute, as AniList counts)

The AniList shape counts episodes the AniList way, per entry from 1, and anime movies need no episode. It answers with a redirect onto the TMDB URL, so the title plays with every server, not an anime-only one. Add ?dub=1 to start on the English dub. An AniList id with no TMDB mapping yet returns a 404.

Parameters

Every parameter is optional and validated server-side. Anything invalid is ignored rather than treated as an error, so a bad value never breaks playback.

ParameterValueWhat it does
titleTextShown on the loading card while a source is being found. Without it the player looks the title up itself.
brandText, ≤ 28 charsYour name in the eyebrow above the title on the loading card.
brandLogohttps URLYour logo above the title on the loading card, up to 28px tall. PNG or SVG with a transparent background looks best.
brandColor6-digit hexAccent for the progress bar, eyebrow and switches. With or without the #.
posterhttps URLReplaces the title's own backdrop art behind the loading card.
watermark1 or https URLA small logo in the top-right corner during playback, shown only while the controls are hidden. 1 reuses brandLogo.
showTitle1Shows the top-left title bar in windowed mode too. By default it appears only in fullscreen.
exit1Adds a Back arrow at the left of the title, and an Exit button (an X) in the corner of the "Not out yet" and "won't play" screens. Clicking it sends vidrift:exit to your page so you can close the player screen.
uiScale0.5 – 2.5Scales every control, icon and label: the bars, the menus, the Episodes view, the panels and the pause card. 1.5 for a TV screen, 0.85 for a compact box.
caps0Sentence case for the small labels (eyebrows, menu headers, the episode line in the top bar), which are uppercase by default.
controlBgsolidA flat dark strip behind the controls instead of the gradient, for bright video.
fontGoogle Fonts familySwaps the player's typeface, e.g. font=Inter or font=Bebas%20Neue. Letters, digits and spaces only.
muted1Start muted for this load. Sound comes on when the viewer taps the speaker.
autoplay0Do not autoplay. The player loads the source and waits for a click. Note that forcing autoplay with sound is not possible in any browser; the default is already the most that browsers allow.
hideComma listHides controls: progress, play (the play/pause button), volume, skip, time, title (the title and episode line in the top bar), server, next, episodes, subtitles, settings, lock, pip, cast, fullscreen. Also gradient (the dark shading behind the top and bottom bars) and pausecard (the title card that comes up after eight seconds paused). Cosmetic only: keyboard shortcuts keep working.
mobileSheetstrueOn phones, delegates the Settings, Subtitles and Source panels to your own UI. See Mobile sheet.
fsparentYour page owns fullscreen. The fullscreen button sends vidrift:fullscreen instead of fullscreening the iframe; you fullscreen the box around it and answer vidrift:fullscreen-state. Lets you swap the iframe for the next episode without leaving fullscreen. See Messages.
dub1AniList URLs only: start on the English dub server.

Finding TMDB ids

Ids come from themoviedb.org. They are in the URL of any title page, and available through the TMDB API.

  • Movies: themoviedb.org/movie/1440098 → id is 1440098
  • TV and anime: themoviedb.org/tv/1399 → id is 1399
  • Anime by AniList id: anilist.co/anime/16498 → /embed/anilist/16498/{episode}. No TMDB lookup needed on your side.

The home page builds the finished URL for you. Type an id into it, press Enter, and the real player loads.

Customise

The player is skinned from the URL, so there is nothing to configure anywhere else. The home page has a panel that builds the URL live while you watch the result.

The loading card

While a source is found the player shows a title card: the title's own backdrop art, dimmed, with the title bottom-left, a progress hairline and the servers a viewer can switch between. Most starts never show it. It appears only when a start takes longer than about two seconds, and the art is fetched only then, so a fast start costs nothing extra.

  • brand: your name in the eyebrow above the title, max 28 characters.
  • brandLogo: an https URL to a PNG, SVG or JPG, shown above the title.
  • brandColor: a 6-digit hex accent for the progress bar, eyebrow and switches.
  • poster: an https image URL that replaces the backdrop art.

Size, contrast, type and start

  • uiScale: 1.5 makes every control TV-sized, 0.85 tightens a small box. Hit targets scale with it.
  • controlBg=solid: a flat dark strip behind the controls, for video with bright edges where the gradient is not enough.
  • font: any Google Fonts family name. It replaces the display and body faces in one go.
  • muted=1 and autoplay=0: start silent, or do not start at all until the viewer clicks. By default the player autoplays muted and switches sound on at the first interaction, which is the most any browser allows.

During playback

  • watermark=1 shows brandLogo as a small corner bug, top-right, only while the controls are hidden, so it never covers the top bar. Pass an https URL instead of 1 to use a different image.
  • showTitle=1 keeps the title bar (top-left) visible in windowed mode, not only in fullscreen.
  • exit=1 adds a Back arrow at the left of the title, and an Exit button in the top-right corner of the "Not out yet" and "This one won't play right now" screens. It sends vidrift:exit to your page and nothing more, so your page decides what leaving means: close a modal, go back, or navigate.
  • hide takes a comma list of controls to remove: progress, play, volume, skip, time, title, server, next, episodes, subtitles, settings, lock, pip, cast, fullscreen, plus gradient for the dark shading behind the bars and pausecard for the title card shown after eight seconds paused. Hiding is cosmetic: keyboard shortcuts, tap-to-seek and the phone gestures keep working, so a viewer is never locked out of playback.
Fully customised embed
https://embed.vidrift.net/embed/movie/1440098
  ?title=Drawn%20Together
  &brand=Acme%20Stream
  &brandLogo=https://acme.com/logo.svg
  &brandColor=%2300d1b2
  &watermark=1
  &exit=1
  &hide=server,cast,pip
Handling Exit
window.addEventListener("message", (e) => {
  if (e.origin !== "https://embed.vidrift.net") return;
  if (e.data.type === "vidrift:exit") closePlayer();
});

Every value is validated. A non-https URL, a colour that is not a hex triplet, an unknown control name or an image that 404s is ignored, never an error, so a bad value can never break playback.

What viewers control

  • Lock. A padlock sits with the top-right controls. Locked, the controls stay hidden and a click or tap does nothing except bring up an Unlock button; keyboard shortcuts, Skip intro and Up Next keep working. It resets on every load. hide=lock removes it.
  • Video size. Fit, Fill and Stretch are in the Settings menu. Fit shows the whole picture, Fill crops it to cover the player, Stretch distorts it to fill. The choice is the viewer's and is remembered in their browser, so there is no parameter for it.
  • Server. A server the viewer picks by hand is remembered for that title in their browser and leads on its other episodes, so an anime watched in dub stays in dub. A failover is never remembered, and your provider parameter still wins.
  • Touch. On a phone the first tap shows the controls and the centre button plays or pauses. A double tap on either side seeks 10 seconds.
  • Pause card. Three seconds into a pause, the title, year, TMDB rating, genres and overview fade in bottom-left over the frame. It never covers the controls, and it goes the moment playback resumes or the viewer seeks. There is no parameter for it.

Autoplay

The player always attempts autoplay, muted, as soon as a source is ready. That is what every browser allows without a user gesture, so there is nothing to enable. Sound comes on the moment the viewer interacts with the player.

Episodes

For TV and anime the control bar has an Episodes button, beside Next. It opens the show's seasons as tabs and the chosen season's episodes as a list, with the one playing marked. A pick swaps this iframe to the new episode and keeps every parameter you set, so there is nothing to wire up. Just before it swaps, the player sends vidrift:episode with the new season and episode, in case your page shows the current episode somewhere and wants to stay in step.

If your page has an episode list of its own and wants to stay in charge, send vidrift:nextup-info (next section) once the iframe has loaded: the in-player list and its Next button step aside and the player follows your page instead. hide=episodes removes the button outright.

Keeping your URL in step
window.addEventListener("message", (e) => {
  if (e.origin !== "https://embed.vidrift.net") return;
  if (e.data.type === "vidrift:episode") history.replaceState(null, "", `/tv/${e.data.tmdbId}/${e.data.season}/${e.data.episode}`);
});

Auto next episode

For TV and anime the player shows a five-second Up Next card with the next episode's still as the credits start (The Intro DB for TV, AniSkip's ending marker for anime, 30 seconds before the end as the fallback), then plays the next one in the same iframe. It knows the next episode from the same season list the Episodes button uses, so by default there is nothing to do.

A page that drives episodes itself takes over with the two messages below. Once your page has sent vidrift:nextup-info, the card and the Next episode button in the control bar follow your page: the countdown or a click sends vidrift:nextup-play and your page swaps the URL. Send next: null on the last episode and the button goes away; hide=next removes it for good.

1 · Parent → Player, once the iframe has loaded (only if your page drives episodes)
iframe.contentWindow.postMessage({
  type: "vidrift:nextup-info",
  next: { season: 1, episode: 2,
          // optional, shown on the card:
          name: "The Kingsroad", still: "https://image.tmdb.org/t/p/w300/..." }
}, "https://embed.vidrift.net");
2 · Player → Parent, when the countdown ends or the viewer clicks Play Next
window.addEventListener("message", (e) => {
  if (e.origin !== "https://embed.vidrift.net") return;
  if (e.data.type === "vidrift:nextup-play") {
    // e.data: { tmdbId, mediaType, season, episode } of the episode that just finished
    iframe.src = `https://embed.vidrift.net/embed/tv/${e.data.tmdbId}/${e.data.season}/${e.data.episode + 1}`;
  }
});

Messages

Every message is scoped to the https://embed.vidrift.net origin. Always check event.origin before trusting an incoming message, and always pass the origin as the second argument when sending one. All player messages carry tmdbId, mediaType, season and episode. On anime titles they also carry anilist: { id, mal, episode }, the AniList entry actually playing (with its MyAnimeList id) and the episode number inside it (counted from 1, so the next cour reports the next entry), null elsewhere. Key continue watching on it from vidrift:progress if your site thinks in AniList ids.

TypeDirectionWhen
vidrift:progressPlayer → ParentEvery 5 seconds while playing, after the first 5 seconds. Adds currentTime and duration.
vidrift:pausedPlayer → ParentPlayback paused, by the viewer or at the end. Adds currentTime. Use it to show your own UI over the player, such as a nav bar.
vidrift:unpausedPlayer → ParentPlayback started or resumed. Adds currentTime. Hide that UI again.
vidrift:ui-visiblePlayer → ParentThe player's own controls (top and bottom bars) came on screen: mouse move, tap, or pause. Show an overlay of yours at the same moment and it reads as part of the player.
vidrift:ui-hiddenPlayer → ParentThe controls auto-hid, the pointer left while playing, or the player was locked. Hide that overlay again. Sent once per change, plus once on load for the initial state.
vidrift:nextupPlayer → ParentOnce, as the credits or ending theme start (30 seconds before the end when no skip data exists).
vidrift:nextup-playPlayer → ParentThe Up Next countdown finished, or the viewer clicked Play Next or the Next episode button. Only sent once your page has sent vidrift:nextup-info; otherwise the player moves on by itself.
vidrift:episodePlayer → ParentThe viewer picked an episode in the player, or Up Next moved on by itself. Carries the new season and episode; the iframe swaps right after.
vidrift:endedPlayer → ParentPlayback reached the end.
vidrift:exitPlayer → ParentThe viewer clicked the Exit button (?exit=1). Close or navigate away from the player on your side; the player does nothing else.
vidrift:mobile-panelPlayer → ParentA phone viewer opened a panel, with mobileSheets=true. Adds panel, options and current.
vidrift:fullscreenPlayer → ParentWith fs=parent: the viewer pressed the fullscreen button, F or double-clicked. on is true to enter, false to leave. Call requestFullscreen() on the element around the iframe right away, inside the message handler, so the viewer's click still counts.
vidrift:statePlayer → ParentAfter your page sends vidrift:get-state: right away, then again whenever anything in it changes. See Controlling the player.
vidrift:command-errorPlayer → ParentA command could not be carried out. Adds command, field and reason.
vidrift:play · vidrift:pauseParent → PlayerPlay or pause.
vidrift:skipParent → PlayerMove by seconds, negative to go back.
vidrift:setParent → PlayerChange volume, speed, server, quality, audio, subtitles, their look or their sync.
vidrift:castParent → PlayerStart casting; needs one click inside the player.
vidrift:get-stateParent → PlayerAsk for vidrift:state and keep it coming.
vidrift:resumeParent → PlayerSeek to currentTime in seconds. Safe to send before the video is ready; it is applied once it is.
vidrift:quality-preferenceParent → PlayerPin a rendition: label is "1080p", "720p", "480p", "360p" or "Auto".
vidrift:exit-controlParent → PlayerWith exit=1 in the URL: show: false hides the player's Back arrow, show: true brings it back, without changing the URL. For a page that draws its own back control on some layouts (a phone header above the player) and leaves it to the player on others; send it on load and whenever the layout changes.
vidrift:title-controlParent → Playershow: false takes the title and episode line out of the player's top bar outside fullscreen, show: true puts them back. In fullscreen your page is not on screen, so the player shows them there anyway; hide=title removes them everywhere. For a page that prints the title itself on some layouts; send it on load and whenever the layout changes.
vidrift:nextup-infoParent → PlayerTake over episodes: the Up Next card and Next button use your next: { season, episode, name?, still? } (an https still shows on the card), and the in-player Episodes list steps aside. Send next: null on the last episode.
vidrift:mobile-optionParent → PlayerReply to a mobile-panel message with the chosen option and value.
vidrift:fullscreen-stateParent → PlayerWith fs=parent: tell the player whether your box is fullscreen (on). Send it on every fullscreenchange and once each new iframe has loaded, so the button icon, the title bar and the phone sheets follow.
One listener, every event
window.addEventListener("message", (e) => {
  if (e.origin !== "https://embed.vidrift.net") return;
  switch (e.data.type) {
    case "vidrift:progress": save(e.data.tmdbId, e.data.currentTime); break;
    case "vidrift:ended":    markWatched(e.data.tmdbId);         break;
  }
});

Controlling the player

Your page can drive the player and read everything its menus offer. Pause it while a menu of yours is open, or move Quality, Server, Audio and Subtitles into your own sidebar and take them out of the player with hide=settings,subtitles,server. Commands are messages to the iframe. The player answers vidrift:get-state with vidrift:state, and anything it cannot do with vidrift:command-error.

SendFieldsDoes
vidrift:playPlays. Sent before the stream is ready, it lets the start go ahead.
vidrift:pausePauses. Sent before the stream is ready, the first frame waits.
vidrift:skipseconds10 forward, -10 back. Stops at the start and the end, and flashes the seek icon on the video, as a double tap does.
vidrift:setany belowChanges one field or several in one message.
vidrift:castStarts casting to a TV, with one click from the viewer (below).
vidrift:get-stateAnswers with vidrift:state, then sends it again every time something in it changes. Send it once, after the iframe loads.
vidrift:state (and what vidrift:set takes)
{
  type: "vidrift:state", tmdbId, mediaType, season, episode, anilist,
  paused: false, currentTime: 812.4, duration: 2884.1,
  volume: 0.8, muted: false, speed: 1,
  server:   "vela", servers:   [{ value: "vela", label: "Vela", state: "ready" }, …],
  source:   "0",    sources:   [{ value: "0", label: "Vela · English" }, …],
  quality:  "2",    qualities: [{ value: "auto", label: "Auto" }, …],
  audio:    "0",    audios:    [{ value: "0", label: "English" }, …],
  subtitle: "en",   subtitles: [{ value: "off", label: "Off" }, …],
  subtitleStyle: { size: 100, color: "#ffffff", background: "box",
                   font: "default", position: 0 },
  subtitleOffset: 0,
  cast: "available"   // "unavailable" | "available" | "connecting" | "connected"
}
  • vidrift:set takes volume (0 to 1), muted, speed (0.25 to 4), subtitleOffset (seconds, −30 to 30; positive shows captions later, the "Sync to audio" control) and subtitleStyle with any of size (percent, 50 to 200), color (#rrggbb), background (box or shadow), font (default, system or serif) and position (0 to 40, how far the captions are raised).
  • For server, source, quality, audio and subtitle, send back a value exactly as its list gives it. quality also takes a label, such as "720p", so you can pin a rendition without reading the list. A server picked this way is remembered for the viewer, as a pick from the player's own menu is.
  • The lists change while a title plays: subtitles arrive, a failing server hands over to the next, and each server brings its own qualities and audio. vidrift:state follows all of it, so draw your menus from the latest one. audios is empty when there is only one track. A server's state is "ready" once it has played here, "failed" if it had nothing, and empty before it is tried. currentTime is a snapshot; vidrift:progress keeps time.

When a command is refused

vidrift:command-error names the command, for vidrift:set the field, and a reason. The other fields of the same message still apply.

  • invalid: a value that is not in its list, or out of range.
  • click-required: the viewer has to click inside the player first. Browsers turn sound on and open the Cast picker only from a click in the frame that plays, so unmuting can come back with this, and the video carries on muted. On some visits the player first checks the page it is embedded in; until the viewer's first click in the player, those take only pause and get-state.
  • not-ready: vidrift:skip before the stream has loaded.
  • unavailable: vidrift:cast with nothing to cast to. cast in the state tells you before you offer it.

Casting

A device picker opens only from a click inside the player, and a message is not one. So vidrift:cast brings the controls up with the player's Cast button marked "Click to cast" for eight seconds (shown even with hide=cast), answers click-required, and the viewer's click on it starts casting.

Pause behind your menu, plus a quality list of your own
const EMBED = "https://embed.vidrift.net";
const send = (msg) => iframe.contentWindow.postMessage(msg, EMBED);
let state = null, wasPlaying = false;

iframe.addEventListener("load", () => send({ type: "vidrift:get-state" }));
window.addEventListener("message", (e) => {
  if (e.origin !== EMBED) return;
  if (e.data.type === "vidrift:state") {
    state = e.data;
    drawQualityMenu(state.qualities, state.quality);
  }
  if (e.data.type === "vidrift:command-error") console.warn(e.data.reason, e.data);
});

function openMenu() {
  wasPlaying = state && !state.paused;
  send({ type: "vidrift:pause" });
}
function closeMenu() {
  if (wasPlaying) send({ type: "vidrift:play" });
}

// From your own buttons:
send({ type: "vidrift:set", quality: "720p" });
send({ type: "vidrift:skip", seconds: -10 });
send({ type: "vidrift:set", subtitle: "en", subtitleStyle: { size: 120 } });

Mobile sheet

On phones the player opens its own bottom sheet for Settings, Subtitles and Source. If you would rather render that sheet in your site's own UI, add ?mobileSheets=true to the URL and answer the panel message. Leave the flag off and no listener is needed.

Delegated sheet
window.addEventListener("message", (e) => {
  if (e.origin !== "https://embed.vidrift.net") return;
  if (e.data.type !== "vidrift:mobile-panel") return;
  // e.data.panel: "settings" | "subtitles" | "source"
  // e.data.options: [{ option, value, label, group, desc? }]
  //   "settings" carries quality, audio, speed and fit (video size)
  // e.data.current: selected now, e.g. { quality: "auto", fit: "contain" }
  const chosen = await showYourSheet(e.data.options);
  iframe.contentWindow.postMessage({
    type: "vidrift:mobile-option",
    option: chosen.option, // use the chosen option's own field, not the panel name
    value: chosen.value
  }, "https://embed.vidrift.net");
});
  • The settings panel always has a quality group. When the stream offers nothing to choose it is a single Auto entry, and its desc says why; show desc under the label when it is present.
  • fit takes contain (Fit), cover (Fill) or fill (Stretch).
  • In fullscreen the player opens its own sheet instead of asking for yours: only the player is on screen there, so your sheet would open behind it.

Notes & limits

  • hls.js ships inside every embed page. There are no dependencies to add on your side.
  • Mobile-responsive, with iOS Safari fullscreen via webkitEnterFullscreen. That is Safari's own player, so on iPhone the player's menus are available outside fullscreen only.
  • Embeds are rate-limited per client. Normal traffic never touches the limit; scripted bulk fetching does.
  • Public embeds carry a single ad. That is what pays for the bandwidth.
  • Metadata and artwork come from TMDB. VidRift is not endorsed or certified by TMDB.
  • Issues and takedown requests reach a human on Discord.