71.8M listings
Every country's schedule under one key, one shape and one set of ids.
GET /v1/now
What every channel in a country is showing this minute, with the next programme after it.
MIT, no lock-in
Every lab tool reads only the public API with your own key, so it runs on the free key.
Working normally
Checked every minute from outside our own servers.
A small client that handles keys, errors and rate limits, and two scripts that already work: what is on now in any country, and any channel's day in local time. Copy them, set your key, run.
$ python now.py GB 4seven Gogglebox 22:00 The Great British Bake Off 5Action Police Interceptors 22:00 Highway Thru Hell 5SELECT Warship: Life in the Royal Navy 22:00 Battlefront - WWII: Road To Victory 5STAR Ambulance: Code Red 22:00 A&E After Dark 5USA Wild Cards 22:00 Law & Order: Special Victims Unit 8 Out Of 10 Cats Episode 5 22:02 Episode 4 African Movie Channel Se Lodge 49 22:10 Lodge 49 Ahlebait TV Preaching of Ahlebait (a.s) 22:00 Islamic Movie Akaal Channel Live: Sikligar Wanjare Gurdwara 22:25 Today's Sikh History Alb UK TV Programi i ALB UK 23:00 Programi i ALB UK
tvguideapi.com/loginpip install requestsexport TVG_KEY=tvg_live_...python now.py GB Europe/LondonA client, a now-and-next script and a schedule script. The Output tab is what python now.py GB printed a few minutes ago, from the live guide.
# tvguide.py: a small client for the TV Guide API. Python 3.9+, requests only.
import os, time, requests
class TvGuide:
def __init__(self, key=None, base="https://api.tvguideapi.com"):
self.key = key or os.environ["TVG_KEY"]
self.base = base
self.http = requests.Session()
self.http.headers.update({"Authorization": f"Bearer {self.key}", "Accept": "application/json"})
def get(self, path, **params):
for attempt in range(3):
r = self.http.get(self.base + path, params=params, timeout=20)
if r.status_code == 429 and attempt < 2:
time.sleep(int(r.headers.get("Retry-After", "2"))) # wait as long as the API asks
continue
body = r.json() if r.content else {}
if r.status_code >= 400:
err = body.get("error", {})
raise RuntimeError(f"{err.get('code', r.status_code)}: {err.get('message', 'request failed')}")
return body
def channel_names(self, country):
return {c["id"]: c["name"] for c in self.get("/v1/channels", country=country, limit=500)["data"]}
def find_channel(self, name, country=None):
found = self.get("/v1/channels/search", q=name, country=country)["data"]
return found[0] if found else None# now.py: what is on now in a country, with what comes next. # python now.py GB Europe/London import sys from datetime import datetime from zoneinfo import ZoneInfo from tvguide import TvGuide tv = TvGuide() country = (sys.argv[1] if len(sys.argv) > 1 else "GB").upper() tz = ZoneInfo(sys.argv[2] if len(sys.argv) > 2 else "Europe/London") names = tv.channel_names(country) for row in tv.get("/v1/now", country=country)["data"]: now = (row.get("now") or {}).get("title", "Off air") nxt = row.get("next") when = datetime.fromisoformat(nxt["start"].replace("Z", "+00:00")).astimezone(tz).strftime("%H:%M") if nxt else "" print(f"{names.get(row['channel'], row['channel'])[:24]:<24} {now[:36]:<36} {when} {nxt['title'] if nxt else ''}")
# schedule.py: one channel's day, in local time. # python schedule.py "BBC One" GB 2026-10-10 Europe/London import sys from datetime import datetime, date from zoneinfo import ZoneInfo from tvguide import TvGuide tv = TvGuide() name = sys.argv[1] if len(sys.argv) > 1 else "BBC One" country = sys.argv[2] if len(sys.argv) > 2 else "GB" day = sys.argv[3] if len(sys.argv) > 3 else date.today().isoformat() zone = sys.argv[4] if len(sys.argv) > 4 else "Europe/London" channel = tv.find_channel(name, country) if not channel: sys.exit(f"No channel called {name} in {country}") for slot in tv.get(f"/v1/channels/{channel['id']}/schedule", date=day, tz=zone)["data"]: t = datetime.fromisoformat(slot["start"].replace("Z", "+00:00")).astimezone(ZoneInfo(zone)) live = " [LIVE]" if slot["flags"]["live"] else "" print(f"{t:%H:%M} {slot['title']}{live}")
$ python now.py GB
4seven Gogglebox 22:00 The Great British Bake Off
5Action Police Interceptors 22:00 Highway Thru Hell
5SELECT Warship: Life in the Royal Navy 22:00 Battlefront - WWII: Road To Victory
5STAR Ambulance: Code Red 22:00 A&E After Dark
5USA Wild Cards 22:00 Law & Order: Special Victims Unit
8 Out Of 10 Cats Episode 5 22:02 Episode 4
African Movie Channel Se Lodge 49 22:10 Lodge 49
Ahlebait TV Preaching of Ahlebait (a.s) 22:00 Islamic Movie
Akaal Channel Live: Sikligar Wanjare Gurdwara 22:25 Today's Sikh History
Alb UK TV Programi i ALB UK 23:00 Programi i ALB UK
Built on the client above. Each opens its endpoint in the documentation, with Try it.
grid = tv.get("/v1/now", channels="ch_a,ch_b,ch_c")["data"]Cache a day as JSONFree keyimport json
day = tv.get(f"/v1/channels/{cid}/schedule", tz="Europe/London")
open("today.json", "w").write(json.dumps(day)) # refresh every 10 minutesFind a channel idFree keycid = tv.find_channel("TF1", "FR")["id"] # store it: ids never changeWhen is a show on nextFree keysid = tv.get("/v1/shows/search", q="Bluey")["data"][0]["id"]
next_up = tv.get(f"/v1/shows/{sid}/airings", country="GB", days=7)["data"]Live sport on TV nowPaid planslive = tv.get("/v1/events/live", country="GB")["data"]An XMLTV file for your playerPaid plansr = tv.http.get(tv.base + "/v1/xmltv", params={"country": "GB", "days": 7})
open("guide.xml", "wb").write(r.content)A past day, for researchPaid planspast = tv.get(f"/v1/channels/{cid}/history", **{"from": "2024-07-05T00:00:00Z", "to": "2024-07-06T00:00:00Z"})Your calls leftFree keyprint(tv.get("/v1/usage")["data"])The same shape for every country. Fields in full on Now on.
{
"data": [
{
"channel": "ch_...",
"now": {
"title": "Gogglebox"
},
"next": {
"title": "The Great British Bake Off",
"local_start": "22:00"
}
},
{
"channel": "ch_...",
"now": {
"title": "Police Interceptors"
},
"next": {
"title": "Highway Thru Hell",
"local_start": "22:00"
}
}
],
"meta": {
"count": 10
}
}Everything above except sport, XMLTV and the archive runs on the free key.
A free key in a minute, with Google. No card. 41,066 channels in 155 countries.