A personal listening-stats dashboard: top tracks/artists/albums/genres, lifetime totals, a listening clock, a calendar heatmap, per-entity detail pages, recently-played, and now-playing.
Spotify's Web API is locked down. A newly-created app sits in Development Mode: max 5 manually-authorized users, a Premium account is required, and only a reduced set of endpoints is available. Extended (public) access now requires a registered business with 250k+ monthly active users — so this is built for you and a few friends, not a public launch.
Critically, the live API has no lifetime history and the catalog endpoints (/tracks, /artists,
/albums) were removed in Feb 2026. So Statsify uses two data sources:
| Source | Gives you | How |
|---|---|---|
| Live Spotify API | Top tracks/artists (3 fixed ranges), recently-played, now-playing, genres | OAuth login |
| Imported history | Full lifetime per-stream data → all the deep analytics | Upload your Extended Streaming History JSON |
Metadata (art, genres) is harvested from whatever the live API returns, and album/artist art for the rest of your history is backfilled from Deezer's free public API.
Next.js 16 (App Router) · React 19 · TypeScript · Tailwind CSS v4 · Prisma 6 + SQLite · Auth.js (NextAuth v5) · Recharts · stream-json.
Requires Node 22+.
npm install- Go to the Spotify Developer Dashboard and create an app.
- Add this Redirect URI exactly:
http://127.0.0.1:3000/api/auth/callback/spotify(Spotify rejectslocalhostfor loopback — you must use127.0.0.1.) - Under the app's User Management, add the Spotify account email of every person who will sign in (up to 5). Development Mode requires each user to have Spotify Premium.
Copy .env.example to .env and fill in:
DATABASE_URL="file:./dev.db"
AUTH_SECRET="..." # generate with: npx auth secret
SPOTIFY_CLIENT_ID="..." # from your Spotify app
SPOTIFY_CLIENT_SECRET="..." # from your Spotify app
AUTH_URL="http://127.0.0.1:3000"npm run db:pushnpm run devOpen http://127.0.0.1:3000 (use 127.0.0.1, not localhost, so the OAuth redirect matches) and
click Connect Spotify.
The live API can't give you lifetime stats — you import them once:
-
On Spotify's Privacy page → Download your data, untick Account data and tick Extended streaming history. Request it. Spotify emails the export within ~30 days as
Streaming_History_Audio_*.jsonfiles. -
Import them, either way:
- In the app: go to
/importand drag the.jsonfiles in. - CLI (recommended for large exports — no HTTP timeout):
npm run import -- ~/Downloads/Spotify\ Extended\ Streaming\ History/Streaming_History_Audio_*.json
Re-importing the same files is safe — duplicates are skipped.
- In the app: go to
-
Backfill album/artist art (Spotify can't provide it for arbitrary tracks; this uses Deezer):
npm run enrich
Or click Sync metadata from Spotify on the dashboard to harvest art/genres for items that appear in your live top/recent lists.
- Times in charts (listening clock, heatmap) are UTC — Spotify's export timestamps are UTC.
- Genres only exist for artists Spotify returns via the API; imported-only artists won't have them.
- Top albums are import-only (Spotify has no top-albums endpoint).
- Podcast/episode plays are skipped (music only, v1).
- A play is counted as a stream (row); minutes sum
ms_played. The analytics layer supports an optionalminMsthreshold (≈30s) if you want Spotify-style stream counting.
| Command | Does |
|---|---|
npm run dev |
Start the dev server |
npm run build / npm start |
Production build / serve |
npm run import -- <files…> |
Bulk-import streaming history JSON |
npm run enrich |
Backfill art from Deezer |
npm run db:push |
Create/sync the SQLite schema |
npm run db:studio |
Open Prisma Studio |
An unofficial, personal/educational project. Not affiliated with, endorsed by, or connected to Spotify or stats.fm. "Spotify" is a trademark of Spotify AB. Use of the Spotify API is subject to the Spotify Developer Terms. You are responsible for your own app credentials and for staying within Spotify's quota and usage policies.
app/ # routes (dashboard, top, stats, track/artist/album, recent, import, api/*)
components/ # Nav, charts, TopList, NowPlaying, UI primitives
lib/
analytics.ts # raw-SQL aggregations over imported history
spotify.ts # API wrapper (token refresh, 429 backoff)
auth.ts # Auth.js + Spotify provider
import/{parse,ingest} # streaming JSON parser + dedup-safe batched inserts
enrich.ts # harvest metadata from Spotify payloads
enrichFallback.ts # Deezer art backfill
scripts/{import,enrich} # CLIs
prisma/schema.prisma # data model (Stream fact table + catalog entities)
