A simple web service for generating anonymous Spotify tokens.
- Overview
- How It Works
- Getting Started
- API Reference
- Automation and Secret Management
- Deployment
- Client Integration Examples
Spotify Web Player relies on time-based one-time password (TOTP) challenges to issue anonymous access tokens for client sessions. These tokens allow web clients to query metadata, search catalogs, and access player capabilities without requiring full user OAuth login.
This project automates the entire lifecycle:
- Scrapes the latest JavaScript bundles from the Spotify Web Player.
- Extracts and indexes versioned cryptographic secrets.
- Synchronizes with Spotify official server time.
- Generates valid 6-digit TOTP codes using Spotify customized cipher.
- Exchanges TOTP verification for access tokens via Spotify internal API.
- Exposes a clean, CORS-enabled REST API ready for serverless or standalone deployment.
+--------------------------+ 1. Scrape bundle URL +--------------------------+
| | -------------------------------> | |
| Secret Scraper Service | | Spotify Web Player |
| | <------------------------------- | (open.spotify.com) |
+--------------------------+ 2. Extract versioned secret +--------------------------+
|
v
+--------------------------+ 3. Fetch Server Time +--------------------------+
| | -------------------------------> | Spotify Server Time |
| Token Generator Core | <------------------------------- | (/api/server-time) |
| | 4. Return timestamp +--------------------------+
| |
| | 5. Request Token (TOTP) +--------------------------+
| | -------------------------------> | Spotify Token API |
| | <------------------------------- | (/api/token) |
+--------------------------+ 6. Return Access Token +--------------------------+
|
v
[ Client / App ]
- Secret Harvesting: The service inspects
https://open.spotify.com/to locate the activeweb-player.[hash].jsbundle and extracts the embedded{ secret, version }definitions. - Server Time Sync: It queries
https://open.spotify.com/api/server-timeto ensure clock drift does not invalidate the time counter. - TOTP Calculation: It applies a custom XOR byte transformation to the secret array, runs an HMAC-SHA1 hash over the 30-second time counter, and extracts the 6-digit code.
- Token Exchange: It performs a GET request to
https://open.spotify.com/api/tokenwith the generated TOTP parameters and returns the JSON payload.
The TOTP generation follows standard RFC 6238 time-step principles with Spotify proprietary secret obfuscation:
// Step 1: XOR transform on secret byte array
const transformed = secretArray.map((byte, idx) => byte ^ ((idx % 33) + 9));
// Step 2: Convert to byte buffer and calculate 30-second interval counter
const counter = Math.floor(serverTimestamp / 30);
const counterBuffer = Buffer.alloc(8);
counterBuffer.writeBigUInt64BE(BigInt(counter));
// Step 3: Compute HMAC-SHA1 signature
const hmac = crypto.createHmac('sha1', secretBytes).update(counterBuffer).digest();
// Step 4: Dynamic truncation to 6 digits
const offset = hmac[hmac.length - 1] & 0x0f;
const code = ((hmac[offset] & 0x7f) << 24) |
((hmac[offset + 1] & 0xff) << 16) |
((hmac[offset + 2] & 0xff) << 8) |
(hmac[offset + 3] & 0xff);
const totp = (code % 1000000).toString().padStart(6, '0');- Node.js 18.0.0 or higher (Node.js 20+ recommended)
- npm or yarn or pnpm
Clone the repository and install the dependencies:
git clone https://github.com/listune/tokener-spotify.git
cd tokener-spotify
npm installStart the local server in production mode:
npm startStart the local server with hot-reloading (Node.js watch mode):
npm run devBy default, the server listens on http://localhost:37353. You can customize the port by setting the PORT environment variable:
PORT=8080 npm startGenerates and returns an anonymous Spotify authentication token.
GET /api/getToken HTTP/1.1
Host: localhost:37353
Accept: application/json{
"clientId": "d8a5ed958d274c2e8ee717e6a4b0971d",
"accessToken": "BQC...[truncated]...",
"accessTokenExpirationTimestampMs": 1724058000000,
"isAnonymous": true
}{
"error": "Unable to extract Spotify secrets: Player JS URL not found in Spotify homepage"
}Returns the health status and current timestamp of the generator service.
GET /api/health HTTP/1.1
Host: localhost:37353{
"status": "ok",
"service": "tokener-spotify",
"timestamp": "2026-08-19T05:29:19.457Z"
}Returns general service metadata and available endpoint links.
{
"name": "tokener-spotify",
"description": "Spotify authentication token generator with TOTP verification",
"endpoints": {
"getToken": "GET /api/getToken",
"health": "GET /api/health"
}
}Spotify periodically rotates client bundle URLs and internal secrets. This repository includes an automated update pipeline:
Run the update script to scrape Spotify and update the JSON files in the secrets/ directory:
npm run update:secretsOr run via the script directly:
node scripts/update-secrets.jsThe workflow defined in .github/workflows/update-secrets.yml runs every hour (0 * * * *). It performs the following steps:
- Checks out the repository.
- Sets up Node.js 20.
- Installs dependencies.
- Executes the secret scraper.
- Commits and pushes any detected changes back to the main branch.
This repository is pre-configured for instant deployment on Vercel:
- Install the Vercel CLI or link the repository via the Vercel Dashboard:
npm i -g vercel vercel
- The
vercel.jsonfile automatically routes incoming requests to./index.jsusing@vercel/node. - Once deployed, the endpoint will be live at
https://your-vercel-domain.vercel.app/api/getToken.
npm install -g pm2
pm2 start src/server.js --name "tokener-spotify"Create a Dockerfile in the root folder:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 37353
CMD ["node", "src/server.js"]Build and run the container:
docker build -t tokener-spotify .
docker run -p 37353:37353 -d tokener-spotifycurl -s http://localhost:37353/api/getToken | jq .async function getSpotifyToken() {
const response = await fetch('http://localhost:37353/api/getToken');
if (!response.ok) {
throw new Error(`Failed to fetch token: ${response.status}`);
}
const data = await response.json();
console.log('Access Token:', data.accessToken);
return data;
}
getSpotifyToken();import requests
def get_spotify_token():
url = "http://localhost:37353/api/getToken"
response = requests.get(url)
response.raise_for_status()
data = response.json()
print("Access Token:", data.get("accessToken"))
return data
if __name__ == "__main__":
get_spotify_token()This project is intended strictly for educational, research, and authorized personal development purposes. It is not affiliated with, maintained by, or endorsed by Spotify AB. Use of the Spotify Web API and Spotify Web Player services is subject to Spotify Developer Terms of Service and applicable laws.