π§ NOW SHOWING IN A TERMINAL NEAR YOU π§
ποΈ Get Tickets β’
πΏ Showtimes β’
π¬ Cast & Crew β’
π½οΈ Director's Cut
tmdbfusion isn't just another API wrapper options it's a cinematic experience for your codebase. Built for the modern Python auteur, it fuses high-performance async capabilities with strict type safety, delivering data faster than a summer blockbuster opening weekend.
The Movie Database (TMDB) is the world's premier source for movie and TV metadata. But interacting with raw HTTP endpoints is like watching a movie without sound you get the picture, but you miss the soul.
tmdbfusion changes the script. It encapsulates the complexity of API pagination, rate limiting, and session management into a sleek, elegant interface. Whether you're building a simple CLI tool or a massive streaming platform, this library provides the special effects you need to dazzle your users.
Every frame of this library was crafted with intent:
- Dual Audio Tracks: Full support for both Synchronous and Asynchronous (asyncio) runtimes.
- High Frame Rate: Powered by
httpxandmsgspecfor lightning-fast serialization. - IMAX Certified: Fully type-hinted data models (no more cryptic dictionaries).
- Director's Control: Precise handling of rate limits and retries.
"If
requestsis the silent era,tmdbfusionis Dolby Atmos." A Developer, probably.
Before you step onto the red carpet, ensure your trailer is equipped with the following:
| Component | Rating | Notes |
|---|---|---|
| Python | 3.13+ | Only the latest tech for this production. |
| httpx | 0.24+ | The engine behind the scenes. |
| msgspec | 0.18+ | For high-speed parsing stunts. |
Grab your front-row seat using the standard package manager.
# Standard Admission
pip install tmdbfusion
# VIP Access (Dev Dependencies)
pip install "tmdbfusion[dev]"You cannot enter the theater without a ticket. Obtain your API Key from TMDB Settings.
Method A: Environment Variable (Recommended) Export your key to keep it off-camera.
export TMDB_API_KEY="your_api_key_here"Method B: Direct Pass (Not Recommended for Production) Pass it during client initialization (see Showtimes below).
If you want to contribute to the sequel, set up the studio lot:
# Clone the reel
git clone https://github.com/xsyncio/tmdbfusion.git
cd tmdbfusion
# Install hatch (The Producer)
pip install hatch
# Run the test screening
hatch run testThe crew uses a specialized toolset to ensure the production quality remains high.
| Role | Tool | Command | Description |
|---|---|---|---|
| Stunt Coordinator | Nox | nox |
Runs the full test suite across multiple python versions. |
| Script Supervisor | Ruff | ruff check . |
Enforces flake8, isort, and other linting rules. |
| Cinematographer | Ruff Format | ruff format . |
Auto-formats code to the project style. |
| Safety Inspector | Mypy | mypy . |
Strict static type checking to prevent runtime crashes. |
| Script Doctor | Numpydoc | nox -s lint |
Validates strict adherence to NumPy docstring standards. |
| Set Designer | Deptry | deptry . |
Checks for unused or missing dependencies. |
| Security Guard | Pip-Audit | pip-audit |
Scans dependencies for known vulnerabilities. |
| Distributor | Bump My Version | bump-my-version show |
Manages versioning and release tagging. |
| Editor | Towncrier | towncrier |
Compiles the changelog from fragment files. |
| Quality Control | Pre-commit | pre-commit run --all-files |
Runs all checks before you can commit. |
Pro Tip: Install the git hooks to auto-run safety checks:
pre-commit install
Grab your popcorn. Here are three feature presentations to get you started.
For when you want to enjoy the film one frame at a time.
This example demonstrates the standard synchronous workflow: initializing the client, fetching a movie by ID, and accessing its data model attributes. Note the use of dot-notation for attributes (movie.title) thanks to our type-safe models.
from tmdbfusion import TMDBClient
from tmdbfusion.exceptions import TMDBError
def main():
# π¬ ACTION! Initialize the client
# Pro Tip: Pass api_key=None to read from TMDB_API_KEY env var
with TMDBClient("your_api_key_here") as client:
try:
# Fetch "Fight Club" (ID: 550)
# The director calls for a close-up on the details
movie = client.movies.details(550)
print(f"Title: {movie.title}")
print(f"Tagline: {movie.tagline}")
print(f"Budget: ${movie.budget:,.2f}")
except TMDBError as e:
# π¬ CUT! Something went wrong on set.
print(f"Scene failed: {e}")
if __name__ == "__main__":
main()For when you need speed. Lots of speed.
This example showcases the power of asyncio. We use the AsyncTMDBClient to fetch the first pages of "Popular", "Top Rated", and "Now Playing" movies simultaneously. This is the IMAX way to use the library.
import asyncio
from tmdbfusion import AsyncTMDBClient
async def main():
async with AsyncTMDBClient("your_api_key_here") as client:
# Schedule concurrent filming units
# We fetch three different lists at the exact same time
tasks = [
client.movies.popular(),
client.movies.top_rated(),
client.movies.now_playing()
]
# π¬ ACTION! Execute all tasks
results = await asyncio.gather(*tasks)
popular, top_rated, now_playing = results
print(f"Popular: {popular.results[0].title}")
print(f"Top Rated: {top_rated.results[0].title}")
print(f"Now Playing: {now_playing.results[0].title}")
if __name__ == "__main__":
asyncio.run(main())Binge-watch the entire series without lifting a finger.
Manually handling page numbers is so 1999. Use the built-in paginate helper to stream results lazily. This iterator handles the page requests for you behind the scenes.
from tmdbfusion import TMDBClient
def main():
with TMDBClient("your_api_key") as client:
# Create an iterator for popular movies
# We start rolling and don't stop strictly at page 1
iterator = client.paginate(client.movies.popular)
print("--- Top 20 Movies ---")
# take(20) ensures we only consume what we need
for movie in iterator.take(20):
print(f"β {movie.vote_average} | {movie.title}")
if __name__ == "__main__":
main()Import: from tmdbfusion import TMDBClient, AsyncTMDBClient
The Client is the auteur of your application. It manages the connection, handles authentication, and directs the scene.
def __init__(
self,
api_key: str,
*,
access_token: str | None = None,
language: str = "en-US",
timeout: float = 30.0,
retries: int = 3,
backoff_factor: float = 0.5,
max_connections: int = 100,
max_keepalive_connections: int = 20,
cache_ttl: float | None = None,
log_hook: Callable[[str, str, int], None] | None = None,
)π Production Notes (Parameters)
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key |
str |
Required | Your TMDB API Key (v3). |
access_token |
str |
None |
Your API Read Access Token (v4). Required for some v4 endpoints. |
language |
str |
"en-US" |
Default language for all requests (ISO 639-1). |
timeout |
float |
30.0 |
Connection timeout in seconds. Don't be late to the set. |
retries |
int |
3 |
Application-level retries for 5xx errors and 429s. |
backoff_factor |
float |
0.5 |
Exponential backoff multiplier. |
cache_ttl |
float |
None |
Time-to-live for local caching (seconds). If set, GET requests are cached. |
Signature: get(id_or_external: int | str, *, append: list[str] | None = None) -> object
Description: The "Magic Lens". Attempts to auto-detect the media type based on the ID format.
- If
int: AssumesMovie. - If
str(e.g.,tt12345): Performs afindfor IMDb ID. - If
str(e.g.,tv:123): Explicitly fetches TV show.
Signature: paginate(method, *, map_response=None, **kwargs) -> PaginatedIterator
Description: Creates a lazy iterator for any API method that accepts a page parameter.
Returns: PaginatedIterator (Sync) or AsyncPaginatedIterator (Async) with .take(n) and .skip(n) methods.
Signature: batch(concurrency: int = 10) -> BatchContext
Description: Creates a context manager to queue operations and execute them with controlled concurrency.
Usage:
async with client.batch(concurrency=5) as batch:
for mid in [550, 551, 552]:
batch.add(client.movies.details(mid))
# Results available after exit
results = batch.resultsSignature: sync_config() -> None
Description: Fetches the latest API configuration from TMDB and configures the images utility with base URLs.
Signature: close() -> None
Description: Cuts the wrap. Closes the underlying HTTP transport.
Class: MoviesAPI / AsyncMoviesAPI
Purpose: The headliner. Interfaces with the standard Movie endpoints.
| Method | Description |
|---|---|
details |
Get the primary details of a movie. |
popular |
Get a list of the current popular movies. |
top_rated |
Get the top rated movies on TMDB. |
now_playing |
Get a list of movies in theatres. |
upcoming |
Get a list of movies coming soon. |
credits |
Get the cast and crew for a movie. |
recommendations |
Get a list of recommended movies for a movie. |
similar |
Get a list of similar movies. |
images |
Get the images that belong to a movie. |
videos |
Get the videos that have been added to a movie. |
watch_providers |
Get the list of streaming providers. |
π Method Details: Movies
-
Signature:
details(movie_id: int, *, append_to_response: str | None = None) -> MovieDetails -
Sync/Async: Both.
-
Parameters:
movie_id(int): The ID of the movie.append_to_response(str): Comma separated list of extra requests (e.g., "credits,images").
-
Returns:
MovieDetailsmodel. -
Usage:
movie = client.movies.details(550, append_to_response="credits")
-
Signature:
popular(*, page: int = 1) -> MoviePaginatedResponse -
Sync/Async: Both.
-
Parameters:
page(int): Page number (default: 1).
-
Returns:
MoviePaginatedResponsecontaining list ofMovie. -
Usage:
popular = client.movies.popular(page=2)
- Signature:
top_rated(*, page: int = 1) -> MoviePaginatedResponse - Sync/Async: Both.
- Parameters:
page(int). - Returns:
MoviePaginatedResponse.
-
Signature:
credits(movie_id: int) -> Credits -
Sync/Async: Both.
-
Parameters:
movie_id(int). -
Returns:
Creditsmodel (containscastandcrewlists). -
Usage:
credits = client.movies.credits(550) print(credits.cast[0].name)
- Signature:
images(movie_id: int, *, include_image_language: str | None = None) -> ImagesResponse - Sync/Async: Both.
- Parameters:
movie_id(int).include_image_language(str): Filter by language (e.g., "en,null").
- Returns:
ImagesResponse(backdrops, posters, logos).
(All other methods follow similar standard signatures)
Class: TVAPI / AsyncTVAPI
Purpose: The binge-worthy content. Interfaces with TV Show endpoints.
| Method | Description |
|---|---|
details |
Get the primary TV show details. |
popular |
Get a list of the current popular TV shows. |
top_rated |
Get the top rated TV shows. |
on_the_air |
Get a list of shows that are currently on the air. |
airing_today |
Get a list of shows that are airing today. |
credits |
Get the cast and crew. |
external_ids |
Get the external Ids for a TV show. |
π Method Details: TV
- Signature:
details(tv_id: int, *, append_to_response: str | None = None) -> TVDetails - Sync/Async: Both.
- Parameters:
tv_id(int): The ID of the TV show.append_to_response(str).
- Returns:
TVDetailsmodel.
- Signature:
on_the_air(*, page: int = 1) -> TVPaginatedResponse - Sync/Async: Both.
- Parameters:
page(int). - Returns:
TVPaginatedResponse.
See client.tv_seasons for season-specific endpoints.
Class: PeopleAPI / AsyncPeopleAPI
Purpose: The talent. Interfaces with People endpoints.
| Method | Description |
|---|---|
details |
Get the primary person details. |
movie_credits |
Get the movie credits for a person. |
tv_credits |
Get the TV credits for a person. |
combined_credits |
Get the combined movie and TV credits. |
images |
Get the images for a person. |
popular |
Get the list of popular people. |
π Method Details: People
-
Signature:
details(person_id: int, *, append_to_response: str | None = None) -> PersonDetails -
Sync/Async: Both.
-
Parameters:
person_id(int). -
Returns:
PersonDetailsmodel. -
Usage:
brad = client.people.details(287)
- Signature:
combined_credits(person_id: int) -> CombinedCredits - Sync/Async: Both.
- Parameters:
person_id(int). - Returns:
CombinedCredits(cast/crew for both media types).
Class: SearchAPI / AsyncSearchAPI
Purpose: Find what you're looking for.
| Method | Description |
|---|---|
movie |
Search for movies. |
tv |
Search for TV shows. |
person |
Search for people. |
multi |
Search for movies, TV shows, and people in a single request. |
collection |
Search for collections. |
company |
Search for companies. |
keyword |
Search for keywords. |
π Method Details: Search
-
Signature:
movie(query: str, *, page: int = 1, year: int | None = None, ...) -> MoviePaginatedResponse -
Parameters:
query(str),page,year,primary_release_year,include_adult. -
Usage:
results = client.search.movie("The Matrix", year=1999)
- Signature:
multi(query: str, *, page: int = 1) -> MultiPaginatedResponse - Returns: Mixed list of
Movie,TV, andPersonobjects. Checkmedia_typeattribute.
Class: DiscoverAPI / AsyncDiscoverAPI
Purpose: The engine of discovery. Filter content by detailed criteria.
| Method | Description |
|---|---|
movie |
Discover movies by specified criteria. |
tv |
Discover TV shows by specified criteria. |
π Method Details: Discover
-
Signature:
movie(**kwargs) -> MoviePaginatedResponse -
Parameters: Accepts all standard TMDB discover parameters as kwargs (
with_genres,sort_by,primary_release_year, etc.). -
Usage:
# Find Action movies from 2023 sorted by revenue discoveries = client.discover.movie( with_genres=28, primary_release_year=2023, sort_by="revenue.desc" )
Class: TrendingAPI / AsyncTrendingAPI
Purpose: What's hot right now.
| Method | Description |
|---|---|
movie |
Get trending movies. |
tv |
Get trending TV shows. |
person |
Get trending people. |
all |
Get trending content of all types. |
π Method Details: Trending
-
Signature:
all(time_window: str = "day") -> MultiPaginatedResponse -
Parameters:
time_window: "day" or "week". -
Usage:
trending = client.trending.all("week")
Class: FindAPI / AsyncFindAPI
Purpose: Locate TMDB objects by external IDs (IMDb, TVDB, Facebook, Instagram, Twitter).
- Signature:
by_id(external_id: str, *, external_source: str) -> FindResponse - Parameters:
external_id: The ID from the external source.external_source: "imdb_id", "tvdb_id", etc.
- Returns:
FindResponsecontainingmovie_results,tv_results, etc.
Class: GenresAPI / AsyncGenresAPI
Purpose: Get the list of official genres.
| Method | Description |
|---|---|
movie_list |
Get the list of movie genres. |
tv_list |
Get the list of TV genres. |
Class: CollectionsAPI / AsyncCollectionsAPI
Purpose: Get collection details (e.g., "The Avengers Collection").
- Signature:
details(collection_id: int) -> CollectionDetails
Class: CompaniesAPI / AsyncCompaniesAPI
Purpose: Get production company details.
- Signature:
details(company_id: int) -> CompanyDetails
Class: KeywordsAPI / AsyncKeywordsAPI
Purpose: Get keyword details.
- Signature:
details(keyword_id: int) -> Keyword
Class: NetworksAPI / AsyncNetworksAPI
Purpose: Get TV network details.
- Signature:
details(network_id: int) -> Network
Class: ReviewsAPI / AsyncReviewsAPI
Purpose: Get full review details.
- Signature:
details(review_id: str) -> Review - Note:
review_idis a string (UUID).
Class: WatchProvidersAPI / AsyncWatchProvidersAPI
Purpose: Get available watch providers and regions.
| Method | Description |
|---|---|
movie_providers |
Get available movie providers. |
tv_providers |
Get available TV providers. |
regions |
Get available regions. |
π Method Details: Watch Providers
- Signature:
movie_providers(*, watch_region: str = "US") -> WatchProvidersList - Parameters:
watch_region: ISO 3166-1 code.
Class: AccountAPI / AsyncAccountAPI
Purpose: Manage user account data (V3).
Requirement: Most methods require a session_id.
| Method | Description |
|---|---|
details |
Get your account details. |
favorite_movies |
Get your favorite movies. |
favorite_tv |
Get your favorite TV shows. |
add_favorite |
Mark a movie or TV show as favorite. |
rated_movies |
Get movies you have rated. |
rated_tv |
Get TV shows you have rated. |
rated_episodes |
Get TV episodes you have rated. |
watchlist_movies |
Get movies on your watchlist. |
watchlist_tv |
Get TV shows on your watchlist. |
add_to_watchlist |
Add a movie or TV show to your watchlist. |
lists |
Get lists you have created. |
π Method Details: Account (V3)
-
Signature:
details(*, session_id: str) -> AccountDetails -
Parameters:
session_id(str). -
Usage:
details = client.account.details(session_id="your_session_id") print(f"Logged in as {details.username}")
-
Signature:
add_favorite(account_id: int, *, session_id: str, media_type: str, media_id: int, favorite: bool) -> StatusResponse -
Parameters:
account_id(int): Your account ID.media_type(str): "movie" or "tv".favorite(bool):Trueto add,Falseto remove.
-
Usage:
client.account.add_favorite( account_id=123, session_id="sess_id", media_type="movie", media_id=550, favorite=True )
Class: AuthenticationAPI / AsyncAuthenticationAPI
Purpose: Create Sessions and Guest Sessions.
| Method | Description |
|---|---|
create_request_token |
Step 1: Create a temporary request token. |
create_session_with_login |
Step 2 (Optional): Validate token with username/password. |
create_session |
Step 3: Create a full session ID. |
create_guest_session |
Create a temporary guest session. |
delete_session |
Logout/Delete a session. |
π Method Details: Authentication
- Returns:
RequestToken(containsrequest_tokenstring). - Note: Redirect user to
https://www.themoviedb.org/authenticate/{request_token}for approval if not using direct login.
- Signature:
create_session(*, request_token: str) -> Session - Parameters:
request_token(approved). - Returns:
Session(containssession_id).
Class: GuestSessionAPI / AsyncGuestSessionAPI
Purpose: Fetch rated items for a guest session.
| Method | Description |
|---|---|
rated_movies |
Get rated movies. |
rated_tv |
Get rated TV shows. |
rated_episodes |
Get rated episodes. |
Class: AccountV4API / AsyncAccountV4API
Purpose: Manage user account data using V4 Access Tokens.
Requirement: Use access_token in Client constructor.
| Method | Description |
|---|---|
lists |
Get lists. |
favorite_movies |
Get favorite movies. |
favorite_tv |
Get favorite TV shows. |
rated_movies |
Get rated movies. |
rated_tv |
Get rated TV shows. |
movie_recommendations |
Get movie recommendations. |
tv_recommendations |
Get TV recommendations. |
movie_watchlist |
Get movie watchlist. |
tv_watchlist |
Get TV watchlist. |
Class: AuthV4API / AsyncAuthV4API
Purpose: Manage V4 Request Tokens and Access Tokens.
| Method | Description |
|---|---|
create_request_token |
Create a V4 request token. |
create_access_token |
Create a V4 access token. |
delete_access_token |
Delete an access token. |
Class: ListsV4API / AsyncListsV4API
Purpose: Manage V4 Lists.
| Method | Description |
|---|---|
details |
Get list details. |
create |
Create a list. |
update |
Update a list. |
delete |
Delete a list. |
add_items |
Add items to list. |
remove_items |
Remove items from list. |
check_item_status |
Check if item is in list. |
Class: ListsAPI / AsyncListsAPI
Purpose: Get details for public V3 lists.
- Signature:
details(list_id: int | str) -> ListDetails
Class: ChangesAPI / AsyncChangesAPI
Purpose: Get listing of ID changes.
| Method | Description |
|---|---|
movie |
Get movie change list. |
tv |
Get TV change list. |
person |
Get person change list. |
Class: CertificationsAPI / AsyncCertificationsAPI
Purpose: Get supported certifications.
| Method | Description |
|---|---|
movie_list |
Get movie certifications. |
tv_list |
Get TV certifications. |
For the power users who want to go behind the scenes and tweak the lighting.
The paginate method is a high-level abstraction, but under the hood, it's doing heavy lifting.
# Create an async iterator for popular movies
iterator = client.paginate(client.movies.popular)
# Take 100 items, but skip the first 20
# This automatically handles page boundaries.
# If page 1 has 20 items, it skips page 1 entirely and starts fetching from page 2.
subset = iterator.skip(20).take(100)
async for movie in subset:
print(movie.title)You can inject your own logging hooks or adjust the retry logic.
def my_log_hook(method, url, status):
print(f"π₯ [CUT] {method} {url} -> {status}")
client = TMDBClient(
"api_key",
retries=5, # More persistence
backoff_factor=1.0, # Slower backoff
cache_ttl=3600, # Cache GET requests for 1 hour
log_hook=my_log_hook
)When you need to fetch data for a grid view (e.g., 20 movies at once), sequential requests are too slow. Use batch().
async with client.batch(concurrency=10) as batch:
for movie_id in top_20_ids:
# These are queued, not awaited immediately
batch.add(client.movies.details(movie_id))
# All results return in the same order they were added
movies = batch.resultsThe images utility helps you construct full URLs for posters and backdrops using the current configuration.
# Ensure config is loaded
client.sync_config()
poster_path = movie.poster_path # e.g., "/abc.jpg"
url = client.images.get_url(poster_path, "poster", "w500")
print(url)
# Output: https://image.tmdb.org/t/p/w500/abc.jpgWe chose msgspec over pydantic for one reason: Speed. When deserializing thousands of movie objects for a data science pipeline or a high-traffic web app, msgspec offers significant performance gains. Every model is a frozen=True Struct, ensuring immutability and thread safety.
Rather than using a single client with asyncio.run() hacks for sync usage, we provide two distinct implementations (TMDBClient and AsyncTMDBClient) that share the same logical structure but use different transport layers (httpx.Client vs httpx.AsyncClient). This ensures that synchronous code is truly blocking and asynchronous code is truly non-blocking, without overhead.
We employ Strict mode in our mypy configuration. Optional types are explicit. There are no loose dictionaries returned from the API; everything is validated against a schema. If the API changes and returns an unknown field, we ignore it by default to prevent crashing, but missing required fields will raise validation errors, alerting you to contract breaches immediately.
Distributed under the MIT License. See LICENSE for more information.