Spotify's API is one of the most generous free APIs available. You get access to 100 million tracks, audio features, recommendations, and playlist data — all for free.
Here's how to build a music discovery tool in 50 lines.
Setup (2 Minutes)
- Go to developer.spotify.com
- Create an app (free)
- Copy your Client ID and Client Secret
pip install httpx
Authentication
Spotify uses Client Credentials flow for public data:
import httpx
import base64
CLIENT_ID = 'your_client_id'
CLIENT_SECRET = 'your_client_secret'
def get_token():
auth = base64.b64encode(f'{CLIENT_ID}:{CLIENT_SECRET}'.encode()).decode()
r = httpx.post('https://accounts.spotify.com/api/token',
headers={'Authorization': f'Basic {auth}'},
data={'grant_type': 'client_credentials'}
)
return r.json()['access_token']
token = get_token()
headers = {'Authorization': f'Bearer {token}'}
Search for Artists
def search_artist(name):
r = httpx.get('https://api.spotify.com/v1/search',
headers=headers,
params={'q': name, 'type': 'artist', 'limit': 1}
)
artist = r.json()['artists']['items'][0]
return {
'name': artist['name'],
'id': artist['id'],
'followers': artist['followers']['total'],
'genres': artist['genres'],
'popularity': artist['popularity']
}
print(search_artist('Radiohead'))
# {'name': 'Radiohead', 'followers': 11M, 'genres': ['art rock', ...], 'popularity': 79}
Get Top Tracks
def top_tracks(artist_id, market='US'):
r = httpx.get(f'https://api.spotify.com/v1/artists/{artist_id}/top-tracks',
headers=headers,
params={'market': market}
)
return [{
'name': t['name'],
'album': t['album']['name'],
'popularity': t['popularity'],
'preview_url': t['preview_url']
} for t in r.json()['tracks'][:5]]
Get Audio Features (The Cool Part)
Spotify analyzes every track and provides:
- danceability — how suitable for dancing (0-1)
- energy — intensity and activity (0-1)
- valence — musical positivity (0-1)
- tempo — BPM
- acousticness — acoustic vs electronic (0-1)
def audio_features(track_id):
r = httpx.get(f'https://api.spotify.com/v1/audio-features/{track_id}',
headers=headers
)
f = r.json()
return {
'danceability': f['danceability'],
'energy': f['energy'],
'valence': f['valence'],
'tempo': f['tempo'],
'acousticness': f['acousticness']
}
Discover Similar Artists
def discover(artist_name):
"""Find similar artists with different vibes."""
artist = search_artist(artist_name)
r = httpx.get(f'https://api.spotify.com/v1/artists/{artist["id"]}/related-artists',
headers=headers
)
related = r.json()['artists'][:10]
return [{
'name': a['name'],
'genres': a['genres'][:3],
'popularity': a['popularity'],
'followers': f"{a['followers']['total']:,}"
} for a in related]
# Find artists similar to Radiohead
for artist in discover('Radiohead'):
print(f"{artist['name']} ({artist['popularity']}/100) — {', '.join(artist['genres'])}")
Get Recommendations
def get_recommendations(seed_artists=None, seed_genres=None, **kwargs):
params = {'limit': 10}
if seed_artists:
params['seed_artists'] = ','.join(seed_artists)
if seed_genres:
params['seed_genres'] = ','.join(seed_genres)
params.update(kwargs) # target_energy=0.8, min_tempo=120, etc.
r = httpx.get('https://api.spotify.com/v1/recommendations',
headers=headers, params=params
)
return [{'name': t['name'], 'artist': t['artists'][0]['name']}
for t in r.json()['tracks']]
# High energy dance tracks
recs = get_recommendations(
seed_genres=['electronic', 'dance'],
target_energy=0.9,
target_danceability=0.8,
min_tempo=120
)
Rate Limits
- Free tier: ~180 requests/minute
- No API key cost
- Rate limit headers in response
What You Can Build
- Mood-based playlist generator
- Artist similarity explorer
- Music taste analyzer
- Genre evolution tracker
- DJ set builder (match BPM/key)
More API Tutorials
- arXiv API: Search 2M+ Papers — Research tool
- Awesome Free Research APIs — 200+ APIs
- 7 APIs Worth Thousands
What would you build with the Spotify API? Mood playlists? Music analytics? Something else? 👇
API tutorials at dev.to/0012303
More from me: 10 Dev Tools I Use Daily | 77 Scrapers on a Schedule | 150+ Free APIs
Need web scraping or data extraction? I've built 77+ production scrapers. Email spinov001@gmail.com — quote in 2 hours. Or try my ready-made Apify actors — no code needed.
Top comments (2)
really cool tutorial, the audio features endpoint is the hidden gem here imo. being able to pull stuff like acousticness and valence opens up some interesting possibilities for building mood-based playlists.
one thing I've been messing with lately is using the related artists endpoint to map out genre connections — like starting from a folk artist and seeing how many hops it takes to get to metal lol. the recommendation seeds are powerful for that kind of exploration too.
quick question — have you run into any issues with the audio features endpoint returning null for certain tracks? I noticed some newer releases sometimes don't have that data populated right away. curious if you found a workaround or if it's just a timing thing on Spotify's end.
Your question has aged into a much bigger answer, and it is bad news for the post rather than for you.
I went and checked Spotify's own reference pages today. Four of the endpoints that tutorial leans on are now marked Deprecated in their docs: Get Track's Audio Features, Get Track's Audio Analysis, Get Recommendations, and Get Artist's Related Artists. Get Track itself is still fine. So the nulls you were hitting were not only new releases lagging behind — you were looking at an endpoint already on its way out, and the "hidden gem" framing in my post has not aged well. Same goes for the related-artists genre-hopping idea, which is a genuinely nicer use of that data and sits on the same list. Sorry. That one deserved to survive.
On the original nulls, for what it is worth: I never found a real workaround. What I settled on was treating a missing feature vector as a normal state rather than an error — skip the track, count the skip, print the count per run. If the miss rate moves, you have learned something about the endpoint instead of about your parsing. The version I regret is filling the gap with a neighbouring track's values to keep the playlist logic tidy, because that hides exactly the signal you would want to see.
Post flagged for correction. Thanks for asking a question specific enough to be checkable 165 days later.