GuidesMOVIE APIS
Free movie API for a React app
We tested 10 movie and TV APIs from Chromium, Firefox and WebKit on October 26, 2026. Six send Access-Control-Allow-Origin: *, so a React app can call them with fetch() and no proxy. Start with TMDB, free for non-commercial use with attribution, and keep its token in a server route; or use TVmaze, which needs no key at all. IMDb has no free official API.
Which free movie API to use
Use TMDB for movies, TV, people and posters. Use OMDb for quick lookups by IMDb ID, TVmaze for TV shows without a key, and Watchmode to find where a title streams. Wikidata maps IDs between these services, and AniList covers anime. For other categories, see 70 free public APIs tested for CORS.
| API | Key | CORS from the browser | Free limit | Data |
|---|---|---|---|---|
| TMDB | Free key or read token | Yes, any origin | About 40 requests per second, non-commercial | Movies, TV, people, images |
| OMDb | Free key by email | Yes, any origin | 1,000 requests per day | Movies and series by IMDb ID or title |
| TVmaze | None | Yes, any origin | 20 calls per 10 seconds per IP | TV shows, episodes, cast, schedules |
| Trakt | Client ID header | Origins you register | 1,000 GET calls per 5 minutes | Trending, lists, watch history, IDs |
| Watchmode | Free key | Yes, any origin | 2,500 credits per month, non-commercial | Streaming availability by country |
| IMDb API | Paid license | Not public | No free tier, sold on AWS Data Exchange | Ratings, metadata, box office |
| IMDb datasets | None | No | Daily TSV files, personal use only | Titles, ratings, people |
| IMDb suggestion endpoint | None | localhost and imdb.com only | Undocumented | Title autocomplete with posters |
| Wikidata SPARQL | None | Yes, any origin | 60 seconds per query, 5 parallel per IP | IDs across sites, dates, cast, CC0 |
| AniList | None for public data | Yes, any origin, POST | 90 per minute, 30 while degraded | Anime and manga |
| Jikan | None | Unreachable on October 26, 2026 | 60 per minute, 3 per second | MyAnimeList data, unofficial |
TVmaze in React with no key
TVmaze needs no key and says "All endpoints are CORS enabled." It covers TV only: shows, episodes, cast and schedules. The limit is at least 20 calls every 10 seconds per IP, then 429. The data is CC BY-SA, so credit TVmaze with a link back; hotlinking its images is allowed.
// A React component: TVmaze needs no key and sends CORS headers
import { useEffect, useState } from 'react';
export function ShowSearch() {
const [query, setQuery] = useState('severance');
const [shows, setShows] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
if (query.trim().length < 2) return;
const controller = new AbortController();
// Wait for a 300 ms pause in typing, then cancel any older request
const timer = setTimeout(async () => {
try {
const url = 'https://api.tvmaze.com/search/shows?q=' + encodeURIComponent(query);
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error(`TVmaze answered ${response.status}`);
const results = await response.json();
setShows(results.map((result) => result.show));
setError(null);
} catch (err) {
if (err.name !== 'AbortError') setError(err.message);
}
}, 300);
return () => {
clearTimeout(timer);
controller.abort();
};
}, [query]);
return (
<div>
<input value={query} onChange={(event) => setQuery(event.target.value)} />
{error && <p role="alert">{error}</p>}
<ul>
{shows.map((show) => (
<li key={show.id}>
{show.image && <img src={show.image.medium} alt="" width="70" />}
<a href={show.url}>{show.name}</a> {show.premiered?.slice(0, 4)}
</li>
))}
</ul>
</div>
);
}/search/shows?q=severance returned 4,097 bytes. /singlesearch/shows?q=severance&embed=episodes returned the show plus every episode in one 14,572-byte response, so use embed instead of a second request. TVmaze caches responses for 60 minutes.
TMDB: CORS works, the token is the problem
TMDB sends Access-Control-Allow-Origin: *, even on 401 errors, and its preflight allows the Authorization header. The browser is not the blocker. The token is: a VITE_ or REACT_APP_ variable is compiled into your bundle, and anyone can read it in DevTools. Keep the token in a Worker or API route and let React call that (why and how).
TMDB accepts the v3 api_key query parameter or the API Read Access Token as Authorization: Bearer; both give the same access. A public CORS proxy is the wrong fix: TMDB needs no CORS help, and the free cors.dev tier refuses api_key= in a URL (credential_in_url) and Authorization headers (header_not_allowed) by design.
// A Cloudflare Worker: the token lives in env.TMDB_TOKEN, never in your React bundle
export default {
async fetch(request, env) {
const query = new URL(request.url).searchParams.get('query') ?? '';
const url = 'https://api.themoviedb.org/3/search/movie?include_adult=false&query=' + encodeURIComponent(query);
const upstream = await fetch(url, {
headers: { Authorization: `Bearer ${env.TMDB_TOKEN}`, Accept: 'application/json' },
});
const data = await upstream.json();
const headers = { 'Access-Control-Allow-Origin': 'https://your-app.example' };
if (!upstream.ok) {
return Response.json({ error: data.status_message }, { status: upstream.status, headers });
}
const movies = data.results.map((movie) => ({
id: movie.id,
title: movie.title,
year: movie.release_date?.slice(0, 4) ?? '',
poster: movie.poster_path ? `https://image.tmdb.org/t/p/w342${movie.poster_path}` : null,
}));
return Response.json(movies, { headers });
},
};Read the TMDB FAQ before launch. Non-commercial use is free with attribution: the TMDB logo plus "This product uses the TMDB API but is not endorsed or certified by TMDB." in your About or Credits section. Driving traffic or ad revenue counts as commercial and needs an agreement. The terms forbid caching content for more than 6 months and using it to train AI models. The old limit of 40 requests per 10 seconds was removed on December 16, 2019; the ceiling now sits around 40 requests per second.
OMDb, Watchmode and Trakt
OMDb emails a free key good for 1,000 requests per day. Look up by IMDb ID (i) or title (t), or search with s and page 1 to 100. It sends CORS headers, but the key travels in the URL (apikey=), so anyone can see it in the network tab. Data is CC BY-NC 4.0, and the Poster API (over 280,000 posters) is for Patreon backers only. TMDB and OMDb both still accept a JSONP callback parameter, which you don't need here (JSONP vs CORS).
Watchmode answers "where can I stream this". The free plan gives 2,500 credits per month for non-commercial use with attribution, sources in up to 3 countries, no card; some endpoints cost more than 1 credit, and the next plan is $349 per month. The key goes in the apiKey query parameter.
Trakt wants three headers: trakt-api-key (your client ID), trakt-api-version: 2 and Content-Type: application/json. It returns CORS headers for the JavaScript origins you list when you create the API app, so register both your dev and production origins. The limit is 1,000 GET calls every 5 minutes.
IMDb has no free API
The official IMDb API is a paid license sold only through AWS Data Exchange. The free non-commercial datasets are gzipped TSV files, refreshed daily, for personal use; title.basics.tsv.gz alone is 229 MB and has no CORS headers. Import them into a database, not fetch().
The undocumented suggestion endpoint, https://v3.sg.media-imdb.com/suggestion/x/QUERY.json, returns a d array with ID, title, year, type, top cast and poster URL. It is a trap: it sends Access-Control-Allow-Origin only to http://localhost and imdb.com origins. In our test the same fetch() worked from http://localhost and failed from http://127.0.0.1 in Chromium, Firefox and WebKit. Netlify, GitHub Pages and Vercel origins get no header either, so it works on your machine and breaks after deploy.
Through https://proxy.cors.dev/ it returned 200 from every origin in all three browsers. It stays undocumented and can change, and IMDb's Conditions of Use forbid robots and scraping without written consent, so keep it to prototypes.
// Runs in a module script or the DevTools console
const proxy = 'https://proxy.cors.dev/';
async function imdbSuggest(text) {
const term = encodeURIComponent(text.trim().toLowerCase());
const url = `https://v3.sg.media-imdb.com/suggestion/x/${term}.json`;
const response = await fetch(proxy + url, { credentials: 'omit' });
if (!response.ok) {
const reason = response.headers.get('X-Cors-Error') ?? 'upstream error';
throw new Error(`Suggestions failed: ${response.status} ${reason}`);
}
const { d = [] } = await response.json();
return d
.filter((item) => item.id.startsWith('tt'))
.map((item) => ({ id: item.id, title: item.l, year: item.y, type: item.q, cast: item.s }));
}
console.table(await imdbSuggest('dune part two'));Wikidata and anime APIs
Wikidata's SPARQL endpoint (query.wikidata.org/sparql) needs no key, sends Access-Control-Allow-Origin: * and its data is CC0. Use it as an ID bridge: P345 is the IMDb ID, P4947 the TMDB movie ID, P577 the release date. IMDb tt1160419 maps to Dune, released 2021-09-03, TMDB ID 438631. Each query has a 60-second deadline, and each client gets 60 seconds of processing per minute and 5 parallel queries per IP. It requires a User-Agent: browsers send one, but Node's default got a 403.
AniList serves anime and manga over GraphQL with no key and CORS for any origin. Send a POST straight from React; a GET returns 404, so the GET-only free proxy cannot help, and does not need to. Jikan, the unofficial MyAnimeList API, documents 60 requests per minute and 3 per second, but on October 26, 2026 it timed out from a datacenter IP, failed from a US residential IP and returned 504 through cors.dev.
Limits to plan for
- TMDB: non-commercial only, logo and notice required, no caching past 6 months, about 40 requests per second.
- OMDb: 1,000 requests per day per free key, CC BY-NC 4.0.
- TVmaze: 20 calls per 10 seconds per IP, link back required.
- Wikidata: 60 seconds per query, 30 error queries per minute, then 429 with
Retry-After. - AniList: 90 requests per minute normally, 30 while the API is degraded, as it was on October 26, 2026.
- cors.dev free tier, needed here only for the IMDb suggestion endpoint: 600 requests per minute per IP, 1 MiB per response, 10-second deadline, no keys in URLs.
Good questions.
Is there a free movie API with no key?
For TV, TVmaze needs no key and sends CORS headers on every endpoint. For movies, Wikidata needs no key; full catalogs like TMDB and OMDb need a free registration key.
Can I call the TMDB API from React without a backend?
Technically yes: TMDB sends Access-Control-Allow-Origin: * and allows the Authorization header. But a VITE_ or REACT_APP_ token ships in your public bundle, so put it behind a Worker or API route for anything public.
Does IMDb have a free API?
No. The official IMDb API is a paid license on AWS Data Exchange. The free datasets are daily TSV dumps for non-commercial use, 229 MB for titles alone, with no CORS headers.
Why does my IMDb fetch work on localhost but fail after deploy?
The undocumented v3.sg.media-imdb.com suggestion endpoint sends Access-Control-Allow-Origin only to http://localhost and imdb.com origins. Your deployed origin gets no header, so the browser blocks the response; prefix the URL with https://proxy.cors.dev/ or call it from your server.
How many requests does the OMDb free key allow?
1,000 requests per day. More requests, and the Poster API, need a Patreon tier.
Does Letterboxd have a public API?
No open one; access is by request. The Letterboxd API guide covers the RSS and page-data route.