Episode guide API
The episode guide API lists every episode of a series through GET /3/tv/{tv_id}/season/{season_number}. Each episode has a name, overview, air date, runtime and still image. The episode_groups list names alternate orders such as DVD or absolute. Paths and JSON match TMDB v3, so existing clients only need a new base URL.
Get every episode of a season
GET /3/tv/{tv_id}/season/{season_number} returns a season with its full episodes array. Each episode has episode_number, name, overview, air_date, runtime and still_path. Ids are the numeric ids TMDB uses, for example 1396 for Breaking Bad.
You get seasons, episodes with dates and runtimes, stills, the next and last aired episode of a series, and the list of alternate episode orders. Text fields follow the language parameter, and an overview stays empty when no translation exists.
What data you get
| Endpoint | Returns |
|---|---|
GET /3/tv/{tv_id} | seasons summary, number_of_seasons, status, next_episode_to_air, last_episode_to_air |
GET /3/tv/{tv_id}/season/{season_number} | One season with all its episodes |
GET /3/tv/{tv_id}/season/{season_number}/episode/{episode_number} | One episode with crew and guest_stars |
GET /3/tv/{tv_id}/episode_groups | Name, type and episode count of each alternate order defined for the series |
Fields of an episode:
| Field | Type | Notes |
|---|---|---|
id, show_id | integer | Episode id and series id. show_id is in season lists and in next_episode_to_air, not on the single episode path |
episode_number, season_number | integer | Position in the season |
episode_type | string | For example standard |
name, overview | string | Localized with language |
air_date | string or null | YYYY-MM-DD, empty or null when not announced |
runtime | integer or null | Minutes |
still_path | string or null | Join to https://img.amodb.dev/t/p/{size} |
vote_average, vote_count | number, integer | Currently 0 on episodes |
production_code | string | Studio code, often empty |
crew, guest_stars | array | People credited on the episode, with the same fields as a cast member |
Start in 3 steps
- Choose a plan on pricing. All plans return the same episode data, and only the request rate differs.
- Create a key in your account dashboard and store it in an environment variable.
- Request
/3/tv/{tv_id}/season/1with the key as a Bearer header or asapi_key.
Example request
The sample fetches season 1 of Breaking Bad (series id 1396) in English. The Go program decodes it into typed structs and prints one line per episode with the air date, the runtime and the still URL.
curl -s "https://api.amodb.dev/3/tv/1396/season/1?language=en-US" \
-H "Authorization: Bearer $AMODB_KEY"package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
type Episode struct {
EpisodeNumber int `json:"episode_number"`
Name string `json:"name"`
AirDate string `json:"air_date"`
Runtime int `json:"runtime"`
StillPath *string `json:"still_path"`
}
type Season struct {
Name string `json:"name"`
SeasonNumber int `json:"season_number"`
Episodes []Episode `json:"episodes"`
}
func main() {
client := &http.Client{Timeout: 10 * time.Second}
url := "https://api.amodb.dev/3/tv/1396/season/1?language=en-US"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("AMODB_KEY"))
res, err := client.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
panic(fmt.Sprintf("amodb returned %s", res.Status))
}
var season Season
if err := json.NewDecoder(res.Body).Decode(&season); err != nil {
panic(err)
}
fmt.Println(season.Name)
for _, e := range season.Episodes {
still := "no still"
if e.StillPath != nil {
still = "https://img.amodb.dev/t/p/w300" + *e.StillPath
}
fmt.Printf("S%02dE%02d %s %3d min %s %s\n",
season.SeasonNumber, e.EpisodeNumber, e.AirDate, e.Runtime, e.Name, still)
}
}Trimmed response: 2 of the 7 episodes, with one crew member and one guest star each.
{
"air_date": "2008-01-20",
"episodes": [
{
"air_date": "2008-01-20",
"episode_number": 1,
"episode_type": "standard",
"id": 62085,
"name": "Pilot",
"overview": "When an unassuming high school chemistry teacher discovers he has a rare form...",
"production_code": "",
"runtime": 59,
"season_number": 1,
"show_id": 1396,
"still_path": "/ydlY3iPfeOAvu8gVqrxPoMvzNCn.jpg",
"vote_average": 0,
"vote_count": 0,
"crew": [
{
"department": "Camera",
"job": "Director of Photography",
"credit_id": "52b7029219c29533d00dd2c1",
"adult": false,
"gender": 2,
"id": 2483,
"known_for_department": "Camera",
"name": "约翰·托尔",
"original_name": "约翰·托尔",
"popularity": 1.1957,
"profile_path": "/cfL9A6tC7L5Ps5fq1o3WpVKGMb1.jpg"
}
],
"guest_stars": [
{
"character": "Steven Gomez",
"credit_id": "5271b489760ee35b3e0881a7",
"order": 8,
"adult": false,
"gender": 2,
"id": 61535,
"known_for_department": "Acting",
"name": "史蒂文·迈克尔·奎扎达",
"original_name": "史蒂文·迈克尔·奎扎达",
"popularity": 1.7511,
"profile_path": "/pVYrDkwI6GWvCNL2kJhpDJfBFyd.jpg"
}
]
},
{
"air_date": "2008-01-27",
"episode_number": 2,
"episode_type": "standard",
"id": 62086,
"name": "Cat's in the Bag...",
"overview": "Walt and Jesse attempt to tie up loose ends. The desperate situation gets mor...",
"production_code": "",
"runtime": 49,
"season_number": 1,
"show_id": 1396,
"still_path": "/AbMoecO0ZZio0LcgeLxlzdyGs6X.jpg",
"vote_average": 0,
"vote_count": 0,
"crew": [
{
"department": "Camera",
"job": "Director of Photography",
"credit_id": "52b7049c19c2953b63015013",
"adult": false,
"gender": 2,
"id": 17948,
"known_for_department": "Camera",
"name": "Reynaldo Villalobos",
"original_name": "Reynaldo Villalobos",
"popularity": 0.904,
"profile_path": "/4YlfhHDRZWhbRgeqT2Bwl1u5p9j.jpg"
}
],
"guest_stars": [
{
"character": "Krazy-8",
"credit_id": "52725845760ee3046b09426e",
"order": 504,
"adult": false,
"gender": 2,
"id": 1046460,
"known_for_department": "Acting",
"name": "Max Arciniega",
"original_name": "Max Arciniega",
"popularity": 1.4399,
"profile_path": "/eqKAJKPpt41KpsLIkkBnAY4HMAL.jpg"
}
]
}
],
"name": "Season 1",
"overview": "High school chemistry teacher Walter White's life is suddenly transformed by...",
"id": 3572,
"poster_path": "/91EIXdWxmp2mHNRsSuDW3WyDJLj.jpg",
"season_number": 1,
"vote_average": 0
}Fetch a whole series
amodb does not accept season/1 style values in append_to_response, so the season calls stay separate. Read number_of_seasons and the seasons list from the TV details first, then request each season in a loop.
const base = 'https://api.amodb.dev/3';
const headers = { Authorization: `Bearer ${process.env.AMODB_KEY}` };
async function get(path) {
const res = await fetch(base + path, { headers });
if (!res.ok) throw new Error(`amodb returned ${res.status} for ${path}`);
return res.json();
}
const show = await get('/tv/1396');
for (const { season_number } of show.seasons) {
const season = await get(`/tv/1396/season/${season_number}`);
console.log(season.name, season.episodes.length);
}seasons includes season 0 when the series has specials, so Breaking Bad costs 1 details call plus 6 season calls. A series with 10 seasons costs 11 requests. Details are cached at the edge for 6 hours, so store the result and refresh it on a schedule.
Air dates and what comes next
GET /3/tv/{tv_id} holds next_episode_to_air and last_episode_to_air, full episode objects with name, air_date, season_number, episode_number, runtime and still_path. next_episode_to_air is null when nothing is announced. Combine it with status (for example Returning Series, Ended or Canceled) to decide whether to show a countdown.
air_date is a calendar date without a time of day. Compare it with today's date to mark new episodes. For a schedule of what is on, the airing_today and on_the_air lists on trending and lists are cached for 30 minutes.
Stills and runtimes
Use w185 or w300 for list rows and original for full size. The still sizes are w92, w185, w300 and original. An episode can have no still, so render a placeholder when still_path is null. The image host needs no key, see images.
runtime is in minutes and can be null. To show a binge time for a season, add up runtime over episodes and count null as 0. Add append_to_response=credits,images to the single episode path for a credits object and a stills list. The episode cast array is empty, and the people sit in crew and guest_stars. Details are on seasons and episodes and credits.
Alternate orders with episode groups
Some series are watched in a different order than the one they aired in. GET /3/tv/{tv_id}/episode_groups lists the orders defined for a series. Each entry has id, name, description, type, episode_count, group_count and network. The results array is empty when none are defined.
type | Order |
|---|---|
| 1 | Original air date |
| 2 | Absolute |
| 3 | DVD |
| 4 | Digital |
| 5 | Story arc |
| 6 | Production |
| 7 | TV |
amodb returns only this list. It does not offer GET /3/tv/episode_group/{id}, so the episodes inside a group are not available. Use the season endpoints for the episode data.
How it compares
| Capability | Scrape episode pages | Free-tier metadata API | amodb |
|---|---|---|---|
| Full season | One page fetch per season | Check their docs | One call, episodes array included |
| Whole series | Crawl every season | Check their docs | One call per season, listed in seasons |
| Air dates | Parse text | Check their docs | air_date plus next_episode_to_air |
| Stills | Download and resize | Check their docs | Fixed sizes on img.amodb.dev, no key |
| Other episode orders | Not available | Check their docs | episode_groups list, group contents not served |
| Languages | One locale per site | Check their docs | 14 languages via language |
Pricing
Plans are prepaid for 1, 3, 6, 12 or 24 months, with 0, 10, 20, 30 or 40 percent off for the longer periods, and they never renew on their own. The 20, 40 and 120 requests per minute plans return every endpoint on this page. Refreshing a series costs one request for the details and one per season. There is no free tier. Pick a plan on pricing.
Questions and answers
How do I list all episodes of a TV series?
Read number_of_seasons from GET /3/tv/{tv_id}, then call GET /3/tv/{tv_id}/season/{season_number} for each season. Every season reply contains the full episodes array, so you need no separate call per episode. A few seasons come back with an empty episodes array when no episode data is held.
Can I get several seasons in one request?
No. amodb ignores season/1 style values in append_to_response, so each season is its own request. A series with 10 seasons costs 11 requests, one for the details and one per season.
What is an episode group?
An episode group is an alternate order of the same episodes, such as DVD, absolute or story arc order. GET /3/tv/{tv_id}/episode_groups lists them with name, type and episode count. amodb does not serve the episodes inside a group.
How do I find out when the next episode airs?
GET /3/tv/{tv_id} returns next_episode_to_air with its air_date, season_number and episode_number. The value is null when nothing is announced. last_episode_to_air gives the most recent one.
Where do episode images come from?
Each episode has a still_path. Join it to https://img.amodb.dev/t/p/{size}, for example w300 for list rows. Images need no key. A still_path can be null, so keep a placeholder ready.