Skip to content

LukeOsland1/statsify

Repository files navigation

Statsify — a self-hosted stats.fm clone for Spotify

CI License: MIT

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.

Statsify — top tracks with selectable time windows

Why it works the way it does

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.

Tech stack

Next.js 16 (App Router) · React 19 · TypeScript · Tailwind CSS v4 · Prisma 6 + SQLite · Auth.js (NextAuth v5) · Recharts · stream-json.

Setup

Requires Node 22+.

1. Install

npm install

2. Create a Spotify app

  1. Go to the Spotify Developer Dashboard and create an app.
  2. Add this Redirect URI exactly: http://127.0.0.1:3000/api/auth/callback/spotify (Spotify rejects localhost for loopback — you must use 127.0.0.1.)
  3. 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.

3. Configure environment

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"

4. Create the database

npm run db:push

5. Run

npm run dev

Open http://127.0.0.1:3000 (use 127.0.0.1, not localhost, so the OAuth redirect matches) and click Connect Spotify.

Importing your lifetime history

The live API can't give you lifetime stats — you import them once:

  1. On Spotify's Privacy pageDownload your data, untick Account data and tick Extended streaming history. Request it. Spotify emails the export within ~30 days as Streaming_History_Audio_*.json files.

  2. Import them, either way:

    • In the app: go to /import and drag the .json files 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.

  3. 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.

Notes & limitations

  • 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 optional minMs threshold (≈30s) if you want Spotify-style stream counting.

Scripts

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

Disclaimer

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.

Project layout

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)

About

Self-hosted Spotify listening stats — a stats.fm-style dashboard. Live top tracks/artists/genres, recent, now-playing, and optional lifetime-history import. Next.js, Prisma/SQLite, Auth.js.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages