Step 1: Search
Use the search function to find the show you’re looking for. The show
“24” is a particularly good example for a show with a bad name, because
If we just search for “24”, we’ll get a wrong result due to
ambiguity.
To nudge the search function to find the show we’re looking for, we can
specify the year of release:
show_info <- search_query("24", years = 2001, type = "show")
show_info
#> # A tibble: 1 × 9
#> type score title year trakt slug tvdb imdb tmdb
#> <chr> <dbl> <chr> <int> <chr> <chr> <chr> <chr> <chr>
#> 1 show 5.79e17 24 2001 1960 24-2001 76290 tt0285331 1973Now we have the basic show info to work with, including the
title and various IDs.
Step 2: Use the ID
Use the trakt ID for subsequent API calls, which is
guaranteed to be always available and unique on trakt.tv.
Now we’ll use seasons_summary() to get data for all seasons
of the show, while also getting an additional list-column containing all
episode data, which includes user ratings.
seasons <- seasons_summary(show_info$trakt, extended = "full", episodes = TRUE)
glimpse(seasons)
#> Rows: 9
#> Columns: 15
#> $ season <int> 1, 2, 3, 4, 5, 6, 7, 8, 9
#> $ rating <dbl> 8.18852, 8.16570, 8.11644, 8.19305, 8.45992, 7.44286, 7…
#> $ votes <int> 488, 344, 292, 259, 237, 210, 199, 192, 201
#> $ episode_count <int> 24, 24, 24, 24, 24, 24, 24, 24, 12
#> $ aired_episodes <int> 24, 24, 24, 24, 24, 24, 24, 24, 12
#> $ title <chr> "Season 1", "Season 2", "Season 3", "Season 4", "Season…
#> $ overview <chr> "Counter-terrorism agent Jack Bauer attempts to stop th…
#> $ first_aired <dttm> 2001-11-07 02:00:00, 2002-10-30 02:00:00, 2003-10-29 02…
#> $ updated_at <dttm> 2025-11-02 02:26:57, 2025-11-02 01:34:43, 2025-11-02 01…
#> $ network <chr> "FOX", "FOX", "FOX", "FOX", "FOX", "FOX", "FOX", "FOX",…
#> $ original_title <chr> NA, NA, NA, NA, NA, NA, NA, NA, "Live Another Day"
#> $ episodes <list> [<tbl_df[24 x 20]>], [<tbl_df[24 x 20]>], [<tbl_df[24 x…
#> $ trakt <chr> "6262", "6263", "6264", "6265", "6266", "6267", "6268"…
#> $ tvdb <chr> "10063", "10064", "10065", "10066", "10067", "16794", …
#> $ tmdb <chr> "5845", "5846", "5847", "5848", "5849", "5850", "5851",…Step 3: Tidying up
We’re interested in the $episodes list-column, which
needs unnesting. In this case we can use dplyr::bind_rows()
to take the list of tibbles and rbind them all
together, meaning the result is a tibble of the episode
data we care about.
episodes <- bind_rows(seasons$episodes)
glimpse(episodes)
#> Rows: 204
#> Columns: 20
#> $ season <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
#> $ episode <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, …
#> $ title <chr> "12:00 A.M.-1:00 A.M.", "1:00 A.M.-2:00 A.M.", …
#> $ episode_abs <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ overview <chr> "Moments after discovering his daughter, Kimber…
#> $ rating <dbl> 7.65880, 7.73068, 7.71177, 7.70521, 7.69697, 7.…
#> $ votes <int> 1568, 1229, 1079, 1055, 990, 947, 926, 918, 900…
#> $ comment_count <int> 9, 3, 3, 3, 3, 4, 4, 2, 2, 3, 1, 3, 1, 2, 4, 5,…
#> $ first_aired <dttm> 2001-11-07 02:00:00, 2001-11-14 02:00:00, 2001…
#> $ updated_at <dttm> 2025-11-02 02:26:52, 2025-11-02 02:26:57, 2025…
#> $ available_translations <list> [], [], [], [], [], [], [], [], [], [], [], []…
#> $ runtime <int> 43, 41, 43, 42, 42, 43, 43, 43, 43, 43, 43, 43,…
#> $ episode_type <chr> "series_premiere", "standard", "standard", "sta…
#> $ original_title <chr> "12:00 A.M.-1:00 A.M.", "1:00 A.M.-2:00 A.M.", …
#> $ after_credits <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE…
#> $ during_credits <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE…
#> $ trakt <chr> "146247", "146248", "146249", "146250", "146251…
#> $ tvdb <chr> "189255", "189256", "189257", "189258", "189259…
#> $ imdb <chr> "tt0502165", "tt0502167", "tt0502169", "tt05021…
#> $ tmdb <chr> "972745", "972752", "972753", "134397", "134398…Step 4: Graph!
Now we have our episode data in a tidy form, might as well look at it.
ggplot(data = episodes, aes(x = episode, y = rating, color = votes)) +
geom_point(size = 3, alpha = 2 / 3) +
facet_wrap(~season, nrow = 1, scales = "free_x") +
scale_x_continuous(breaks = c(1, 10, 20), expand = c(0, 3)) +
scale_y_continuous(breaks = seq(0, 10, .5), minor_breaks = seq(0, 10, .25), limits = c(7, 9)) +
scale_color_viridis_c() +
guides(color = guide_colorbar(barwidth = unit(6, "cm"), title.vjust = .75)) +
labs(
title = "24: Episode Ratings on trakt.tv",
subtitle = "Episode ratings by trakt.tv users by season",
x = "Episode Number", y = "Rating (1-10)", color = "# of Votes",
caption = "jemus42.github.io/tRakt"
) +
theme_minimal() +
theme(
plot.title.position = "plot",
legend.position = "bottom"
)
