Using query parameters with requests

Query parameters

When making requests for data relating to a particular track or album, the URL itself (with the {id} at the end of the endpoint) is sufficient to specify the data we want returned:

https://api.spotify.com/v1/albums/1To7kv722A8SpZF789MZy7

However, we often need or want to be more specific, for example:

  • making a search for data with specific features
  • fetching a subset of data from a given collection

APIs tend to be structed to handle such requests through the use of query parameters.

Example requiring query parameters

Take a look at the API documentation for Search.

You'll notice that values for the q (short for 'query') and type parameters are required - our request will fail if we don't provide them.

Fear not - requests is here to make life easier for us.

Code walkthrough

First, fork the repl.

Access Token

Remember that you'll need to enter your own valid access_token, which you can generate by forking this repl and adding your own Spotify API credentials to a .env file.

Run the repl, copy your access_token from the console, and and paste it into main.py in the repl for the walkthrough.

import requests

headers = {'Authorization': f'Bearer {access_token}'}

base_url = 'https://api.spotify.com/v1'
search_endpoint = '/search'
search_url = f'{base_url}{search_endpoint}'

params = {'q': 'waterfall', 'type': 'track'}
response = requests.get(search_url, headers=headers, params=params)

The params keyword argument

Having covered authorization and endpoints in the MixTape tutorial, we'll skip straight to the relevant code for query parameters:

params = {'q': 'waterfall', 'type': 'track'}
response = requests.get(search_url, headers=headers, params=params)
  • we assigned a dictionary containing the parameter:value pairs to params
  • we used our params dictionary as the params argument in the GET request

Examining the response

After running the repl, you can try out the following code snippets in the console.

data = response.json()
type(data)
    dict
list(data.keys())
    ['tracks']
  • the top-level dictionary has only one key; perhaps at some stage, type wasn't a required query parameter and so the returned dataset could also have contained albums, artists, etc

More nested data...

list(data['tracks'].keys())
    ['href', 'items', 'limit', 'next', 'offset', 'previous', 'total']
  • the keys in the next level relate to the search itself, including data on relevant tracks
    • the items value is a list of dictionaries, each containing data about a given track
  • each item in this list is a dictionary, some of whose values are also data structures

We have to go deep into the JSON structure to find our individual track data:

print(list(data['tracks']['items'][0].keys()))
    ['album', 'artists', 'available_markets', 'disc_number', 'duration_ms', 'explicit', 'external_ids', 'external_urls', 'href', 'id', 'is_local', 'name', 'popularity', 'preview_url', 'track_number', 'type', 'uri']
len(data['tracks']['items'])
    20
  • our request has returned 20 items, but there are many more tracks which were identified by our search...

Result and rate limits on API requests

APIs will not typically return all items if there are a large number of matches; instead, further calls to the API will be required. You may also encounter limits on the frequency or volume of requests to an API or particular endpoint.

print(waterfall_tracks['tracks']['total'])
print(waterfall_tracks['tracks']['limit'])
print(waterfall_tracks['tracks']['next'])
    97307
    20
    https://api.spotify.com/v1/search?query=waterfall&type=track&offset=20&limit=20
  • given the total number of results, we can see why there is a limit rather than returning all of them :)
  • the Spotify API conveniently provides us with an endpoint for the next block of tracks

Your turn...

Spend some time using requests.get() to fetch data from endpoints listed under Albums, Artists, Browse, Search, and Tracks in the documentation; all of these can be done using the Client Credentials access_token you created earlier.

See if you can create some reuable functions, to do things such as:

  • fetching data for a given track, using an access_token and a track id as arguments
  • searching for artists, using an access_token and a string of keywords as arguments

For now, don't worry about automatically renewing the access_token or diving too deep into the nested data - we'll be looking at that later on.