Spotify Endpoints - Take434/Appollon GitHub Wiki

The Spotify Web API (Application Programming Interface, a deeper explanation can be found here) has many different endpoints providing unique data. These range from general data about available genres to querying public playlists, getting recommendations or even user-specific data about their profile and playlists. Besides get-methods, Spotify allows certain post- / put- and delete methods. These perform actions such as removing / adding tracks to playlists or following / un-following artists [2][3].

It is important to know, that some of these endpoints require different scopes. These scopes are essentially permissions that the user has to give the app to perform certain actions in behalf of them. For example, to get the liked songs of a user, the app needs the "user-library-read" scope to query the endpoint successfully [4].

Requesting a Spotify endpoint returns 1 of 4 specific status codes [2]:

  • 200: OK - a successful request, possible results will be returned
  • 401: Unauthorized - the request either did not include user authorization or it included invalid ones
  • 403: Forbidden - a successful request was sent, but the server refused to fulfill it
  • 429: Too Many Requests - the app sent too many requests to the API

Spotify's API implements a rate limit, limiting the amount of requests you can make in a 30 seconds time window. The exact number of allowed requests are not officially shared, but the API will start returning the 429 status code after too many requests. This has the consequence of not being able to retrieve any more data from the API for a maximum of 30 seconds. These rate limits also differ for Applications in development mode and those who applied for extended quota mode [1], with ones in development mode having lower rate limits [5].

The Spotify API implements paged requests. This means, that most requests, that return any number of items, require two parameters.
The first one is the offset, which tells the API how many items should be skipped. The second one is the limit, which tells the API how many items to return. The limit has a maximum value, that depends on the endpoint queried, but usually it is 50.
Since a more detailed explanation of the concept of pagination is out of scope for this project, a more in-depth document can be found here

Endpoints

The application uses six main endpoints, which are queried, to gain the data.
The first one being the /me endpoint. This one provides general information about the user, like their name and their profile picture. This one is queried right at the start, after the login process finished.

Secondly, querying the /me/playlists endpoint retrieves data about a user's playlists, including ones they created themselves or those they are following. This endpoint only provides general information, like the title, the name of the creator, a cover link and more. This does not include any tracks yet, because these are obtained from the /playlists/playlistId/tracks endpoint.

Another endpoint is /artists. This gets information about many artists, based on their ids, which are required in the body. Especially important for genres.

The fifth is the /me/tracks endpoint. This one returns the liked songs of a user. While these seem like a normal playlist, they are handled a bit differently in the API. They are a collection of tracks and therefore do not have an overlying playlist.
The last one is the /audio-features endpoint. This one expects an array of trackIds and returns their corresponding audio features. These include information such as beats per minute, tempo or danceability.

All of these endpoints can be seen and tested on the official Spotify Web API page. The only restriction being, that a Spotify account is needed [6].

API Requests in the Application

The process of querying data from the API will be explained by the getSavedTracks function. It is responsible for retrieving the liked songs for a user. Within the function the first thing to be checked, is, if there is a valid token. This token is required to authorize each API request. This is achieved in the following way:

const token = cookies().get("token")?.value;

  if (!token) {
    return "No token found";
  }

Next up is the first actual API call. This one is just to obtain the total amount of items that need to be queried from the API, since paging is used here.
This code snippet uses many different libraries and patterns. Axios is used to make easy API calls for node.js. In typescript, Axios takes a generic, which predefines its expected type. This type comes from Zod, which is important for the type-safety and makes Zod objects, that have inferable types. Here the get-method is used, which requires an url. In this case, the url is
https://api.spotify.com/v1/me/tracks.
Since the Spotify API is being requested, the authorization-header is required. Otherwise a 401 response would be returned, because the token is not valid. Additionally the following parameters have to be provided:

  • market, which determines the targeted market for the request. This is important, because certain tracks are only available in certain countries.
  • limit, sets how many tracks should be returned.
  • offset, specifies how many tracks should be skipped.

In this case, only one track should be returned, so the following values are used:

const apiCall = await axios.get<z.infer<typeof apiSavedTracksResponse>>(
    "https://api.spotify.com/v1/me/tracks",
    {
      headers: {
        Authorization: "Bearer " + token,
      },
      params: {
        market: "GE",
        limit: 1,
        offset: 0,
      },
    }
  );

For reference, the Zod object looks like this:

const apiSavedTracksResponse = z.object({
  limit: z.number(),
  offset: z.number(),
  total: z.number(),
  items: z.array(
    z.object({
      track: apiTrack,
    })
  ),
});

This pattern is often repeated among all Spotify API endpoints. They return an object with items, which is an array of the desired data and information about the request itself, like limit, offset and total items returned.

The following code snippet shows the main logic of the function. It consists of a while-loop, that loops as long as there are still tracks to be queried. After that the amount of tracks, that still need to be queried, is calculated. If there are more than 50, the limit will be set to 50.
Now the API request is send to Spotify again, with the difference, that there are dynamic values transmitted every time. At the end, the data from the API is being pushed into an array using the spread operator and the current track count is increased.

while (totalTrackCount > currentTrackCount) {
    const currentTrackLimit =
      totalTrackCount - currentTrackCount > 50
        ? 50
        : totalTrackCount - currentTrackCount;

    const res = await axios.get<z.infer<typeof apiSavedTracksResponse>>(
      "https://api.spotify.com/v1/me/tracks",
      {
        headers: {
          Authorization: "Bearer " + token,
        },
        params: {
          market: "GE",
          limit: currentTrackLimit,
          offset: currentTrackCount,
        },
      }
    );
    
    savedTracksArray.push(...res.data.items.map((items) => items.track));
    currentTrackCount += currentTrackLimit;
  }

Lastly, the newly retrieved data is checked, whether it is of the correct type and there are no required objects or properties missing. This is again done by using the Zod library. If there are no errors with the data, the data is returned.

const parsedData = savedTracks.safeParse(savedTracksArray);

  if (!parsedData.success) {
    console.log(parsedData.error);
    console.error(parsedData.error.flatten());
    return;
  }

  return parsedData.data;

Terminology

[API] An API is a set of rules that allow different applications to communicate with each other. It acts as a facilitator to enable developers to use functionalities and data of another application or service.
The way an API functions, is as follows:

  1. Sending a request to an API (including headers and potentially a request body with parameters)
  2. API calls the underlying application for the data (if the request is valid)
  3. Data is send to the API
  4. Response with the data is sent back to the requester

A common example for an API would be Twitter. Twitter has an API, which lets other applications integrate the Share on Twitter button. [7].

References

[1] Spotify Docs Quota Modes last accessed 21.07.2023
[2] Spotify Docs API Calls last accessed 20.07.2023
[3] Spotify Docs Overview last accessed 21.07.2023
[4] Spotify Docs Scopes last accessed 21.07.2023
[5] Spotify Docs Rate Limits last accessed 21.07.2023
[6] Spotify Web API Reference Get Saved Tracks last accessed 21.07.2023
[7] IBM What is an API? last accessed 24.07.2023
[8] Spotify Web API Reference Get Saved Tracks last accessed 21.07.2023
[9] Spotify Web API Reference Get Saved Tracks last accessed 21.07.2023

⚠️ **GitHub.com Fallback** ⚠️