A lightweight, single-file, zero-dependency Spotify wrapper, that can be dropped into any Python 2.x or 3.x project with minimal hassle.
pip install spotify-lite
or simply drop spotify/spotify.py
anywhere into your project.
Get an instance of the API wrapper:
import spotify
client_id = "my-client-id"
client_secret = "top-secret"
# optional but required when registering a user via authorization code flow
redirect_uri = "http://localhost:1337"
api = spotify.SpotifyAPI(client_id, client_secret, redirect_uri)
Get user permission and complete authorization:
url = api.oauth2_url(scopes=['user-read-private'])
# redirect user to above url, get auth code and then...
user = api.set_user_from_code('really-long-code-string')
See Authorization section for more options and detailed explanation.
Get a playlist:
pl = api.playlist('1SCHh6WSTufPLgEFjGSteL')
print(pl['name'])
The response for single resource endpoints is the JSON response from Spotify, as a Python dictionary.
Get associated user's playlists:
pls = api.playlists()
for pl in pls:
print(pl['name'])
The response for endpoints returning multiple resources is always a generator, yielding the inner Spotify resource(s). Pagination is automatically handled.
Post a boatload of tracks to a playlist:
track_uris = ["spotify:track:4uLU6hMCjMI75M1A2tKUQC"] * 5000
api.playlist_tracks_add('some-playlist-id', track_uris)
You don't need to batch your requests - the above will execute multiple batch requests internally to add all tracks to the playlist.
See API Endpoints for a more in-depth look at the request/response mapping.
First, you need to initialize an instance of the API wrapper with your developer credentials:
import spotify
client_id = "my-client-id"
client_secret = "top-secret"
# optional but required when registering a user via authorization code
redirect_uri = "http://localhost:1337"
api = spotify.SpotifyAPI(client_id, client_secret, redirect_uri)
Alternatively you can set the SPOTIFY_CLIENT_ID
, SPOTIFY_CLIENT_SECRET
and SPOTIFY_REDIRECT_URI
environment variables and call the above constructor with no arguments:
import os
# ... or directly through your runtime environment
os.environ['SPOTIFY_CLIENT_ID'] = "my-client-id"
os.environ['SPOTIFY_CLIENT_SECRET'] = "top-secret"
os.environ['SPOTIFY_REDIRECT_URI'] = "http://localhost:1337"
api = spotify.SpotifyAPI()
This library supports the authorization code flow. You will need to prompt a user to grant permissions which will generate an authorization code that you can use to associate the user with the API instance. You can call api.oauth2_url(scopes=['user-read-private'])
on the above created object to generate a valid auth URL requesting the specified roles (passed as a list of strings). Refer to the documentation for allowed authorization scopes.
After you've sent the user to the above URL (and assuming your setup credentials are valid) you will obtain an authorization code on the specified redirect URI. You can then pass that code to the API instance:
code = "code-i-received-from-authorization-flow"
user = api.set_user_from_code(code)
If successful, the user
object you receive contains the currently generated access and refresh tokens. You can store user.refresh_token
to skip the authorization code flow in the future. According to Spotify refresh tokens should be valid indefinitely unless you change your client credentials.
If you've persisted a user's refresh token you can directly instantiate an API instance by creating a SpotifyUser
object and passing it to the wrapper's constructor:
# assume environment variables for client credentials are set
user = spotify.SpotifyUser(refresh_token='user-token-persisted-across-sessions')
api = spotify.SpotifyAPI(user=user)
SpotifyUser
s can be dynamically assigned to a SpotifyAPI
instance:
new_user = spotify.SpotifyUser(refresh_token='another-saved-token')
api.set_user(new_user)
# API methods now work against new_user
If you only have one user for your application and you've already obtained the refresh token you can set the environment variable SPOTIFY_REFRESH_TOKEN
to eliminate any boilerplate code:
import os
# ... or directly through your runtime environment
os.environ['SPOTIFY_CLIENT_ID'] = "my-client-id"
os.environ['SPOTIFY_CLIENT_SECRET'] = "top-secret"
os.environ['SPOTIFY_REFRESH_TOKEN'] = "user-token-persisted-across-sessions"
api = spotify.SpotifyAPI()
print(api.profile())
This library automatically handles pagination for requests and responses. Additionally, responses will always return the parsed JSON received from Spotify.
Each Spotify API endpoint is mapped to a method you can access via the API wrapper instance. For details on what parameters are accepted for a particular endpoint, refer to the official Web API Reference. Path parameters and IDs for batch requests are passed as positional arguments, and query parameters as keyword arguments. The names of keyword arguments follow the naming convetion outlined in the Spotify documentation:
>>> api.playlist('2pFB0I2SvMbsPHZrfRfbZL', fields=['description', 'uri'])
{'description': 'Music candy. High replay value.', 'uri': 'spotify:playlist:2pFB0I2SvMbsPHZrfRfbZL'}
You will notice in the above we are using a Python list instead of what the documentation mentions, which is a comma-separated list of strings. In general, CSV parameters should be passed as lists and the library will automatically convert them. Additionally, ignore any parameters that have to deal with pagination, such as limit
or offset
as these are automatically handled internally.
All methods that should return multiple items return a generator, which you use to iterate over all results without specifiying any pagination parameters:
tracks_generator = api.playlist_tracks("1SCHh6WSTufPLgEFjGSteL")
for track in tracks_generator:
print(track['track']['name'])
You don't have to unwrap the Spotify paging object or worry about hitting pagination limits, the library will automatically handle all of this and always use the highest available limit for each paginated request to limit round-trip time.
Endpoints that receive multiple ids usually need to be sent in batches. The library takes care of all that for you so you can simply pass the entire list:
# huge list of 5000 tracks
track_uris = [...]
api.playlist_tracks_add("my-playlist-id", track_uris)
Below is a list of supported endpoints and their corresponding method names, accessed via the object returned by the call to SpotifyAPI
. Argument positioning and naming as explained previously:
Get Multiple Albums -> albums
Get an Album -> album
Get an Album's Tracks -> album_tracks
Get Multiple Artists -> artists
Get an Artist -> artist
Get an Artist's Top Tracks -> artist_top_tracks
Get an Artist's Related Artists -> artist_related_artists
Get an Artist's Albums -> artist_albums
Get All New Releases -> new_releases
Get All Featured Playlists -> featured_playlists
Get All Categories -> categories
Get a Category -> category
Get a Category's Playlists -> category_playlists
Get Recommendations -> recommendations
Get Multiple Episodes -> episodes
Get an Episode -> episode
Follow a Playlist -> follow_playlist
Unfollow Playlist -> unfollow_playlist
Check if Users Follow a Playlist -> is_playlist_followed
Get User's Followed Artists -> artists_followed
Follow Artists or Users -> follow_artists
, follow_users
Unfollow Artists or Users -> unfollow_artists
, unfollow_users
Get Following State for Artists/Users -> is_following_artists
, is_following_users
Get User's Saved Albums -> saved_albums
Save Albums for Current User -> saved_albums_add
Remove Albums for Current User -> saved_albums_remove
Check User's Saved Albums -> are_albums_saved
Get User's Saved Tracks -> saved_tracks
Save Tracks for User -> saved_tracks_add
Remove User's Saved Tracks -> saved_tracks_remove
Check User's Saved Tracks -> are_tracks_saved
Get User's Saved Shows -> saved_shows
Save Shows for Current User -> saved_shows_add
Remove User's Saved Shows -> saved_shows_remove
Check User's Saved Shows -> are_shows_saved
Get a User's Top Artists and Tracks -> top_artists
, top_tracks
Get a List of Current User's Playlists -> playlists
Get a List of a User's Playlists -> playlists
(w/ user ID as parameter)
Create a Playlist -> playlists_add
Get a Playlist -> playlist
Change a Playlist's Details -> playlist_edit
Get a Playlist's Items -> playlist_tracks
Add Items to a Playlist -> playlist_tracks_add
Reorder or Replace a Playlist's Items -> playlist_tracks_reorder
, playlist_tracks_replace
Remove Items from a Playlist -> playlist_tracks_remove
Get a Playlist Cover Image -> playlist_images
Upload a Custom Playlist Cover Image -> playlist_image_add
(w/ image binary as a file descriptor, e.g. through open
)
Search for an Item -> search_albums
, search_artists
, search_playlists
, search_tracks
, search_shows
Get Multiple Shows -> shows
Get a Show -> show
Get a Show's Episodes -> show_episodes
Get Several Tracks -> tracks
Get a Track -> track
Get Audio Features for Several Tracks -> tracks_audio_features
Get Audio Features for a Track -> track_audio_features
Get Audio Analysis for a Track -> track_audio_analysis
Get Current User's Profile -> profile
Get a User's Profile -> profile
(w/ user ID as param)
Get Information About The User's Current Playback -> player
Transfer a User's Playback -> player_transfer
Get a User's Available Devices -> player_devices
Get the User's Currently Playing Track -> player_current_track
Start/Resume a User's Playback -> player_play
Pause a User's Playback -> player_pause
Skip User's Playback To Next Track -> player_next
Skip User's Playback To Previous Track -> player_previous
Seek To Position In Currently Playing Track -> player_seek
Set Repeat Mode On User's Playback -> player_repeat
Set Volume For User's Playback -> player_volume
Toggle Shuffle For Userâs Playback -> player_shuffle
Get Current User's Recently Played Tracks -> player_recent_tracks
Add an item to queue -> player_queue_add
I've added a few additional convenience methods:
playlist_track_objs
, saved_album_objs
, saved_show_objs
, saved_track_objs
, player_recent_track_objs
These break the contract that you always get the exact JSON response from Spotify and return the inner objects, omitting any additional metadata in the outer object. As an example, these do the same thing:
for track in api.playlist_tracks("1SCHh6WSTufPLgEFjGSteL"):
print(track['track']['name'])
for track in api.playlist_track_objs("1SCHh6WSTufPLgEFjGSteL"):
print(track['name'])
You can use this library purely as an authentication wrapper and send direct requests to the Spotify API. Call the get
, post
, put
and delete
methods on the wrapper instance with parameters similar to what you would use in Requests, namely params
, data
, json
and headers
. You will then get back an instance of http.client.HTTPResponse
(in Python 3.x), which you can handle as you like. Example:
import json
import spotify
api = spotify.SpotifyAPI()
response = api.get("playlists/2pFB0I2SvMbsPHZrfRfbZL")
response_json = json.loads(response.read())
print(response_json['name'])
These are used under the hood by the wrapper methods, so are fairly well tested, but you will have to handle URL/parameter construction and response/pagination handling by yourself.
Full integration tests are availble in tests
, running against a real Spotify user. You need to set up the SPOTIFY_CLIENT_ID
, SPOTIFY_CLIENT_SECRET
and SPOTIFY_REFRESH_TOKEN
environment variables as explained in Authorization.
Then, from root directory:
python -m unittest