- Dockerized FastAPI service that assembles a Spotify Wrapped-style report using the Spotify Web API.
- Simple card UI at
/card/rewrappedshowing number of plays, number of unique tracks, top artists, top tracks, top albums and so on. This is available on a month and year level. - Even simpler card UI art
/card/extendedshowing top artists and top tracks over a short, medium and long time bucket as defined by spotify. Also includes basic theme color switching because why not?
- Create a Spotify app in the Dashboard. Add a redirect URI (e.g.,
http://localhost:8080/callback). Note the Client ID and Client Secret. - Build the authorize URL and open it in your browser (replace
CLIENT_IDandREDIRECT_URI; required scopes:user-top-read user-read-recently-played user-read-private):After you accept, Spotify redirects tohttps://accounts.spotify.com/authorize?client_id=CLIENT_ID &response_type=code &redirect_uri=REDIRECT_URI &scope=user-top-read%20user-read-recently-played%20user-read-privateREDIRECT_URI?code=.... Copy thecode. - In a shell (WSL/Linux/macOS), generate the Basic header and exchange the code for tokens:
The response JSON includes
CLIENT_ID="your_client_id" CLIENT_SECRET="your_client_secret" CODE="the_code_from_redirect" REDIRECT_URI="http://localhost:8080/callback" # must match what you used above BASIC=$(printf "%s:%s" "$CLIENT_ID" "$CLIENT_SECRET" | base64) curl --http1.1 -v -X POST "https://accounts.spotify.com/api/token" \ -H "Authorization: Basic $BASIC" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "code=$CODE" \ --data-urlencode "redirect_uri=$REDIRECT_URI"
refresh_token. Keep it safe.
From here you can either run locally or deploy on Render.
- Provision a MongoDB database (Atlas or self-hosted).
- Add these env vars alongside the Spotify vars:
MONGODB_URI(SRV or standard connection string)MONGODB_DB(default:rewrapped)MONGODB_COLLECTION(default:plays)
- Plays are keyed by
played_atand upserted, so the collection will not contain overlapping/duplicate items.
-
Populate
.env(see.env.example) and either(i) Start the API in Terminal:
python -m venv .venv && source .venv/bin/activate # or .venv\Scripts\activate on Windows pip install -r requirements.txt uvicorn app.main:app --reload
(ii) Use Docker:
docker build -t rewrapped . docker run --env-file .env -p 8000:8000 rewrapped
- Clone this repo or push a local version of it to GitHub (do not commit your
.env; keep it local). - In Render, create a new Web Service from the repo, choose Docker as the runtime.
- Set environment variables in the Render dashboard:
SPOTIFY_CLIENT_ID,SPOTIFY_CLIENT_SECRET,SPOTIFY_REFRESH_TOKEN(optionallySPOTIFY_API_BASE,SPOTIFY_AUTH_BASE,REQUEST_TIMEOUT). - Deploy. Use the public URL Render gives you (e.g.,
https://your-app.onrender.com/card/rewrapped,/card/extended).
This implements continuous syncing. A workflow at .github/workflows/ingest.yml runs every 15 minutes (and can be triggered manually) to pull the most recent Spotify plays and store them in MongoDB without overlaps:
- Add repository secrets:
SPOTIFY_CLIENT_ID,SPOTIFY_CLIENT_SECRET,SPOTIFY_REFRESH_TOKEN,MONGODB_URI, and optionallyMONGODB_DB,MONGODB_COLLECTION. - The workflow executes
python -m app.ingest_recent, which upserts plays byplayed_atand keeps indexes fresh.
Optionally request your entire spotify listening history from Spotify via their privacy page. This can take a while. Once you have it:
- Run the command below. This may take a while; take a break.
python -m app.ingest_dump "{path_to_dir}/Streaming_History_Audio*.json" - Unfortunately, the Spotify dump does not include album cover images and album IDs. To fix that, run the commands below. Be careful with the
batch_size(default 1000) as you do not want to exceed Spotify's rate limit.python -m app.backfill_images batch_size wait_time python -m app.backfill_album_ids batch_size wait_time
GET /wrapped/short?top_limit=50&recent_limit=50
Short-term (~4 weeks) top tracks/artists plus the small recent playback window Spotify exposes (about the last 50 plays).GET /wrapped/medium?top_limit=50
Medium-term (~6 months) top tracks and artists.GET /wrapped/long?top_limit=50
Long-term (multi-year) top tracks and artists.GET /wrapped/yearly?year=2024&limit=20
Wrapped view backed by stored plays in MongoDB for a full calendar year. Defaults to the previous calendar year if omitted.GET /wrapped/monthly?month=11&year=2024&limit=20
Wrapped view backed by stored plays in MongoDB for a specific month/year. Defaults to the previous calendar month if omitted. Returns play counts, minutes, and top tracks/artists/albums (albums are included when available).
GET /card
Simple HTML card that visualizes top tracks and artists side by side. Uses/wrapped/{short|medium|long}under the hood; adjust range and limit via the UI controls.GET /card/extended
Themed version of the card with selectable styles plus range and limit controls.GET /card/rewrapped
Card powered by stored MongoDB plays. Toggle month vs year view, choose period and limit; shows top tracks, artists, and albums with play counts and minutes listened.
- Multi-year stats endpoint and dashboard.
- A more interesting dashboard using the API