Movies Guide

Build a world-class
Streaming Portal.

Connect TMDB's massive movie and TV database to VidCore's embed API. Users get a full Netflix-style experience — search, trending, posters, and a fullscreen player — all in a static site.

  Medium Difficulty   ~45 min read   Updated 2026
Lights, camera, deploy.
On this page
01

Overview & Architecture

A streaming portal has two moving parts: TMDB (The Movie Database) for metadata and VidCore for the actual video embed. TMDB gives you titles, posters, descriptions, ratings, and IDs. VidCore takes those IDs and renders a working video player. Neither requires a backend — both are callable from the browser.

TMDB — Metadata & Search

Free API that returns titles, posters, descriptions, ratings, cast, trailers, and IMDB/TMDB IDs. Requires a free API key. Used for search, trending lists, and building your media grid.

Free API Key

VidCore — Video Player

Embed API that takes an IMDB or TMDB ID and serves a fullscreen video player. Supports movies and TV shows (season + episode). No account needed — just construct the URL.

No Account

The data flow for every page load looks like this:

data flow
User searches "Inception"TMDB /search/movie  →  returns results with id, title, poster_pathUser clicks a result (id = 27205)TMDB /movie/27205   →  full details: runtime, genres, tagline, imdb_id
        ↓
Render detail page — build embed URL with the idVidCore iframe      →  https://vidcore.net/movie/27205?autoPlay=true
IMDB ID vs TMDB ID VidCore accepts both IMDB IDs (format: tt1234567) and TMDB integer IDs interchangeably in the {id} parameter. TMDB returns both in its API responses — use whichever is more convenient.

02

TMDB Setup & API Key

TMDB offers a free API with generous rate limits (40 requests per 10 seconds). You need an API key to authenticate requests.

1

Create a free TMDB account

Register at themoviedb.org/signup and verify your email address.

2

Request an API key

Go to your account settings → API → click "Create" under API Key (v3 auth). Select "Developer" for the use case and fill in the form. Your key is issued instantly.

3

Store the key safely

For a client-side site the key will be visible in source code — this is acceptable for read-only TMDB usage (it cannot modify data without a session token). Store it in a single config.js file so it's easy to rotate.

4

Test the key

Open this URL in your browser, replacing YOUR_KEY with your key. You should get a JSON object with configuration data.

test your API key
https://api.themoviedb.org/3/configuration?api_key=YOUR_KEY
config.js — store your key here
const TMDB_KEY  = 'YOUR_TMDB_API_KEY';
const TMDB_BASE = 'https://api.themoviedb.org/3';
const IMG_BASE  = 'https://image.tmdb.org/t/p/w500';   // poster images
const IMG_BACK  = 'https://image.tmdb.org/t/p/w1280';  // backdrop images

03

TMDB Endpoints Reference

These are the four TMDB endpoints you'll use in a streaming portal. All are GET requests to https://api.themoviedb.org/3 and require ?api_key=YOUR_KEY.

Search Movies

GET /search/movie?api_key=KEY&query={search_term}
query The search string — movie title, partial name, or keyword.
page Result page number (default: 1). Each page returns up to 20 results.
year Filter results to a specific release year.
language ISO 639-1 language code for localized results (e.g. en-US).

Search TV Shows

GET /search/tv?api_key=KEY&query={search_term}
query The TV show title to search for.
first_air_date_year Filter results by the year the show first aired.

Movie Details

GET /movie/{movie_id}?api_key=KEY
id TMDB integer ID — pass directly to VidCore.
imdb_id IMDB ID string (e.g. tt6263850) — also accepted by VidCore.
poster_path Relative path — prefix with https://image.tmdb.org/t/p/w500 for a full URL.
backdrop_path Wide landscape image — prefix with https://image.tmdb.org/t/p/w1280.
genres Array of {id, name} objects.
runtime Movie runtime in minutes.
vote_average Community rating out of 10.

Trending Movies & TV (for your home page)

GET /trending/{media_type}/{time_window}?api_key=KEY
media_type movie, tv, or all — the type of content to return.
time_window day or week — the trending window.
append_to_response Add &append_to_response=videos,credits to a movie or TV details call to fetch trailers and cast in a single request, reducing your total API calls. Example: /movie/27205?api_key=KEY&append_to_response=videos,credits

Here's a reusable fetch helper that handles all four endpoints:

tmdb.js — fetch helper
// tmdb.js — import TMDB_KEY and TMDB_BASE from config.js

/** Search movies by title */
async function searchMovies(query, page = 1) {
  const url = `${TMDB_BASE}/search/movie?api_key=${TMDB_KEY}&query=${encodeURIComponent(query)}&page=${page}`;
  const res = await fetch(url);
  return res.json();  // { results: [...], total_pages, total_results }
}

/** Search TV shows by title */
async function searchTV(query, page = 1) {
  const url = `${TMDB_BASE}/search/tv?api_key=${TMDB_KEY}&query=${encodeURIComponent(query)}&page=${page}`;
  const res = await fetch(url);
  return res.json();
}

/** Get full movie details (including imdb_id, genres, runtime) */
async function getMovie(id) {
  const url = `${TMDB_BASE}/movie/${id}?api_key=${TMDB_KEY}&append_to_response=videos,credits`;
  const res = await fetch(url);
  return res.json();
}

/** Get full TV show details */
async function getTV(id) {
  const url = `${TMDB_BASE}/tv/${id}?api_key=${TMDB_KEY}&append_to_response=videos,credits`;
  const res = await fetch(url);
  return res.json();
}

/** Trending this week — media_type: 'movie' | 'tv' | 'all' */
async function getTrending(mediaType = 'all', window = 'week') {
  const url = `${TMDB_BASE}/trending/${mediaType}/${window}?api_key=${TMDB_KEY}`;
  const res = await fetch(url);
  return res.json();
}

/** Build a full poster URL from a TMDB poster_path */
function posterURL(path, size = 'w500') {
  return path ? `https://image.tmdb.org/t/p/${size}${path}` : 'assets/no-poster.webp';
}

04

VidCore — Movie Embeds

VidCore is the video player layer. For movies, the embed URL takes a single ID (IMDB or TMDB) and renders a fullscreen player with server selection, subtitle support, and Chromecast.

EMBED https://vidcore.net/movie/{id}?autoPlay=true
{id} Movie identifier — accepts both IMDB ID (tt6263850) and TMDB integer ID (533535).

Use this URL as the src of an <iframe>. Here are the three example URLs from the VidCore docs:

VidCore movie embed examples
# Basic usage with IMDB ID
https://vidcore.net/movie/tt6263850?autoPlay=true

# With custom theme color (hex, no #)
https://vidcore.net/movie/533535?theme=16A085

# With autoplay and subtitles
https://vidcore.net/movie/533535?autoPlay=true&sub=en

Building the embed URL dynamically from a TMDB result:

JavaScript — render movie player
function buildMovieEmbed(id, options = {}) {
  const params = new URLSearchParams({
    autoPlay: true,
    ...(options.theme  && { theme: options.theme }),
    ...(options.sub    && { sub:   options.sub   }),
    ...(options.server && { server: options.server }),
  });
  return `https://vidcore.net/movie/${id}?${params}`;
}

function renderMoviePlayer(containerId, movieId) {
  const el = document.getElementById(containerId);
  el.innerHTML = `
    <iframe
      src="${buildMovieEmbed(movieId)}"
      style="width:100%;height:100%;border:none;"
      allowfullscreen
      sandbox="allow-scripts allow-same-origin allow-forms allow-pointer-lock"
    ></iframe>
  `;
}

// Example usage after fetching from TMDB:
const movie = await getMovie(533535);
renderMoviePlayer('player-container', movie.id);

05

VidCore — TV Show Embeds

TV show embeds take three path parameters: the show ID, a season number, and an episode number. VidCore also supports auto-next episode functionality for binge-watching.

EMBED https://vidcore.net/tv/{id}/{season}/{episode}?autoPlay=true
{id} TV show identifier — accepts IMDB ID or TMDB integer ID.
{season} The season number (integer). Season 1 = 1, not 01.
{episode} The episode number (integer) within the specified season.

TV-specific example URLs from the VidCore docs:

VidCore TV embed examples
# Basic — Breaking Bad, Season 1, Episode 5
https://vidcore.net/tv/tt4052886/1/5?autoPlay=true

# With auto-next episode button
https://vidcore.net/tv/63174/1/5?nextButton=true&autoNext=true

# With theme and autoplay
https://vidcore.net/tv/63174/2/1?theme=16A085&autoPlay=true

A complete TV player builder, including TMDB season/episode data fetching:

JavaScript — TV player with episode selector
function buildTVEmbed(id, season, episode, options = {}) {
  const params = new URLSearchParams({
    autoPlay: true,
    nextButton: true,
    autoNext: true,
    ...(options.theme && { theme: options.theme }),
    ...(options.sub   && { sub:   options.sub   }),
  });
  return `https://vidcore.net/tv/${id}/${season}/${episode}?${params}`;
}

/** Fetch season details from TMDB (returns episode list) */
async function getSeason(showId, seasonNum) {
  const url = `${TMDB_BASE}/tv/${showId}/season/${seasonNum}?api_key=${TMDB_KEY}`;
  const res = await fetch(url);
  return res.json();  // { episodes: [{episode_number, name, overview, still_path}] }
}

// Usage: load Season 1 Episode 1 of show with TMDB id 63174
const season = await getSeason(63174, 1);
const ep = season.episodes[0];  // first episode

document.getElementById('player').src =
  buildTVEmbed(63174, 1, ep.episode_number);

06

VidCore Optional Parameters

Both the movie and TV embed URLs support these optional query string parameters. Mix and match as needed.

title

Controls whether the media title is displayed in the player. Pass false to hide it.

poster

Determines if the poster image is shown before playback begins.

autoPlay

Controls whether the media starts playing automatically on load. Set true for seamless UX.

startAt

Starts the video at a specified time in seconds. Useful for resuming from a saved position.

theme

Changes the player's accent color. Pass a hex code without the # (e.g. theme=16A085).

server

Changes the default server for the player. Set to a server name to pre-select it for your users.

hideServer

Controls whether the server selector button is shown or hidden from the player UI.

fullscreenButton

Controls whether the fullscreen button is shown or hidden. Set false if you manage fullscreen yourself.

chromecast

Controls whether the Chromecast button is shown or hidden in the player controls.

sub

Sets the default subtitle language (e.g. en, es, fr). User can change it in the player.

nextButton TV only

Displays the "Next Episode" button when 90% of the current episode has been watched.

autoNext TV only

Automatically loads the next episode when the current one ends. Requires nextButton=true.

A fully configured embed URL combining several parameters:

fully configured embed URLs
# Movie — themed, English subs, autoplay, server selector hidden
https://vidcore.net/movie/533535?autoPlay=true&theme=c0392b&sub=en&hideServer=true

# TV show — auto-next, themed, French subs
https://vidcore.net/tv/63174/3/1?autoPlay=true&nextButton=true&autoNext=true&theme=8e44ad&sub=fr

07

Putting It All Together

Here's a complete minimal streaming portal: a search page that queries TMDB and renders a detail modal with a VidCore embed. Copy this as a starting point and style it to match your site.

index.html — full streaming portal skeleton
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>StreamSite</title>
  <script src="config.js"></script>  <!-- TMDB_KEY, TMDB_BASE, IMG_BASE -->
</head>
<body>

  <input id="q" placeholder="Search movies..." oninput="search(this.value)">
  <div id="results"></div>

  <!-- Modal for fullscreen player -->
  <div id="modal" style="display:none;position:fixed;inset:0;background:#000;z-index:999">
    <button onclick="closeModal()" style="position:absolute;top:12px;right:16px;z-index:1000;..."></button>
    <iframe id="player" style="width:100%;height:100%;border:none"
      allowfullscreen
      sandbox="allow-scripts allow-same-origin allow-pointer-lock allow-forms"
    ></iframe>
  </div>

  <script>
    let timer;

    // Debounced search
    function search(q) {
      clearTimeout(timer);
      if (q.trim().length < 2) return;
      timer = setTimeout(async () => {
        const url  = `${TMDB_BASE}/search/movie?api_key=${TMDB_KEY}&query=${encodeURIComponent(q)}`;
        const data = await (await fetch(url)).json();
        renderResults(data.results);
      }, 400);
    }

    function renderResults(movies) {
      document.getElementById('results').innerHTML =
        movies.map(m => `
          <div class="card" onclick="openMovie(${m.id})">
            <img src="${IMG_BASE}${m.poster_path}" loading="lazy" alt="${m.title}">
            <h3>${m.title}</h3>
            <p>${m.release_date?.slice(0,4) || ''} · ⭐ ${m.vote_average?.toFixed(1)}</p>
          </div>
        `).join('');
    }

    function openMovie(id) {
      const src = `https://vidcore.net/movie/${id}?autoPlay=true`;
      document.getElementById('player').src = src;
      document.getElementById('modal').style.display = 'block';
    }

    function closeModal() {
      document.getElementById('player').src = '';  // stop playback
      document.getElementById('modal').style.display = 'none';
    }

    // Load trending movies on page load
    (async () => {
      const url  = `${TMDB_BASE}/trending/movie/week?api_key=${TMDB_KEY}`;
      const data = await (await fetch(url)).json();
      renderResults(data.results);
    })();
  </script>
</body>
</html>
Always clear the iframe src on close Setting player.src = '' when closing the modal stops audio/video from playing in the background. If you just hide the modal with CSS without clearing the src, the video keeps playing.

To support TV shows alongside movies, add a type toggle (Movies / TV) to your search UI and switch between /search/movie and /search/tv. For TV results, open a season/episode picker that calls /tv/{id}/season/{n} before building the VidCore URL.

Rate limiting TMDB allows 40 requests per 10 seconds per IP. Debounce your search input (the code above uses 400 ms) and cache results in a JS Map to avoid hitting the limit on repeat searches.

08

Next Steps

You now have a fully functional streaming portal. Here's where to go from here:

Add background music

Let users play music while they browse. The Music guide shows how to add a persistent audio player with zero server cost.

Music Guide

Add a virtual browser tab

Embed a Hyperbeam virtual machine so users can browse the full web right from your site — the ultimate streaming companion.

VMs Guide

Deploy to Cloudflare

Push your site live with DDoS protection, CDN caching, and analytics all configured in under 15 minutes.

Deploy Guide

Add user accounts

Let users save watchlists and continue where they left off. The Accounts guide covers the full auth + storage setup.

Accounts Guide

Ready to go live?

The Deploy guide walks you through Cloudflare Pages, WAF protection, and analytics — ship your streaming site in under 15 minutes.