GET /v1/recommendations is permanently deprecated for Spotify apps created after November 27, 2024 (returns 403). The working alternative: use GET /v1/me/top/tracks and GET /v1/me/top/artists to get seed data, then GET /v1/search with genre and artist keywords to discover similar tracks, and POST /v1/playlists/{id}/tracks to save results. This approach requires 10-20 search queries per session — throttle to 3 requests/second to stay within the rolling 30-second rate limit.
API Quick Reference
OAuth 2.0
Rolling 30-second window (exact threshold undisclosed)
JSON
Available
Understanding the Spotify Web API
The Spotify Web API is a REST API at https://api.spotify.com/v1. This page specifically addresses the most common broken-tutorial problem in the Spotify developer ecosystem: GET /v1/recommendations was permanently deprecated for apps created after November 27, 2024. If you've been following a tutorial that uses this endpoint, it will return HTTP 403 on any app you created after that date. Grandfathered Extended Quota apps from before that date still have access — which is why many tutorials still reference it.
What else was deprecated at the same time: GET /v1/audio-features, GET /v1/audio-analysis, GET /v1/artists/{id}/related-artists, GET /v1/browse/featured-playlists, GET /v1/browse/categories/{id}/playlists, and the 30-second preview_url field. These are all permanently unavailable for new apps.
The working alternative for building recommendation-like functionality uses a three-step approach: understand the user's taste via their top tracks and artists, extract genre signals and artist names, then use GET /v1/search with those signals as queries to discover similar catalog content. This is more manual than the old recommendations API but works for all apps. Official docs: https://developer.spotify.com/documentation/web-api
https://api.spotify.com/v1Setting Up Spotify API Authentication
This automation requires user-specific data (top tracks/artists) and the ability to create playlists. Use OAuth 2.0 Authorization Code flow with user-top-read and playlist-modify-private scopes. The access token expires in 3,600 seconds — implement token refresh using your stored refresh token before each run.
- 1Log in to https://developer.spotify.com/dashboard with your Spotify Premium account
- 2Click 'Create app', add a name, description, and set a redirect URI (e.g., http://localhost:8888/callback)
- 3Copy your Client ID and Client Secret from the app settings page
- 4Build the authorization URL with the required scopes: GET https://accounts.spotify.com/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REDIRECT_URI&scope=user-top-read%20playlist-modify-private
- 5After user authorization, extract the code from the redirect URL query parameter
- 6Exchange code for tokens: POST https://accounts.spotify.com/api/token with grant_type=authorization_code, code, redirect_uri, Authorization: Basic base64(client_id:client_secret)
- 7Store access_token and refresh_token securely in environment variables
- 8Refresh the token before each run: POST https://accounts.spotify.com/api/token with grant_type=refresh_token
1import os2import base643import requests45CLIENT_ID = os.environ['SPOTIFY_CLIENT_ID']6CLIENT_SECRET = os.environ['SPOTIFY_CLIENT_SECRET']7REFRESH_TOKEN = os.environ['SPOTIFY_REFRESH_TOKEN']89def get_access_token():10 creds = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()11 response = requests.post(12 'https://accounts.spotify.com/api/token',13 headers={'Authorization': f'Basic {creds}'},14 data={'grant_type': 'refresh_token', 'refresh_token': REFRESH_TOKEN}15 )16 response.raise_for_status()17 return response.json()['access_token']1819token = get_access_token()20headers = {'Authorization': f'Bearer {token}'}Security notes
- •Store Client ID, Client Secret, and refresh token in environment variables — never in source code
- •Refresh tokens are long-lived — encrypt at rest if storing in a database
- •Access tokens expire in 3,600 seconds — always refresh before each automation run
- •Only request user-top-read and playlist-modify-private — do not request public playlist scopes unless needed
- •Never log token values — log token refresh events only
- •In Development Mode, only 5 users can authorize your app — do not share credentials between test accounts
Key endpoints
/v1/me/top/tracksReturns the user's top tracks. Use short_term for current taste signals to seed the recommendation search. The track names and artist IDs here drive the search queries.
| Parameter | Type | Required | Description |
|---|---|---|---|
time_range | string | optional | short_term (~4 weeks), medium_term (~6 months), long_term (years). Use short_term for current taste. |
limit | number | optional | Number of items, 1-50 |
Response
1{"items":[{"id":"4iV5W9uYEdYUVa79Axb7Rh","name":"Everlong","artists":[{"id":"7jy3rLJdDQY21OgRLCZ9sD","name":"Foo Fighters"}],"album":{"name":"The Colour and the Shape"},"popularity":74,"uri":"spotify:track:4iV5W9uYEdYUVa79Axb7Rh"}],"total":50}/v1/me/top/artistsReturns the user's top artists with genre arrays. Genres are the primary seed signal for building search-based recommendations since audio features are deprecated.
| Parameter | Type | Required | Description |
|---|---|---|---|
time_range | string | optional | short_term, medium_term, or long_term |
limit | number | optional | Number of items, 1-50 |
Response
1{"items":[{"id":"7jy3rLJdDQY21OgRLCZ9sD","name":"Foo Fighters","genres":["alternative metal","alternative rock","grunge","post-grunge","rock"],"popularity":79}],"total":50}/v1/searchSearch the Spotify catalog for tracks matching genre, artist, or keyword queries. This is the core engine for discovery in the absence of the deprecated /recommendations endpoint.
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | required | Query string. Supports field filters: genre:, artist:, album:, year:. E.g., 'genre:grunge year:2020-2026' |
type | string | required | Must include 'track' for track results |
limit | number | optional | Results per query, 1-50 |
market | string | optional | ISO country code for availability filtering |
Response
1{"tracks":{"items":[{"id":"0VjIjW4GlUZAMYd2vXMi3b","name":"The Pretender","artists":[{"name":"Foo Fighters"}],"popularity":77,"uri":"spotify:track:0VjIjW4GlUZAMYd2vXMi3b","album":{"name":"Echoes, Silence, Patience & Grace","release_date":"2007-09-28"}}],"total":1200}}/v1/playlists/{playlist_id}/tracksAdd discovered tracks to a playlist. Use this to save the recommendation results as a 'Discover' playlist.
| Parameter | Type | Required | Description |
|---|---|---|---|
playlist_id | string | required | Spotify playlist ID to add tracks to |
uris | array | required | Array of Spotify track URIs, max 100 per request |
position | number | optional | Zero-based insert position, default appends to end |
Request
1{"uris":["spotify:track:0VjIjW4GlUZAMYd2vXMi3b","spotify:track:4iV5W9uYEdYUVa79Axb7Rh"]}Response
1{"snapshot_id":"MTY2LDljYzE0OTM4MjVkN2UxMjdmNzJiNGI4YjBiZmMxMzIxMzExYjM0ZQ=="}Step-by-step automation
Understand What Was Deprecated and Why
Why: Using GET /v1/recommendations on a new app will always return 403 — you need to know exactly what was removed to build the right alternative.
The following endpoints are permanently unavailable for apps created after November 27, 2024: GET /v1/recommendations, GET /v1/audio-features/{id} (and batch), GET /v1/audio-analysis/{id}, GET /v1/artists/{id}/related-artists, GET /v1/browse/featured-playlists, GET /v1/browse/categories/{id}/playlists, and the preview_url field. These are not temporary restrictions — they are permanent for new apps. Do not attempt to work around the 403 by changing app settings; it cannot be fixed for new apps. The alternative approach uses search queries seeded by the user's listening history.
1# This WILL return 403 for apps created after Nov 27, 2024:2# curl 'https://api.spotify.com/v1/recommendations?seed_genres=rock&limit=20' \3# -H 'Authorization: Bearer YOUR_TOKEN'45# Verify your app creation date at developer.spotify.com/dashboard6# If your app was created before Nov 27, 2024 AND has Extended Quota Mode, it still works.7# For all new apps, use the search-based approach shown in steps 2-4 below.Pro tip: If you see tutorials using /recommendations dated 2024 or later, check if the author notes they have a 'grandfathered Extended Quota app'. Those tutorials work for them but will 403 for you.
Expected result: Clear understanding of which endpoints are unavailable and the working alternative flow.
Extract Genre and Artist Seeds from Listening History
Why: The user's top genres and artists become the search query seeds for discovery — this is the direct replacement for the recommendations endpoint's seed_genres and seed_artists parameters.
Fetch the user's top artists using short_term time range. Extract the genre arrays from each artist and count frequency across the top 20 artists to find dominant genres. Also collect the top 5 artist names as search seeds. These will be used to construct targeted search queries in the next step.
1curl -G 'https://api.spotify.com/v1/me/top/artists' \2 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \3 -d 'time_range=short_term' \4 -d 'limit=20'Pro tip: Use weighted genre scoring where top-ranked artists contribute more to the genre score than lower-ranked ones. This prevents a single artist with unusual genre tags from dominating the discovery queries.
Expected result: A list of top genres (e.g., ['alternative rock', 'post-grunge', 'grunge']), top artist names, and a set of already-heard track IDs to exclude from recommendations.
Search Catalog Using Genre and Artist Seeds
Why: Multiple targeted search queries across different genres produce a diverse pool of discovery candidates.
For each top genre, run a GET /v1/search?q=genre:{genre}&type=track&limit=20 query. Also run artist-based queries like genre:{artist_name_genre}. This produces a pool of 100-200 candidate tracks. Add 333ms delays between requests to stay at 3 requests/second. Filter out tracks already in the user's top tracks (the already_heard_ids set from the previous step) and deduplicate by track ID.
1# Search by genre2curl -G 'https://api.spotify.com/v1/search' \3 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \4 --data-urlencode 'q=genre:alternative rock year:2022-2026' \5 -d 'type=track' \6 -d 'limit=20' \7 -d 'market=US'89# Search by artist similarity10curl -G 'https://api.spotify.com/v1/search' \11 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \12 --data-urlencode 'q=artist:Foo Fighters genre:post-grunge' \13 -d 'type=track' \14 -d 'limit=20' \15 -d 'market=US'Pro tip: Popularity scores (0-100) reflect recent global streaming, not your personal affinity. Mix high-popularity (>60) and mid-popularity (40-60) tracks to balance chart hits with more niche discoveries that match your genre taste.
Expected result: A deduplicated pool of candidate tracks not in the user's recent listening history, sorted by popularity.
Save Recommendations to a Discover Playlist
Why: Creating a 'Discover' playlist gives the user a persistent, playable destination for the recommended tracks.
Get the user ID from GET /v1/me, then create or update a 'Discover Weekly (Manual)' private playlist. Use PUT /v1/playlists/{id}/tracks to replace tracks on existing playlists, or POST /v1/users/{user_id}/playlists followed by POST /v1/playlists/{id}/tracks for new ones. Log the playlist URL so the user can find it.
1# Create playlist2curl -X POST 'https://api.spotify.com/v1/users/YOUR_USER_ID/playlists' \3 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \4 -H 'Content-Type: application/json' \5 -d '{"name":"Discover (Manual)","description":"Search-based recommendations from your taste profile","public":false}'67# Add tracks (replace YOUR_PLAYLIST_ID and track URIs)8curl -X POST 'https://api.spotify.com/v1/playlists/YOUR_PLAYLIST_ID/tracks' \9 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \10 -H 'Content-Type: application/json' \11 -d '{"uris":["spotify:track:0VjIjW4GlUZAMYd2vXMi3b","spotify:track:4iV5W9uYEdYUVa79Axb7Rh"]}'Pro tip: Use PUT (replace) rather than POST (append) when updating an existing playlist — this keeps the playlist fresh and prevents it from growing to hundreds of tracks over time.
Expected result: A private 'Discover (Manual)' playlist created or updated with the recommended tracks. Console output includes the Spotify playlist URL.
Complete working code
This complete script runs the full search-based recommendation pipeline: refreshes OAuth token, fetches listening history for seeds, runs genre and artist searches, deduplicates and filters out heard tracks, and saves a 30-track 'Discover' playlist. This is the working alternative to the deprecated GET /v1/recommendations endpoint.
1import os2import base643import time4import requests5from collections import Counter67CLIENT_ID = os.environ['SPOTIFY_CLIENT_ID']8CLIENT_SECRET = os.environ['SPOTIFY_CLIENT_SECRET']9REFRESH_TOKEN = os.environ['SPOTIFY_REFRESH_TOKEN']10PLAYLIST_NAME = os.environ.get('DISCOVER_PLAYLIST_NAME', 'Discover (Manual)')1112def get_token():13 creds = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()14 r = requests.post('https://accounts.spotify.com/api/token',15 headers={'Authorization': f'Basic {creds}'},16 data={'grant_type': 'refresh_token', 'refresh_token': REFRESH_TOKEN})17 r.raise_for_status()18 return r.json()['access_token']1920def api_get(token, path, params=None):21 r = requests.get(f'https://api.spotify.com/v1{path}',22 headers={'Authorization': f'Bearer {token}'}, params=params)23 if r.status_code == 429:24 time.sleep(int(r.headers.get('Retry-After', 5)))25 return api_get(token, path, params)26 r.raise_for_status()27 return r.json()2829def api_post(token, path, payload):30 r = requests.post(f'https://api.spotify.com/v1{path}',31 headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'},32 json=payload)33 r.raise_for_status()34 return r.json()3536def main():37 token = get_token()3839 # Get seed data from listening history40 top_artists = api_get(token, '/me/top/artists', {'time_range': 'short_term', 'limit': 20})['items']41 top_tracks = api_get(token, '/me/top/tracks', {'time_range': 'medium_term', 'limit': 50})['items']42 already_heard = {t['id'] for t in top_tracks}4344 # Extract genres (weighted by rank)45 genre_scores = Counter()46 for i, a in enumerate(top_artists):47 for g in a['genres']:48 genre_scores[g] += (20 - i)49 top_genres = [g for g, _ in genre_scores.most_common(5)]50 top_artist_names = [a['name'] for a in top_artists[:3]]51 print(f'Seeds: genres={top_genres[:3]}, artists={top_artist_names}')5253 # Build search queries54 queries = [f'genre:{g} year:2020-2026' for g in top_genres]55 queries += [f'artist:"{n}"' for n in top_artist_names]5657 # Run searches with rate limiting58 candidates = {}59 for q in queries:60 results = api_get(token, '/search', {'q': q, 'type': 'track', 'limit': 20, 'market': 'US'})61 for track in results['tracks']['items']:62 if track['id'] not in already_heard:63 candidates[track['id']] = track64 time.sleep(0.333)6566 # Select top 30 by popularity67 recommended = sorted(candidates.values(), key=lambda t: t['popularity'], reverse=True)[:30]68 print(f'Selected {len(recommended)} tracks')6970 # Create or update playlist71 user_id = api_get(token, '/me')['id']72 playlists = api_get(token, '/me/playlists', {'limit': 50})['items']73 existing = next((p for p in playlists if p['name'] == PLAYLIST_NAME), None)74 uris = [t['uri'] for t in recommended]7576 if existing:77 requests.put(f'https://api.spotify.com/v1/playlists/{existing["id"]}/tracks',78 headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'},79 json={'uris': uris}).raise_for_status()80 print(f'Updated: https://open.spotify.com/playlist/{existing["id"]}')81 else:82 p = api_post(token, f'/users/{user_id}/playlists', {83 'name': PLAYLIST_NAME, 'public': False, 'description': 'Search-based discovery from your taste'})84 api_post(token, f'/playlists/{p["id"]}/tracks', {'uris': uris})85 print(f'Created: {p["external_urls"]["spotify"]}')8687if __name__ == '__main__':88 main()Error handling
{"error":{"status":403,"message":"Player command failed: Premium required"}}Most commonly: calling GET /v1/recommendations or GET /v1/audio-features on an app created after November 27, 2024. These are permanently deprecated for new apps. Also returned when trying to access playback controls without Premium.
Remove all calls to /recommendations, /audio-features, /audio-analysis, /artists/{id}/related-artists from your code. Use the search-based approach documented in this guide. There is no workaround or app setting that restores these endpoints for new apps.
Not retryable — the endpoint is permanently unavailable for new apps.
{"error":{"status":401,"message":"No token provided"}}Access token expired (3,600 second lifetime) or was not included in the request.
Refresh the access token using the refresh_token grant before each automation run.
Refresh immediately and retry once.
{"error":{"status":429,"message":"API rate limit exceeded"}}The rolling 30-second window threshold was exceeded. Running 10-20 search queries in rapid succession can trigger this.
Add 333ms delays between search requests (3 per second). Honor the Retry-After header when 429 is received.
Read Retry-After header and wait exactly that many seconds. Then resume with delays between requests.
{"error":{"status":400,"message":"Bad Request"}}Malformed search query, invalid track URIs in playlist add request, or missing required parameters.
Validate track URIs before adding to playlists — they must be in format 'spotify:track:{id}'. URL-encode search query strings. Do not include null values in the uris array.
Not retryable — fix the request.
Rate Limits for Spotify API
| Scope | Limit | Window |
|---|---|---|
| Per app | Undisclosed exact threshold | Rolling 30 seconds |
| Search queries (practical guidance) | 3 requests per second (conservative) | Add 333ms delay between search calls |
| Playlist track add | 100 URIs per request | Per API call |
1import time23def search_with_retry(token, query, max_retries=3):4 import requests5 for attempt in range(max_retries):6 r = requests.get('https://api.spotify.com/v1/search',7 headers={'Authorization': f'Bearer {token}'},8 params={'q': query, 'type': 'track', 'limit': 20, 'market': 'US'})9 if r.status_code == 429:10 wait = int(r.headers.get('Retry-After', 2 ** attempt))11 print(f'Rate limited, waiting {wait}s...')12 time.sleep(wait)13 elif r.ok:14 return r.json()['tracks']['items']15 else:16 r.raise_for_status()17 return []- Throttle search queries to 3 per second (333ms between requests) to avoid hitting the rolling rate limit
- Cache search results for a given genre set — running the same genre queries daily produces similar results and wastes API calls
- Limit total search queries per run to 10-15 — more queries produce diminishing returns on discovery diversity
- Honor the Retry-After header on every 429 response — do not implement fixed sleep durations
- Add exponential backoff for consecutive 429 responses: 1s, 2s, 4s, 8s
Security checklist
- Store Client ID, Client Secret, and refresh token in environment variables — never in code or version control
- Only request user-top-read and playlist-modify-private scopes — do not request broader permissions
- Refresh tokens are long-lived — encrypt at rest if persisted in a database
- Never use Client Credentials flow for this automation — it cannot access user listening history
- Log automation runs for audit trail but never log token values
- In Development Mode, only the 5 authorized users can generate recommendations — do not expose the OAuth flow publicly
- Validate track URIs before adding to playlists to prevent malformed request injection
Automation use cases
Weekly Discover Playlist
intermediateRun every Monday to refresh a 'Discover (Manual)' playlist with tracks matching the user's current short-term taste.
Genre-Specific Discovery
intermediateInstead of using the user's top genres, allow a user to input specific genres manually and generate a discovery playlist for that specific sound.
Collaborative Discovery
advancedMerge genre seeds from two users (both authorized with the 5-user Development Mode limit) and create a shared discovery playlist with tracks that match both taste profiles.
Mood-Based Search
intermediateAdd mood keywords (e.g., 'focus', 'workout', 'sleep') to genre searches for mood-specific discovery playlists without audio features data.
No-code alternatives
Don't want to write code? These platforms can automate the same workflows visually.
Zapier
Free tier available; paid plans from $19.99/monthZapier's Spotify integration can trigger on listening events and search for tracks, though multi-step recommendation logic requires a complex multi-action Zap.
- + No code required
- + Easy playlist creation from search results
- + Connects to 7,000+ other apps
- - Complex multi-step recommendation logic is difficult to express in Zap format
- - Free tier limited to 100 tasks/month
- - No native genre-seed discovery flow
Make
Free tier available; paid plans from $9/monthMake's HTTP modules can replicate this exact search-based discovery pipeline with genre seeds from the Spotify node's top artists endpoint.
- + Visual scenario builder for multi-step flows
- + HTTP Request module for full API access
- + Free tier includes 1,000 operations/month
- - More complex to set up than a Python script
- - Genre aggregation logic requires custom functions
- - Rate limiting management is less precise than code
n8n
Self-hosted free; cloud from €20/monthn8n's Spotify node combined with Code nodes and HTTP Request modules can replicate the full search-based recommendation pipeline.
- + Self-hosted = free unlimited executions
- + Code node allows full JavaScript logic for genre aggregation
- + Open source with active community
- - Server setup required for self-hosting
- - More complex to maintain than a simple script
Best practices
- Be explicit in your app about the fact that /recommendations is deprecated — users who research this topic will encounter broken tutorials and appreciate clarity
- Prioritize genre-based searches over artist-based searches for discovery — artist searches tend to return the same well-known tracks the user already knows
- Filter out tracks from your top tracks (medium_term) not just recently played — medium_term covers 6 months and prevents re-recommending familiar favorites
- Mix popularity ranges in your selection algorithm: take 15 high-popularity tracks (score >65) and 15 mid-popularity tracks (40-65) for a balance of popular and niche discoveries
- Add a year filter to genre searches (e.g., year:2020-2026) to prioritize contemporary releases over decades-old catalog tracks
- Validate that all track URIs are in the correct format (spotify:track:{22-char-id}) before adding to playlists — malformed URIs cause silent failures
Ask AI to help
Copy one of these prompts to get a personalized, working implementation.
I'm building a Spotify song recommendation system without using the deprecated /recommendations endpoint. My approach: I fetch the user's top artists via GET /v1/me/top/artists (short_term, limit 20), extract weighted genre scores, then run GET /v1/search?q=genre:{genre}&type=track queries for the top 5 genres. I'm getting mostly mainstream chart tracks even for niche genres. How can I improve the search query strategy to surface less-discovered tracks that still match the user's genre preferences? Can you suggest modifications to the search query format or filtering approach that would produce more diverse recommendations?
Build a Spotify Discover tool that replaces the deprecated recommendations endpoint. The UI should have: 1) Spotify OAuth login with user-top-read and playlist-modify-private scopes, 2) After login, show the user's top 5 genres and top 3 artists from their short-term listening history as 'taste seeds', 3) A 'Generate Recommendations' button that searches Spotify using these seeds via GET /v1/search, filters out already-known tracks, and shows 20-30 results in a card grid with album art, track name, artist, and popularity score, 4) Checkboxes on each track for manual selection, 5) A 'Save to Playlist' button that creates a private 'Discover (Manual)' playlist with the selected tracks. Add a note explaining that this is a manual alternative to the deprecated Recommendations API.
Frequently asked questions
Why does GET /v1/recommendations return 403?
GET /v1/recommendations was permanently deprecated for Spotify apps created after November 27, 2024. If your app was created after that date, this endpoint will always return HTTP 403 and there is no workaround. Apps that existed before that date with Extended Quota Mode are grandfathered. The working alternative is to use GET /v1/me/top/artists to extract genre seeds, then run GET /v1/search queries with those genres to discover tracks.
What other Spotify endpoints are deprecated for new apps?
As of November 27, 2024, these endpoints return 403 for new apps: GET /v1/recommendations, GET /v1/audio-features/{id} (and batch), GET /v1/audio-analysis/{id}, GET /v1/artists/{id}/related-artists, GET /v1/browse/featured-playlists, GET /v1/browse/categories/{id}/playlists. The preview_url field is also no longer populated. Additionally, as of February 2026, Development Mode apps have restrictions on artist popularity scores, follower counts, top-tracks per artist, and new-releases browsing.
Is the Spotify API free?
Yes — the Spotify Web API is free to use. Development Mode limits you to 5 authorized users per Client ID (as of February 11, 2026) and requires the app owner to have Spotify Premium. Extended Quota Mode for production apps requires a legally registered organization with 250,000+ monthly active users as of May 2025.
What happens when I hit the rate limit?
You receive HTTP 429 with a Retry-After header in seconds. The search-based recommendation approach uses 10-20 search queries per run, which can hit the rate limit if run too quickly. Add 333ms delays between search requests (3 per second) and honor the Retry-After header when 429 is received.
Can I use audio features (tempo, energy, danceability) to filter recommendations?
No — GET /v1/audio-features is deprecated for apps created after November 27, 2024 and returns 403. You cannot access tempo, energy, danceability, valence, or any other audio analysis properties. The primary filtering signals available to new apps are: genre (from artist data), popularity score (proxy for mainstream-ness), and release year (from album data).
Can RapidDev help build a custom Spotify recommendation engine?
Yes — RapidDev has built 600+ integrations and can design a production recommendation pipeline using the search-based approach that works for all Spotify apps. We can also integrate third-party audio analysis APIs (like Tunebat or AcousticBrainz) as replacements for the deprecated audio features. Reach out at rapidevelopers.com for a free consultation.
Are there third-party alternatives for audio features data since Spotify deprecated it?
Yes — several options exist: Tunebat.com offers a paid API with BPM, key, and energy data for Spotify tracks. AcousticBrainz (open-source, though the main service is shutting down) provided similar data. Some developers use the Essentia library locally to analyze audio files. None are as convenient as Spotify's native audio features API was, but they fill the gap for apps that need acoustic property data.
Need this automated?
Our team has built 600+ apps with API automations. We can build this for you.
Book a free consultation