# Load packages
library(spotifyr) #API interaction
library(tidyverse)
library(kableExtra)
library(httr) # Troubleshooting redirect url
source(here::here("posts/2025-11-18-spotify/keys.R")) # My API keysProject Overview
Inspired by (Muir 2024) who is a MEDS alumni, I want to extract and display data from my Spotify. Similar to the annual Spotify Wrapped information like my top tracks played and top artists can be retrieved. Additionally, I wanna try to get track audio features (track danceability, energy, loudness, key, etc.) like Sam did in their project.
Using the {spotifyr} package
The {spotifyr} package requests data from the Spotify API and we are able to load this data into R.
If you’d like to follow along, do this set up:
Create a Spotify Developer Account on the Spotify Developer Website.
Create an app with a title, description, and redirect url.
- As of April 9th, 2025, localhost string is not allowed in Spotify redirect URI’s. See this {spotifyr} GitHub Issue. I set the redirect as http://127.0.0.1:1410/ and I followed the solution in the GitHub Issue.
Save your client ID & client secret.
I sourced these keys from a separate R script to keep them private. The following chunk is how to set up the keys as environmental variables in R.
# Setup client ID & Secret in keys.R
Sys.setenv(SPOTIFY_CLIENT_ID = SPOTIFY_CLIENT_ID)
Sys.setenv(SPOTIFY_CLIENT_SECRET = SPOTIFY_CLIENT_SECRET)# From GitHub Issue, re-define authorization function
get_spotify_authorization_code_fixed <- function (client_id = Sys.getenv("SPOTIFY_CLIENT_ID"), client_secret = Sys.getenv("SPOTIFY_CLIENT_SECRET"),
scope = scopes()[c(1:19)]) # Trying Sam's scopes in newly defined function
{
endpoint <- oauth_endpoint(authorize = "https://accounts.spotify.com/authorize",
access = "https://accounts.spotify.com/api/token")
app <- oauth_app("spotifyr", client_id, client_secret, redirect_uri = "http://127.0.0.1:1410/")
token <- (purrr::safely(.f = oauth2.0_token))(endpoint = endpoint,
app = app, scope = scope)
if (!is.null(token$error)) {
token$error
}
else {
token$result
}
}Now running the fixed function should work to get an authentication code.
auth_code <- get_spotify_authorization_code_fixed()Use auth_code generated from the fixed authorization code function and we can now call (some) {spotifyr} functions!
# Define function to extract genres from genre list column
create_genre_string <- function(genre_list) {
paste(genre_list, collapse = ", ")
}
# Get top artists
top_artists <- get_my_top_artists_or_tracks(type = "artists",
limit = 10,
time_range = "medium_term",
authorization = auth_code) |>
mutate(genre_string = map_chr(genres, create_genre_string)) |>
select(c(name, genre_string, popularity, followers.total))# Define function to extract artist names from artist dataframe column
create_artist_string <- function(artist_df) {
paste(artist_df$name, collapse = ", ")
}
# Get top tracks
top_tracks <- get_my_top_artists_or_tracks(type = "tracks",
limit = 10,
time_range = "medium_term",
authorization = auth_code) |>
mutate(artist_list = map_chr(artists, create_artist_string)) |>
select(c(name, artist_list, album.name, album.release_date))Other cool functions!
# Get current playing track
current <- get_my_currently_playing(authorization = auth_code)
# Get recently played tracks
recents <- get_my_recently_played(limit = 3, authorization = auth_code)Since I had to source the keys I can’t produce a rendered, up to date output. But I’ll come back to this post, re-run everything, and see how things change. So instead, I have to export the R objects as a csv and read them in.
# # Export top tracks as csv
# write_csv(top_tracks, file = 'posts/2025-11-18-spotify/zach_tracks.csv')
# # Export top artists as csv
# write_csv(top_artists, file = 'posts/2025-11-18-spotify/zach_artists.csv')
# Read in my csvs
zachs_tracks <- read_csv(here::here('posts', '2025-11-18-spotify', 'zach_tracks.csv'))
zachs_artists <- read_csv(here::here('posts', '2025-11-18-spotify', 'zach_artists.csv'))zachs_tracks |>
kbl(col.names = c("Track Name", "Artists", "Album Name", "Album Release Date"))| Track Name | Artists | Album Name | Album Release Date |
|---|---|---|---|
| Hey you ?, Hey sup ? | Masayuki Suzuki | Snazzy | 2024-03-27 |
| Red Swan | YOSHIKI, HYDE | Red Swan | 2018-10-03 |
| Psychedelic City | Masayuki Suzuki | Snazzy | 2024-03-27 |
| YOU | JOSS, Kenny Pham, KELIZA | YOU | 2025-04-11 |
| Full of Grace (I Refuse to Tend My Own Grave) | $uicideboy$ | THY KINGDOM COME | 2025-08-01 |
| Sakura Biyori and Time Machine with Hatsune Miku | Ado, Hatsune Miku | Ado's Best Adobum | 2025-04-08 |
| Good Things Fall Apart vs. Sad Songs (With Said The Sky feat. Annika Wells) | ILLENIUM, Jon Bellion, Said The Sky, Annika Wells | ASCEND (Tour Edits) | 2020-06-12 |
| Whiplash | Crankdat, SOFI | Whiplash | 2025-03-14 |
| 風のゆくえ | Ado | ウタの歌 ONE PIECE FILM RED | 2022-08-09 |
| Ichiban no Takaramono - Yui final Ver. | LiSA, VISUAL ARTS / Key | Ichiban no Takaramono -Yui final ver.- / Girls Dead Monster STARRING LiSA | 2010-12-08 |
My Top 5 Tracks
I’m displaying the above Spotify content manually through the Creating an Embed feature from Spotify.
zachs_artists |>
mutate(followers.total = scales::comma(followers.total)) |>
kbl(col.names = c("Artist Name", "Genres", "Popularity", "Total Followers"))| Artist Name | Genres | Popularity | Total Followers |
|---|---|---|---|
| $uicideboy$ | emo rap, horrorcore, cloud rap, trap metal, underground hip hop | 84 | 9,006,324 |
| Masayuki Suzuki | anime, j-pop, kayokyoku, city pop | 51 | 315,706 |
| Ado | j-pop, anime, vocaloid, j-rock | 76 | 6,790,414 |
| ILLENIUM | melodic bass, future bass, edm, techno | 71 | 1,635,229 |
| Sawano Hiroyuki | anime, soundtrack | 66 | 733,386 |
| LiSA | anime, j-pop, j-rock | 69 | 3,618,103 |
| Ray Volpe | dubstep, riddim, deathstep, melodic bass, bass music, edm | 53 | 173,553 |
| Crankdat | dubstep, riddim, deathstep, edm | 53 | 200,462 |
| SLANDER | melodic bass, dubstep, future bass, edm | 64 | 594,841 |
| Seven Lions | melodic bass, future bass, edm, chillstep, dubstep, techno, progressive trance | 59 | 523,928 |
My Top 5 Artists
Conclusion
Unfortunately, numerous endpoints in the Spotify API were deprecated in November 2024 outlined in this Spotify for Developers Post, including Get Track’s Audio Features (for danceability, energy, etc.). This was due to an update in Spotify’s policies where they no longer wanted users to input these data into a machine learning/AI model.
However, this was still a cool project to do a little bit of wrangling in R and also a slight introduction to APIs for me. Again, I’ll be re-visiting this blog post and updating it periodically to see how my Spotify stats change. Thanks for tuning in!
References
Citation
@online{loo2025,
author = {Loo, Zach},
title = {Spotify on {Webpages}},
date = {2025-11-20},
url = {https://zachyyy700.github.io/posts/2025-11-18-spotify/},
langid = {en}
}