Automate Spotify release tracking by polling GET /v1/me/following?type=artist for followed artists, then checking GET /v1/artists/{id}/albums for new releases. Spotify has NO webhooks — polling is the only option. Critical: GET /v1/artists/{id}/related-artists is permanently deprecated for new apps (returns 403). Rate limits use a rolling 30-second window; monitoring 50+ artists means 50+ sequential requests, so add 200ms delays between calls.
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 that provides access to music catalog data, user profiles, listening history, and artist metadata. It uses OAuth 2.0 for authentication and returns JSON. For tracking artist releases, you primarily use the following endpoint, artist albums, and user following endpoints.
A critical limitation for new apps: the GET /v1/artists/{id}/related-artists endpoint was permanently deprecated on November 27, 2024 and returns HTTP 403 for apps created after that date. Existing Extended Quota apps from before that date are grandfathered. Additionally, as of the February 2026 Development Mode restrictions, artist followers count and artist popularity are restricted for Development Mode apps — these fields may be absent or return 0. Build your automation around release_date and album metadata instead.
Spotify has no webhook system. This means release tracking must be built as a polling automation — you check for new content on a schedule rather than receiving push notifications. Official docs: https://developer.spotify.com/documentation/web-api
https://api.spotify.com/v1Setting Up Spotify API Authentication
Artist tracking requires OAuth 2.0 Authorization Code flow because you need access to the user's followed artists (a user-specific resource). Client Credentials flow only covers public catalog data. You need the user-follow-read scope to call GET /v1/me/following. Access tokens expire in 3,600 seconds — implement refresh token handling from the start.
- 1Log in at 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
- 4Build the authorization URL: GET https://accounts.spotify.com/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REDIRECT_URI&scope=user-follow-read
- 5After user authorization, you receive ?code=AUTH_CODE at your redirect URI
- 6Exchange the code: POST https://accounts.spotify.com/api/token with grant_type=authorization_code, code, redirect_uri; use Authorization: Basic base64(client_id:client_secret)
- 7Store access_token (expires in 3600s) and refresh_token securely in environment variables
- 8Refresh via POST https://accounts.spotify.com/api/token with grant_type=refresh_token before each scheduled run
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 """Refresh and return a new Spotify access token."""11 creds = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()12 response = requests.post(13 'https://accounts.spotify.com/api/token',14 headers={'Authorization': f'Basic {creds}'},15 data={'grant_type': 'refresh_token', 'refresh_token': REFRESH_TOKEN}16 )17 response.raise_for_status()18 return response.json()['access_token']1920token = get_access_token()21headers = {'Authorization': f'Bearer {token}'}Security notes
- •Store Client ID, Client Secret, and refresh token in environment variables — never in source code or version control
- •The refresh token is long-lived — protect it as carefully as a password; if compromised, revoke via the Spotify dashboard
- •Access tokens expire after exactly 3,600 seconds — refresh before each scheduled run rather than waiting for a 401
- •Only request user-follow-read scope; do not request write scopes you don't need
- •Log token refresh events for audit trail but never log the token value itself
- •In Development Mode, only the 5 authorized users' following lists are accessible — do not add unauthorized test accounts
Key endpoints
/v1/me/followingReturns the current user's followed artists or users. Use type=artist to get followed artists, paginated with after cursor. Essential first step for building a release radar.
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | required | Must be 'artist' for artist following data |
limit | number | optional | Number of results, 1-50, default 20 |
after | string | optional | Artist ID cursor for pagination — use the after value from the previous response's cursors object |
Response
1{"artists":{"items":[{"id":"0OdUWJ0sBjDrqHygGUXeCF","name":"Band of Horses","genres":["indie rock","folk rock"],"popularity":58,"followers":{"total":890000}},{"id":"1Cs0zKBU1kc0i8ypK3B9ai","name":"Fleet Foxes","genres":["chamber pop","indie folk"],"popularity":67}],"next":"https://api.spotify.com/v1/me/following?type=artist&after=1Cs0zKBU1kc0i8ypK3B9ai&limit=20","total":47,"cursors":{"after":"1Cs0zKBU1kc0i8ypK3B9ai"}}}/v1/artists/{id}/albumsReturns an artist's discography filtered by album type. Use include_groups=album,single to get releases and check release_date against your last sync timestamp.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | required | Spotify artist ID |
include_groups | string | optional | Comma-separated filter: album, single, appears_on, compilation |
market | string | optional | ISO 3166-1 alpha-2 country code to filter availability |
limit | number | optional | Number of albums per page, 1-50 |
offset | number | optional | Pagination offset |
Response
1{"items":[{"id":"382ObEPsp2rxGrnsizN5TX","name":"Is There a Ghost","release_date":"2024-09-15","release_date_precision":"day","album_type":"single","artists":[{"name":"Band of Horses"}],"external_urls":{"spotify":"https://open.spotify.com/album/382ObEPsp2rxGrnsizN5TX"}}],"total":42,"next":null}/v1/artists/{id}Returns full artist metadata including name, genres, and external URLs. Use this to enrich your database record when you detect a new release.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | required | Spotify artist ID |
Response
1{"id":"0OdUWJ0sBjDrqHygGUXeCF","name":"Band of Horses","genres":["indie rock","folk rock"],"popularity":58,"external_urls":{"spotify":"https://open.spotify.com/artist/0OdUWJ0sBjDrqHygGUXeCF"},"images":[{"url":"https://i.scdn.co/image/ab6761610000e5eb...","height":640,"width":640}]}/v1/albums/{id}Returns full album metadata including tracklist and release information. Use after detecting a new release to store complete album data.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | required | Spotify album ID |
market | string | optional | ISO 3166-1 alpha-2 country code |
Response
1{"id":"382ObEPsp2rxGrnsizN5TX","name":"Is There a Ghost","release_date":"2024-09-15","release_date_precision":"day","label":"Sub Pop","tracks":{"items":[{"id":"7lEptt4wbM0yJTyqhLXsIV","name":"Is There a Ghost","duration_ms":218400,"track_number":1}],"total":1}}Step-by-step automation
Fetch All Followed Artists with Cursor Pagination
Why: A user can follow hundreds of artists — the API returns max 50 per page using cursor-based pagination, not offset.
Call GET /v1/me/following?type=artist&limit=50 and paginate using the cursors.after value from each response until next is null. Note that this endpoint uses cursor-based pagination (the after parameter) rather than offset-based pagination. Collect all artist IDs into a list for the next step.
1# First page2curl -G 'https://api.spotify.com/v1/me/following' \3 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \4 -d 'type=artist' \5 -d 'limit=50'67# Next page using cursor8curl -G 'https://api.spotify.com/v1/me/following' \9 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \10 -d 'type=artist' \11 -d 'limit=50' \12 -d 'after=LAST_ARTIST_ID_FROM_CURSORS'Pro tip: Store this list in your database with a last_checked timestamp. On subsequent runs, you only need to re-fetch following if you expect the user to have followed new artists since the last sync.
Expected result: A flat array of artist objects, each with id, name, genres, and external_urls.spotify.
Check Each Artist for New Releases
Why: New releases must be detected by comparing release_date against your last sync timestamp — there is no event-based notification.
For each artist ID, call GET /v1/artists/{id}/albums?include_groups=album,single&limit=10&market=US. You only need the most recent 10 releases in most cases — Spotify returns them newest-first. Parse release_date carefully: the release_date_precision field tells you whether the date is year-precision (e.g., '2024'), month-precision ('2024-09'), or day-precision ('2024-09-15'). Add 200ms between each artist request to avoid hitting rate limits when monitoring large following lists.
1curl -G 'https://api.spotify.com/v1/artists/0OdUWJ0sBjDrqHygGUXeCF/albums' \2 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \3 -d 'include_groups=album,single' \4 -d 'limit=10' \5 -d 'market=US'Pro tip: Store the last_run timestamp in a file or database. On the next run, use that timestamp as your since_date to avoid re-processing releases you've already logged.
Expected result: A list of albums/singles released after your since_date, each with id, name, release_date, release_date_precision, album_type, and the artist's name.
Sync Artist Metadata to Your Database
Why: The following endpoint returns limited artist data — fetch full profiles for newly discovered or stale artist records.
For each artist you're tracking, call GET /v1/artists/{id} to retrieve genres, image URLs, and external_urls. Only fetch artists you don't already have in your database, or those whose records are older than a week. Note: in Development Mode, popularity scores and follower counts for artist profiles may be restricted or return 0 as of the February 2026 restrictions.
1curl 'https://api.spotify.com/v1/artists/0OdUWJ0sBjDrqHygGUXeCF' \2 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN'Pro tip: Store artist genre arrays in your database — this is the primary data point you can still reliably use for discovery and recommendations since audio features are deprecated for new apps.
Expected result: Artist object with name, genres array, image URLs, and Spotify profile URL. Popularity and follower counts may be unavailable in Development Mode.
Send Notifications for New Releases
Why: The automation's value is delivering timely alerts when tracked artists release new music.
After detecting new releases, send notifications via your preferred channel — email, Slack, webhook, or write to a database or spreadsheet. Format the notification with artist name, release title, release date, album type (album vs. single), and the Spotify URL. Log the current timestamp as your last_sync marker so the next run only checks for newer releases.
1# Send to a Slack webhook (replace with your actual webhook URL)2curl -X POST 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL' \3 -H 'Content-Type: application/json' \4 -d '{"text":"New release: Band of Horses - Is There a Ghost (2024-09-15)\nhttps://open.spotify.com/album/382ObEPsp2rxGrnsizN5TX"}'Pro tip: Send a 'no new releases' confirmation message as well — it helps you verify the automation ran successfully even when there's nothing new to report.
Expected result: Notifications delivered to your chosen channel. Last sync timestamp saved for the next run.
Complete working code
This script runs the complete release radar pipeline: refresh OAuth token, fetch all followed artists, check each for new releases since the last sync, enrich with artist metadata, and send a Slack notification with the results. Last sync time is persisted to a file. Set the environment variables listed in the auth section plus SLACK_WEBHOOK_URL.
1import os2import base643import time4import json5import requests6from datetime import datetime, timedelta78CLIENT_ID = os.environ['SPOTIFY_CLIENT_ID']9CLIENT_SECRET = os.environ['SPOTIFY_CLIENT_SECRET']10REFRESH_TOKEN = os.environ['SPOTIFY_REFRESH_TOKEN']11SLACK_WEBHOOK = os.environ.get('SLACK_WEBHOOK_URL')12SYNC_FILE = os.environ.get('SYNC_STATE_FILE', '/tmp/spotify_sync.json')1314def get_token():15 creds = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()16 r = requests.post('https://accounts.spotify.com/api/token',17 headers={'Authorization': f'Basic {creds}'},18 data={'grant_type': 'refresh_token', 'refresh_token': REFRESH_TOKEN})19 r.raise_for_status()20 return r.json()['access_token']2122def api_get(token, path, params=None):23 r = requests.get(f'https://api.spotify.com/v1{path}',24 headers={'Authorization': f'Bearer {token}'}, params=params)25 if r.status_code == 429:26 time.sleep(int(r.headers.get('Retry-After', 5)))27 return api_get(token, path, params)28 r.raise_for_status()29 return r.json()3031def parse_date(release_date, precision):32 if precision == 'day': return datetime.strptime(release_date, '%Y-%m-%d')33 if precision == 'month': return datetime.strptime(release_date + '-01', '%Y-%m-%d')34 return datetime.strptime(release_date + '-01-01', '%Y-%m-%d')3536def load_state():37 try:38 with open(SYNC_FILE) as f: return json.load(f)39 except: return {'last_sync': (datetime.now() - timedelta(days=7)).isoformat()}4041def save_state(state):42 with open(SYNC_FILE, 'w') as f: json.dump(state, f)4344def main():45 token = get_token()46 state = load_state()47 since = datetime.fromisoformat(state['last_sync'])48 print(f'Checking releases since {since.date()}')4950 # Fetch all followed artists51 artists = []52 url, params = '/me/following', {'type': 'artist', 'limit': 50}53 while url:54 data = api_get(token, url if url.startswith('/') else url.replace('https://api.spotify.com/v1', ''), params)55 page = data['artists']56 artists.extend(page['items'])57 url = page['next']58 params = {}59 time.sleep(0.1)6061 print(f'Monitoring {len(artists)} artists')6263 # Check each for new releases64 new_releases = []65 for i, artist in enumerate(artists):66 albums = api_get(token, f'/artists/{artist["id"]}/albums',67 {'include_groups': 'album,single', 'limit': 10, 'market': 'US'})68 for album in albums['items']:69 if parse_date(album['release_date'], album['release_date_precision']) >= since:70 new_releases.append({**album, 'artist_name': artist['name']})71 time.sleep(0.2)7273 print(f'Found {len(new_releases)} new releases')7475 # Send notification76 if new_releases and SLACK_WEBHOOK:77 lines = [f'*Spotify Release Radar: {len(new_releases)} new releases*']78 for r in new_releases:79 lines.append(f"• {r['artist_name']} — *{r['name']}* ({r['album_type']}, {r['release_date']}) {r['external_urls']['spotify']}")80 requests.post(SLACK_WEBHOOK, json={'text': '\n'.join(lines)}).raise_for_status()8182 save_state({'last_sync': datetime.now().isoformat()})83 print('Sync complete')8485if __name__ == '__main__':86 main()Error handling
{"error":{"status":401,"message":"No token provided"}}Access token is expired or missing. Tokens last exactly 3,600 seconds.
Refresh your access token using the refresh_token grant before each scheduled run. Implement proactive refresh rather than waiting for a 401.
Refresh token immediately and retry once. Do not retry more than twice.
{"error":{"status":403,"message":"Forbidden"}}Most commonly caused by calling a deprecated endpoint (related-artists, audio-features) or missing scope. Also returned when a Development Mode app tries to access resources beyond the 5-user limit.
Do not call GET /v1/artists/{id}/related-artists — it is permanently deprecated for apps created after November 27, 2024. Ensure your token was obtained with user-follow-read scope.
Not retryable — remove the deprecated endpoint call or fix the scope.
{"error":{"status":429,"message":"API rate limit exceeded"}}Too many requests in the rolling 30-second window. Monitoring 50+ artists without delays triggers this regularly.
Read the Retry-After header and wait that many seconds before retrying. Add 200ms proactive delays between artist album checks.
Honor Retry-After header. Add proactive 200ms delays between requests to prevent hitting the limit.
{"error":{"status":404,"message":"Resource not found"}}Artist ID is invalid or the artist has been removed from Spotify's catalog.
Skip 404 artists and log them for review. Remove stale artist IDs from your tracking database.
Not retryable — skip and log.
Rate Limits for Spotify API
| Scope | Limit | Window |
|---|---|---|
| Per app (rolling window) | Undisclosed exact threshold | Rolling 30 seconds |
| Practical guidance (community estimate) | Approximately 180 requests/minute | Not officially confirmed by Spotify |
| Per artist album check | 1 request | Add 200ms delay between each check when monitoring 50+ artists |
1import time23def with_rate_limit_retry(request_fn, max_retries=5):4 for attempt in range(max_retries):5 response = request_fn()6 if response.status_code == 429:7 retry_after = int(response.headers.get('Retry-After', 2 ** attempt))8 print(f'Rate limited. Waiting {retry_after}s...')9 time.sleep(retry_after)10 elif response.ok:11 return response12 else:13 response.raise_for_status()14 raise RuntimeError('Max retries exceeded')- Add 200ms delays between each artist album check — essential when monitoring more than 20 artists
- Honor the Retry-After header exactly on 429 responses — do not use fixed sleep durations
- Cache artist metadata (genres, image URLs) locally and only re-fetch weekly to reduce API calls
- Run the tracker daily, not hourly — Spotify releases are not real-time and daily polling is sufficient
- Persist your last_sync timestamp so you don't re-process releases you've already notified about
Security checklist
- Store Client ID, Client Secret, and refresh token in environment variables — never in source code
- The refresh token is long-lived — treat it like a password and encrypt at rest if stored in a database
- Only request user-follow-read scope — do not request write scopes this automation doesn't need
- Use HTTPS for all API calls — Spotify enforces this and rejects plain HTTP
- Log sync events and error counts, but never log the full access token or refresh token
- Rotate Client Secrets from developer.spotify.com/dashboard if you suspect the secret was exposed
- In Development Mode, only authorize the 5 user accounts you actually intend to track
- Validate artist IDs before making API calls to prevent injection into request URLs
Automation use cases
Personal Release Radar
intermediateDaily check of your own followed artists list for new albums and singles, delivered as a morning digest notification.
Label Catalog Tracker
intermediateTrack a curated list of artists by ID (bypassing the following endpoint) to monitor a record label's output.
Release Detector with Auto-Playlist
advancedCombine with the playlist curation automation: when a new release is detected, automatically add its tracks to a 'New Releases' playlist.
Multi-User Release Aggregator
advancedCollect new release data across multiple authorized users' following lists and aggregate into a shared team playlist or feed.
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 workflows on new saved tracks or artist events, though release radar specifically requires custom polling logic.
- + No code required
- + Easy Slack/email notification setup
- + Runs automatically on schedule
- - Limited Spotify triggers — no native 'new artist release' trigger
- - Free tier limited to 100 tasks/month
- - Cannot monitor followed artists list directly
Make
Free tier available; paid plans from $9/monthMake's HTTP module can call Spotify endpoints on a schedule, providing more flexibility than Zapier's pre-built Spotify triggers.
- + Flexible HTTP routing for custom Spotify calls
- + Visual scenario builder
- + Free tier includes 1,000 operations/month
- - Requires manual OAuth setup in Make
- - No native Spotify release tracking trigger
- - Complex multi-step scenarios require paid plan
n8n
Self-hosted free; cloud from €20/monthn8n's scheduled trigger combined with the Spotify node and HTTP Request node can replicate this full artist tracking pipeline.
- + Self-hosted = free unlimited executions
- + Spotify node plus full HTTP access for custom endpoints
- + Persistent workflow state with database nodes
- - Requires server setup for self-hosting
- - OAuth credential setup more complex than code approach
- - Spotify node covers limited endpoints
Best practices
- Always check release_date_precision before parsing release dates — Spotify returns 'year', 'month', or 'day' precision and parsing a year-only date as a full date will throw errors
- Use include_groups=album,single to exclude 'appears_on' compilations from release radar to reduce noise
- Store artist IDs in your own database rather than re-fetching the following list on every run — check for new follows weekly, not daily
- Persist the last_sync timestamp between runs to avoid re-notifying about releases you've already processed
- Skip and log 404 errors gracefully — artists can be removed from Spotify's catalog, and hard failures will break your entire pipeline
- Do not rely on artist popularity or follower counts in Development Mode — these fields are restricted as of February 2026 and may return 0
- Test your release date parsing logic with all three precision types (year, month, day) before deploying — a missed release due to a parsing bug is hard to debug retroactively
Ask AI to help
Copy one of these prompts to get a personalized, working implementation.
I'm building a Spotify artist release radar using the Web API. My app polls GET /v1/artists/{id}/albums for each followed artist every day and compares release_date against my last sync timestamp. I'm getting inconsistent results because some release_date values are year-only ('2024') and some are full dates ('2024-09-15'). The release_date_precision field can be 'day', 'month', or 'year'. Can you write a Python function that correctly parses all three formats into a comparable datetime object, and explain the edge case behavior I should handle?
Build a Spotify Release Radar dashboard that shows new music from followed artists. The app needs: 1) An OAuth 2.0 login flow with user-follow-read scope, 2) A 'Last checked' display showing when the radar last ran, 3) A 'Check now' button that calls GET /v1/me/following?type=artist, then for each artist calls GET /v1/artists/{id}/albums, and shows new releases from the past 7 days, 4) A results grid with artist name, album cover, release name, release date, album type badge (album/single), and a 'Listen on Spotify' link, 5) An empty state message when no new releases are found. Handle OAuth token refresh automatically and show loading states. Use Tailwind CSS.
Frequently asked questions
Is the Spotify API free for artist tracking?
Yes — the Spotify Web API is free. In Development Mode you are limited to 5 authorized users per Client ID (as of February 11, 2026) and the app owner must have Spotify Premium. For production apps serving more users, Extended Quota Mode requires a legally registered organization with 250,000+ monthly active users.
Can I use GET /v1/artists/{id}/related-artists to discover similar artists?
No — GET /v1/artists/{id}/related-artists was permanently deprecated for apps created after November 27, 2024 and returns HTTP 403. The only working alternative is using the artist's genre array from GET /v1/artists/{id} as seed data for GET /v1/search queries.
Why doesn't Spotify have webhooks for new releases?
Spotify has never offered webhooks for the Web API. The only event-driven Spotify primitive is the Web Playback SDK's in-browser player events. All release tracking must be implemented as polling — call the artist albums endpoint on a schedule (daily is typically sufficient) and compare release dates against your last sync timestamp.
What happens when I hit the rate limit?
You receive HTTP 429 with a Retry-After header. Spotify's exact threshold is undisclosed — it uses a rolling 30-second window. When monitoring 50+ artists, the most important practice is adding 200ms delays between each artist's album check. Always honor the Retry-After header exactly; do not use a fixed sleep duration.
How do I handle release dates that only have year or month precision?
Always check the release_date_precision field before parsing. It can be 'day' (full date like '2024-09-15'), 'month' ('2024-09'), or 'year' ('2024'). Parse accordingly: for 'month' append '-01', for 'year' append '-01-01'. Compare these parsed dates against your last sync timestamp. A year-precision release dated '2024' will parse to January 1, 2024, which may trigger false positives — consider only tracking 'day' or 'month' precision releases for the most accurate radar.
Can RapidDev help build a production Spotify release tracking system?
Yes — RapidDev has built 600+ integrations including music industry tools. We can build a production-grade release radar with OAuth flow, database sync, notification routing, and a management dashboard. Contact us at rapidevelopers.com for a free consultation.
Are artist popularity and follower counts available in Development Mode?
As of the February 2026 Development Mode restrictions, artist popularity scores and follower counts are restricted for Development Mode apps and may return 0 or be absent from responses. Extended Quota Mode apps are unaffected. Build your release tracking logic around release_date and genre data, not popularity scores.
Need this automated?
Our team has built 600+ apps with API automations. We can build this for you.
Book a free consultation