An unofficial Crunchyroll API for Python — usable as a library (a module in another project) and as a standalone CLI.
It lets you:
- Find series by name, TMDB ID, or IMDB ID (including metadata)
- Log in and access your watchlist, history, continue watching, and custom lists (Crunchylists)
- Select between profiles (multiprofile accounts)
- Browse the full catalog, upcoming simulcast seasons, and the release calendar
- Resolve episodes by season/episode number and read stream + subtitle metadata (for downloaders)
Unofficial and not affiliated with Crunchyroll LLC in any way. A premium account is recommended for full functionality. The Crunchyroll API is undocumented and changes occasionally — when it does, the values in
endpoints.pymay need adjusting.
📖 Full command and library reference: see GUIDE.md.
pip install -e .
# with development dependencies (pytest):
pip install -e ".[dev]"
# with encryption support (sealed credentials / encrypted token cache):
pip install -e ".[crypto]"Requires Python ≥ 3.9. Only runtime dependency: requests.
The library reads no .env file. Credentials are supplied by the calling program — either as constructor arguments or via a Config object. Where tokens are cached is up to the host program through a pluggable SessionStore.
from crunchyroll_api import CrunchyrollClient, CrunchyrollFinder, FileSessionStore
client = CrunchyrollClient(
email="you@email.com",
password="your-password",
locale="de-DE",
# optional: cache the login across runs
session_store=FileSessionStore("./.crunchyroll.session.json"),
)
client.login()
# --- Search by name ---
for series in client.search_series("Frieren"):
print(series.title, series.id, series.episode_count)
# --- Series details, seasons, episodes ---
series = client.get_series("GG5H5XQ0D")
seasons = client.get_seasons(series.id)
episodes = client.get_episodes(seasons[0].id)
# --- Select a profile ---
for p in client.list_profiles():
print(p.profile_id, p.profile_name, p.is_primary)
client.switch_profile("<profile_id>")
# --- Watchlist / custom lists / history ---
watchlist = client.get_watchlist()
custom_lists = client.list_custom_lists()
items = client.get_custom_list(custom_lists[0]["list_id"])
history = client.get_history()Crunchyroll itself has no TMDB/IMDB IDs. The bridge uses TMDB to fetch titles + aliases for an ID, searches Crunchyroll with them, and returns the best fuzzy match. A free TMDB API key is required.
from crunchyroll_api import CrunchyrollClient, CrunchyrollFinder
client = CrunchyrollClient(email="...", password="...", locale="de-DE",
tmdb_api_key="YOUR_TMDB_KEY")
client.login()
finder = CrunchyrollFinder(client)
match = finder.find_by_tmdb(209867, media_type="tv") # Frieren (TMDB tv)
if match:
print(match.score, match.matched_title, match.series.id)
match = finder.find_by_imdb("tt22248376")MatchResult contains the matched Series, a score (0–1), and the title that matched.
Implement SessionStore (load/save/clear) to keep tokens in, e.g., a database or keyring:
from crunchyroll_api import SessionStore, CrunchyrollClient
class MyStore(SessionStore):
def load(self): ...
def save(self, data): ...
def clear(self): ...
client = CrunchyrollClient(email="...", password="...", session_store=MyStore())The CLI takes credentials via flags or environment variables (CRUNCHYROLL_EMAIL, CRUNCHYROLL_PASSWORD, CRUNCHYROLL_LOCALE, TMDB_API_KEY, CRUNCHYROLL_SESSION_FILE).
# Search
crunchyroll-api --email a@b.de --password secret search "Frieren"
# With env vars + session cache
export CRUNCHYROLL_EMAIL=a@b.de CRUNCHYROLL_PASSWORD=secret CRUNCHYROLL_LOCALE=de-DE
export CRUNCHYROLL_SESSION_FILE=./.crunchyroll.session.json
crunchyroll-api search "Frieren" --type series --limit 10
crunchyroll-api seasons GG5H5XQ0D
crunchyroll-api episodes <season_id>
# TMDB / IMDB (TMDB key required)
crunchyroll-api --tmdb-key XYZ tmdb 209867 --media tv
crunchyroll-api --tmdb-key XYZ imdb tt22248376
# Account
crunchyroll-api profiles
crunchyroll-api watchlist --profile <profile_id>
crunchyroll-api continue
crunchyroll-api history
crunchyroll-api lists # all custom lists
crunchyroll-api lists --list-id <id> # contents of a list
# Machine-readable output
crunchyroll-api --json search "Frieren"src/crunchyroll_api/
__init__.py public API
client.py CrunchyrollClient: auth, session, profiles, content, lists, streams
config.py Config + pluggable SessionStore (memory/file/custom)
endpoints.py all API endpoints (adjust centrally)
models.py dataclasses: Series, Season, Episode, Movie, Profile, Stream, ...
tmdb.py TMDB client (titles/aliases from a TMDB/IMDB ID)
finder.py CrunchyrollFinder: name/TMDB/IMDB -> fuzzy match
crypto.py optional encryption (sealed credentials, encrypted store)
cli.py command-line interface
tests/ offline tests (mocked HTTP calls, no network)
pip install -e ".[dev]"
pytest -qThe tests run fully offline (mocked HTTP responses) and cover the auth flow, CMS signing, parsing, fuzzy matching, catalog pagination, episode resolution, and stream parsing.
- Premium: watchlist, history, and premium-only content require an active subscription.
- Cloudflare: Crunchyroll uses bot protection. On a block,
CloudflareBlockErroris raised — try a different user agent/IP. - TMDB matching is heuristic (title fuzzy match). For ambiguous titles the result may differ;
min_scoreandrank_by_name()help with tuning. - Video streams are DRM-protected and are not decrypted by this library; the focus is on metadata, subtitle tracks, and account lists.
- Full catalog:
client.get_all_series()/iter_all_series()or CLIall-seriesfetch all series (paginated). A single page:client.browse(...). - Batch search:
client.batch_search(["frieren", "one piece"])or CLIsearch "frieren,one piece"(comma-separated). - Upcoming anime:
client.list_simulcast_seasons()+client.get_season_anime("winter-2026")or CLIsimulcast-seasons/simulcast <id>; weekly schedule viacalendar. - Streams & subtitles:
client.get_streams(episode_id)(manifest, audio, subtitle tracks),download_subtitle()(.ass/.vtt),invalidate_stream(). Video stays DRM. - Episode resolution:
client.resolve_episode("Frieren", 1, 1)orfind_episode(series_id, season, episode)— ideal for downloaders (S/E numbering). - Discovery:
get_similar,list_categories,get_home_feed,get_news_feed,get_recommendations. - Robustness: retries + backoff and a GET cache (
Config(max_retries=, cache_ttl=)), anonymous loginlogin_anonymous(). - Encrypted credentials: pass a sealed blob + key instead of plaintext (
seal_credentials,CrunchyrollClient(sealed_credentials=, secret_key=)), plusEncryptedFileSessionStorefor an encrypted token cache. Needs the[crypto]extra. - JSON export: any CLI output with
--json(stdout) or-o file.json(file). In code: models aredataclasses, raw JSON under.raw.
crunchyroll-api all-series -o catalog.json
crunchyroll-api search "frieren,one piece,naruto" --jsonSee GUIDE.md for all commands and details.
Need just one file to embed in another project (e.g. an AniWorld downloader)?
python build_single_file.py # -> dist/crunchyroll_api.pyCopy the bundled file (only requests as a dependency, CLI not included) and use from crunchyroll_api import CrunchyrollClient. Details in GUIDE.md section 8.
GNU General Public License v3.0 or later (GPLv3+). See LICENSE.