tvlogo.monster — Python client
Python client for tvlogo.monster, a free, non-profit, open-source
database and API of TV channel logos (Canada first). One key in, one logo out: give it an
iptv-org tvg-id, an epg.monster channel id, a
Canadian call sign or a channel name, and get back a ready-to-use PNG URL on i.tvlogo.monster
(64, 128, 256 or 512 px, transparent square canvas). No API key, no account.
pip install tvlogo.monster
The distribution is called tvlogo.monster (pip also accepts tvlogo-monster); the import name is tvlogo.
Python 3.9+, one dependency (httpx), fully typed (py.typed).
Quickstart
This is the same code the website's code panel generates for the default key:
from tvlogo import Client
# 1. one client per program (reuses the HTTPS connection, no API key needed)
client = Client()
# 2. resolve one key: tvg-id, epg.monster id, call sign or channel name
logo = client.resolve(
"CTV.ca@CKYDT",
mode="national", # network logo for every affiliate (e.g. all CTV stations get the CTV logo)
)
# 3. None = no logo for this key (429 rate limits are retried after Retry-After)
if logo is None:
raise SystemExit("no logo for this key")
# 4. use the image URL, e.g. as tvg-logo="..." in an M3U playlist
print(logo.image_url) # https://i.tvlogo.monster/tZtP9.png
# which level answered: station, brand, network or channel
print(logo.resolved_level) # network
# True when a fallback was used (e.g. no dark variant -> default)
print(logo.fallback, logo.theme_served)
More:
logo = client.resolve("CKY-DT", namespace="call_sign", theme="dark", size=128)
logo.url(256) # same image, other size
client.download("CBCTelevision.ca", "logos/") # saves logos/<id>.png, returns the Path
client.search("Citytv", limit=5) # [SearchResult(name=..., id=..., image_url=...), ...]
client.stats() # catalogue counts
client.params() # allowed values and defaults, straight from the API
from tvlogo import image_url, parse_tvg_id
image_url("tZtP9", 64) # https://i.tvlogo.monster/tZtP9-64.png (offline)
parse_tvg_id("CTV.ca@CKYDT") # TvgId(channel='CTV.ca', feed='CKYDT') (offline)
Async:
import asyncio
from tvlogo import AsyncClient
async def main():
async with AsyncClient() as client:
logo = await client.resolve("CTV.ca@CKYDT")
print(logo.image_url if logo else "no logo")
asyncio.run(main())
Options
client.resolve(key, *, namespace, mode, theme, era, size) — anything left out uses the API default.
| Option | Values (default first) | Meaning |
|---|---|---|
key |
string, max 200 chars | tvg-id (CTV.ca@CKYDT), epg.monster id (CTV.Winnipeg.ca), call sign (CKY-DT) or channel name (CTV Winnipeg) |
namespace |
auto, iptv-org, epg.monster, call_sign, name |
Which id system the key belongs to. auto tries tvg-id, epg.monster id, call sign, then name. key_type= is accepted as an alias, as are tvg-id, epg, callsign. |
mode |
local, national |
See below |
theme |
default, dark, light, mono |
Preferred variant (monochrome accepted). Falls back to default when the variant does not exist. |
era |
current, historic |
historic = an older design when known, else current (new/old accepted). |
size |
512, 256, 128, 64 |
Square PNG size in px |
Client(base_url="https://api.tvlogo.monster", timeout=10, *, max_retries=3, max_retry_wait=60, user_agent=None, http_client=None)
The returned Logo has image_url, public_id (5-character image id), resolved_level, fallback,
theme_served, era_served, name, country, namespace, matched_key, source, trademark_note, …
and raw (the full JSON). resolve() returns None when there is no logo; client.logo(key) is the same
but raises NotFound.
Modes and fallback
local(default): the station's own logo (e.g. CTV Winnipeg / CKY-DT). When there is none, it falls back level by level: station → brand → network → channel.national: the network logo for every affiliate (every CTV station gets the CTV logo).
resolved_level tells you which level answered. Themes fall back to default and eras to current;
fallback=True plus theme_served / era_served tell you when you got something other than what you asked for.
Rate limits and etiquette
The API is free and run by volunteers. Limits are per IP (token bucket: burst 60, about 2 lookups/s
sustained). Over the limit you get HTTP 429 with Retry-After; repeated violations lead to temporary blocks.
The client waits Retry-After and retries up to max_retries times (default 3), backs off on 502/503/504
and network errors, and raises RateLimited if it is still limited (or if the server asks for a wait longer
than max_retry_wait). Please:
- reuse one
Client, cache results (logos rarely change; responses are cacheable for 5 minutes), and - for bulk jobs (whole M3U playlists) go sequentially rather than in parallel, or put the image URLs straight into your playlist instead of re-resolving on every run.
Every request sends User-Agent: tvlogo-python/0.1.0 (+https://search.tvlogo.monster).
Errors
TvlogoError ← InvalidParameter (bad option, raised before any request; also a ValueError),
APIError (status_code, detail, payload) ← NotFound (404/410), RateLimited (retry_after).
Command line
$ tvlogo resolve CTV.ca@CKYDT --mode national
https://i.tvlogo.monster/tZtP9.png
$ tvlogo resolve CKY-DT --theme dark --size 128 --json
$ tvlogo search CBC --limit 5
$ tvlogo download CBCTelevision.ca logos/ --size 256
$ tvlogo url tZtP9 --size 64
$ tvlogo stats
(python -m tvlogo ... works too.) Exit code 1 means "no logo", 2 means an error.
Links
- Website and search: https://search.tvlogo.monster
- Documentation: https://search.tvlogo.monster/docs
- API reference (Swagger): https://api.tvlogo.monster/docs · OpenAPI: https://api.tvlogo.monster/openapi.json
- Prompt for AI assistants: https://search.tvlogo.monster/ai-prompt
- Legal: https://search.tvlogo.monster/legal
Legal
Channel logos are trademarks and/or copyright of their respective owners. They are shown for identification only; tvlogo.monster claims no ownership and is not affiliated with or endorsed by any broadcaster. Logo images are not licensed by the project — check with the owner before using them beyond identification. The client code is MIT licensed; the metadata and mappings returned by the API are CC BY 4.0. This package never bundles logo images.
Metadata
Release files for tvlogo.monster 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| tvlogo_monster-0.1.1.tar.gz | 14.8 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tvlogo_monster-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 30.3 kB
Release files / tvlogo_monster-0.1.1.tar.gz
| Download URL | tvlogo_monster-0.1.1.tar.gz |
|---|---|
| Size | 14.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
49676ef760883f976fbaf000a3e0382984af25dfd4bac528e48a6639e464e47d
|
|
BLAKE2b-256 checksum How to use checksums |
007c0f1db5f7a44d4bb7e57446c510a851d168a075620da258e414328354a147
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / tvlogo_monster-0.1.1-py3-none-any.whl
| Download URL | tvlogo_monster-0.1.1-py3-none-any.whl |
|---|---|
| Size | 15.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ec908a66c6854f9c5d31f1cbfa26b57b8cdb634c9c316dd584c9ce3394aabbac
|
|
BLAKE2b-256 checksum How to use checksums |
cdc95394f683635cc5075cc4b45f75416498c8bb0ff1e44903afb66c95b83a69
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Security for tvlogo.monster 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.