Self-hosted Spotify listening history and stats. Import your Spotify Extended Streaming History ZIP, sync new plays from Last.fm, and explore the library through a Next.js dashboard backed by MongoDB Atlas.
Default UI range is This year (ytd). Use the period control or ?range=all for all time.
Soundfolio is not affiliated with Spotify. Spotify is a trademark of Spotify AB. The app reads the official account data export ZIP; the Spotify Web API is optional for recent-play sync.
- Next.js 16 App Router, React 19, Tailwind CSS v4, shadcn/ui
- MongoDB Atlas via the native MongoDB Node driver
- Recharts visualizations
- Vercel deployment
You need Node 20.19+ and a MongoDB Atlas connection string.
npm install
cp .env.example .env.local
npm run db:indexes
npm run devFill in:
MONGODB_URI: MongoDB Atlas URI. Rotate any URI that has been pasted into chat, logs, screenshots, or commits.MONGODB_DB: Database name, defaults tosoundfolio.LASTFM_API_KEYandLASTFM_USER: Required for ongoing scrobble sync.AUTH_KEY: Optional protection for/me.TIMEZONE: Recommended IANA timezone for hour/day buckets.
If you have an existing Neon/Postgres database from the old Prisma version:
POSTGRES_DATABASE_URL="postgresql://..." \
MONGODB_URI="mongodb+srv://..." \
MONGODB_DB="soundfolio" \
npm run db:migrate:postgresThe migration script bulk-upserts streams into MongoDB and preserves existing IDs, timestamps, artwork fields, and isDemo.
- Request Extended streaming history from Spotify account privacy.
- Upload the ZIP at
/history/import. - Connect Spotify to Last.fm in the Spotify app so new plays scrobble.
- Use Sync from Last.fm on the import page, or let the app run a lightweight background sync on load.
Backfill album artwork from iTunes, Last.fm, and Cover Art Archive. Backfill artist images from Discogs, Deezer, and Last.fm.
- Import the repo in Vercel.
- Set
MONGODB_URI,MONGODB_DB,LASTFM_API_KEY,LASTFM_USER, and optionalAUTH_KEY,TIMEZONE, Spotify vars, andCRON_SECRET. - Build command:
npm run build. - Run
npm run db:indexeslocally or from a trusted one-off environment after changing index definitions.
For cron-driven Spotify sync, schedule GET /api/sync or call POST /api/sync with Authorization: Bearer <CRON_SECRET>.
| Command | Description |
|---|---|
npm run dev |
Start the Next.js dev server. |
npm run build |
Production build. |
npm run db:indexes |
Create MongoDB indexes. |
npm run db:migrate:postgres |
One-time Postgres to MongoDB migration. |
npm run db:seed-demo |
Insert synthetic isDemo: true streams for local testing. |
npm run backfill-art |
CLI album artwork backfill. |
npm run backfill-artists |
CLI artist artwork backfill. |
npm run backfill-all |
Run both backfills. |
npm run clear-placeholder-art |
Clear known placeholder artwork URLs. |
Use npm run export-obsidian-stats to write a JSON snapshot from MongoDB-backed stats. For a live dashboard inside Obsidian, embed your deployed /me URL with a webview/custom frame plugin.