JSON API for Spotify top daily tracks data, scraped from Kworb.net and enriched with Spotify metadata.
Meww.me Top Chart is a JSON-only API service built with Next.js that scrapes Spotify daily track chart data from Kworb.net, enriches it with Spotify metadata (cover art, preview URL, Spotify link), and serves it through clean REST endpoints.
- Top Daily Tracks Scraped from Kworb.net for 20 countries + global
- Spotify Integration Automatic track matching with cover art, preview URLs, and Spotify links
- Historical Data Daily snapshots with rank changes
- Multi-Country Support Global + 19 country-specific charts
- Auto-Refresh Cron-based data refresh via Vercel or manual trigger
| Component | Technology |
|---|---|
| Framework | Next.js 14 (App Router) |
| Language | TypeScript |
| Database | MySQL (production) / PostgreSQL / SQLite (development) |
| ORM | Prisma |
| Scraping | Cheerio |
| API Source | Spotify Web API |
| Deployment | Vercel / Hostinger Node.js |
- Node.js 18+
- npm or yarn
- MySQL / PostgreSQL / SQLite database
- Spotify Developer app credentials
# Clone the repository
git clone https://github.com/lrmn7/mewwme-top-chart.git
cd mewwme-top-chart
# Install dependencies
npm install
# Copy environment variables
cp .env.example .envEdit .env with your credentials:
# Required
SPOTIFY_CLIENT_ID=your_spotify_client_id
SPOTIFY_CLIENT_SECRET=your_spotify_client_secret
DATABASE_URL="mysql://user:password@host:3306/database"
# Admin secret for triggering data refresh
ADMIN_SECRET=your_secret_here
# Countries to scrape (comma-separated)
SCRAPE_COUNTRIES=global,id,us,gb,jp,kr,de,fr,br,mx,in,au,es,it,ca,se,ph,tr,ar,nl
# Limits
TOP_TRACKS_LIMIT=25
# Optional: Rate limit rotation (add up to 3 Spotify client pairs)
# SPOTIFY_CLIENT_ID_2=second_client_id
# SPOTIFY_CLIENT_SECRET_2=second_client_secret
# Server port (for custom server)
PORT=3301Three schema variants are provided:
prisma/schema.prisma— MySQL (default)prisma/schema.postgresql.prisma— PostgreSQLprisma/schema.sqlite.prisma— SQLite (local dev)
To switch database, copy the desired schema to schema.prisma and update DATABASE_URL.
# Generate Prisma client
npx prisma generate
# Push schema to database
npx prisma db push
# (Optional) Open Prisma Studio to browse data
npx prisma studio# Development
npm run dev
# Production build
npm run build
npm start| Method | Endpoint | Description |
|---|---|---|
GET |
/api/stats/tracks |
Top daily tracks with streams, rank, Spotify metadata |
GET |
/api/stats/tracks/history |
Track stream/rank history |
GET |
/api/stats/countries |
List of supported countries |
GET |
/api/stats/last-updated |
Timestamp of last data refresh |
| Param | Default | Description |
|---|---|---|
country |
global |
Country code (e.g., id, us, gb) |
limit |
25 |
Number of results |
| Param | Default | Description |
|---|---|---|
track |
— | Track name (required) |
artist |
— | Artist name (required) |
country |
global |
Country code |
days |
30 |
Number of days of history |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/cron/refresh?secret=YOUR_SECRET |
Trigger data refresh |
POST |
/api/cron/refresh |
Trigger data refresh (JSON body) |
GET /api/stats/tracks?country=global&limit=2
{
"tracks": [
{
"trackId": "2plbrEY59IikOBgBGLjaoe",
"name": "Die With A Smile",
"mainArtistName": "Lady Gaga, Bruno Mars",
"rank": 1,
"previousRank": 1,
"rankDelta": 0,
"dailyStreams": 8500000,
"totalStreams": 3200000000,
"imageUrl": "https://i.scdn.co/image/...",
"previewUrl": "https://p.scdn.co/mp3-preview/...",
"spotifyUrl": "https://open.spotify.com/track/..."
}
]
}src/
├── app/
│ └── api/
│ ├── cron/refresh/ # Data refresh endpoint
│ └── stats/
│ ├── tracks/ # Top tracks API
│ ├── countries/ # Supported countries
│ └── last-updated/ # Last refresh timestamp
├── lib/
│ ├── db.ts # Prisma client singleton
│ ├── types.ts # TypeScript interfaces
│ ├── spotify/
│ │ ├── auth.ts # Multi-client Spotify auth with rotation
│ │ └── metadata.ts # Spotify metadata enrichment
│ ├── services/
│ │ └── statsProvider.ts # Core data aggregation service
│ └── scraping/
│ ├── kworbTracks.ts # Global top tracks scraper
│ ├── kworbCountry.ts # Multi-country chart scraper
│ ├── kworbIndonesia.ts # Indonesia-specific scraper
│ └── kworbScraper.ts # Base Kworb scraping utilities
├── prisma/
│ ├── schema.prisma # MySQL schema (primary)
│ ├── schema.mysql.prisma # MySQL variant
│ ├── schema.postgresql.prisma # PostgreSQL variant
│ └── schema.sqlite.prisma # SQLite variant
└── server.js # Custom server (Hostinger compatible)
Kworb.net ──scrape──▶ Raw Track Chart Data
│
▼
Spotify API
(cover art, URLs)
│
▼
statsProvider.ts
(merge & enrich)
│
▼
Prisma / Database
│
▼
JSON API Routes
- Scrape Kworb.net is scraped for top daily tracks (by daily streams) across 20 countries
- Enrich Each track is enriched with Spotify ID, cover art, preview URL, and Spotify link
- Store Data is persisted to the database with daily snapshots for historical tracking
- Serve Clean JSON APIs expose the data with filtering and country support
This project uses a hybrid architecture:
- GitHub Actions handles data scraping (runs on GitHub servers, no time limits)
- Vercel serves the JSON API (fast, serverless)
- MySQL database stores all track data (shared between both)
-
Fork or push this repo to your GitHub account
-
Go to Settings → Secrets and variables → Actions and add these secrets:
Secret Description Example DATABASE_URLMySQL connection string mysql://user:pass@host:3306/dbnameSPOTIFY_CLIENT_IDSpotify app client ID From Spotify Dashboard SPOTIFY_CLIENT_SECRETSpotify app client secret From Spotify Dashboard SCRAPE_COUNTRIESCountries to scrape global,id,us,gb,jp,krTOP_TRACKS_LIMITMax tracks per country 25ADMIN_SECRET(Optional) Secret for manual API refresh Any secure string Optional: Add
SPOTIFY_CLIENT_ID_2,SPOTIFY_CLIENT_SECRET_2, etc. for rate limit rotation -
Make sure your MySQL database is accessible from GitHub Actions (public endpoint or allowlisted IPs)
Run the workflow manually to populate your database for the first time:
- Go to Actions tab in your GitHub repo
- Click "Daily Stats Refresh" workflow on the left
- Click "Run workflow" → "Run workflow" (green button)
- Wait for it to complete (~2-5 minutes)
This will scrape all configured countries and store track data in your database.
-
Import your GitHub repo on vercel.com/new
-
Add the same environment variables in Vercel dashboard:
DATABASE_URLSPOTIFY_CLIENT_IDSPOTIFY_CLIENT_SECRETADMIN_SECRET
-
Deploy! Your API is now live at
https://your-project.vercel.app
After the initial setup:
- GitHub Actions automatically refreshes data twice daily at 06:00 and 18:00 UTC (configurable in
.github/workflows/refresh-cron.yml) - Vercel serves fresh data from the database via API routes
- You can trigger a manual refresh anytime from the GitHub Actions tab
┌─────────────────────────────────┐
│ GitHub Actions │
│ (cron: 06:00 & 18:00 UTC) │
│ │
│ 1. Scrape Kworb.net │
│ 2. Enrich via Spotify API │
│ 3. Write to MySQL │
└──────────────┬──────────────────┘
│ writes
▼
┌─────────────────────────────────┐
│ MySQL Database │
│ TrackCurrent + TrackSnapshot │
└──────────────┬──────────────────┘
│ reads
▼
┌─────────────────────────────────┐
│ Vercel (API) │
│ │
│ /api/stats/tracks │
│ /api/stats/tracks/history │
│ /api/stats/countries │
│ /api/stats/last-updated │
└──────────────┬──────────────────┘
│
▼
Discord Bot / Client
Edit .github/workflows/refresh-cron.yml to change the refresh schedule:
schedule:
- cron: '0 6,18 * * *' # 06:00 & 18:00 UTC (13:00 & 01:00 WIB)You can also trigger manually: Actions → Daily Stats Refresh → Run workflow
| Code | Country | Code | Country |
|---|---|---|---|
global |
🌍 Global | kr |
🇰🇷 South Korea |
us |
🇺🇸 United States | in |
🇮🇳 India |
gb |
🇬🇧 United Kingdom | au |
🇦🇺 Australia |
id |
🇮🇩 Indonesia | es |
🇪🇸 Spain |
jp |
🇯🇵 Japan | it |
🇮🇹 Italy |
de |
🇩🇪 Germany | ca |
🇨🇦 Canada |
fr |
🇫🇷 France | se |
🇸🇪 Sweden |
br |
🇧🇷 Brazil | ph |
🇵🇭 Philippines |
mx |
🇲🇽 Mexico | tr |
🇹🇷 Turkey |
nl |
🇳🇱 Netherlands | ar |
🇦🇷 Argentina |
npm run build
node server.jsThe custom server.js includes .htaccess self-healing for Apache-based hosting (LiteSpeed/Hostinger).
| Script | Description |
|---|---|
npm run dev |
Start development server |
npm run build |
Build for production |
npm start |
Start production server |
npm run lint |
Run ESLint |
npm run db:push |
Push Prisma schema to database |
npm run db:studio |
Open Prisma Studio |
node refresh-data.js |
Manual data refresh |
node check-data.js |
Check data counts in database |
This project is for personal/educational use.