# Read and write a user's library
URL: https://musicapi.com/docs/user-library/overview-and-pagination.md

> Playlists, playlist items, liked tracks, albums, artists and search in the user's own service. One URL shape, pagination with nextParam and limitParam, and at most 20 items per write.

Product: MusicAPI Core. Section: Read and write a user's library.

Every library call names one connected user in the path. The answer has the same shape on every service.

```url
https://api.musicapi.com/api/{integrationUserUUID}/<resource>
```

Authenticate with Basic from your backend, or with a [DevToken](https://musicapi.com/docs/get-started/authentication.md#devtoken) from your frontend. A Client ID token does not work here.

## The endpoints

| What                      | Read                                                                                                                                                                                                                                                                                                                     | Write                                                                                                                                                                                                                                                                                                                                                                                      |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Playlists                 | [`GET /api/{integrationUserUUID}/playlists`](https://musicapi.com/docs/reference/list-playlists.md), [`GET /api/{integrationUserUUID}/playlists/{playlistId}`](https://musicapi.com/docs/reference/get-playlist.md)                                                                                                      | [`POST /api/{integrationUserUUID}/playlists`](https://musicapi.com/docs/reference/create-playlist.md), [`PUT /api/{integrationUserUUID}/playlists/{playlistId}`](https://musicapi.com/docs/reference/update-playlist.md), [`DELETE /api/{integrationUserUUID}/playlists/{playlistId}`](https://musicapi.com/docs/reference/delete-playlist.md)                                             |
| Playlist items            | [`GET /api/{integrationUserUUID}/playlists/{playlistId}/items`](https://musicapi.com/docs/reference/list-playlist-items.md)                                                                                                                                                                                              | [`POST /api/{integrationUserUUID}/playlists/{playlistId}/items`](https://musicapi.com/docs/reference/add-playlist-items.md), [`PUT /api/{integrationUserUUID}/playlists/{playlistId}/items`](https://musicapi.com/docs/reference/move-playlist-items.md), [`DELETE /api/{integrationUserUUID}/playlists/{playlistId}/items`](https://musicapi.com/docs/reference/remove-playlist-items.md) |
| Liked tracks              | [`GET /api/{integrationUserUUID}/liked/tracks`](https://musicapi.com/docs/reference/list-liked-tracks.md)                                                                                                                                                                                                                | [`POST /api/{integrationUserUUID}/liked/tracks`](https://musicapi.com/docs/reference/add-liked-tracks.md), [`DELETE /api/{integrationUserUUID}/liked/tracks`](https://musicapi.com/docs/reference/remove-liked-tracks.md)                                                                                                                                                                  |
| Albums                    | [`GET /api/{integrationUserUUID}/albums`](https://musicapi.com/docs/reference/list-albums.md), [`GET /api/{integrationUserUUID}/albums/{albumId}`](https://musicapi.com/docs/reference/get-album.md), [`GET /api/{integrationUserUUID}/albums/{albumId}/items`](https://musicapi.com/docs/reference/list-album-items.md) | [`POST /api/{integrationUserUUID}/albums`](https://musicapi.com/docs/reference/save-album.md), [`DELETE /api/{integrationUserUUID}/albums/{albumId}`](https://musicapi.com/docs/reference/remove-album.md)                                                                                                                                                                                 |
| Artists                   | [`GET /api/{integrationUserUUID}/artists`](https://musicapi.com/docs/reference/list-artists.md), [`GET /api/{integrationUserUUID}/artists/{artistId}`](https://musicapi.com/docs/reference/get-artist.md)                                                                                                                | [`POST /api/{integrationUserUUID}/artists`](https://musicapi.com/docs/reference/follow-artist.md), [`DELETE /api/{integrationUserUUID}/artists/{artistId}`](https://musicapi.com/docs/reference/unfollow-artist.md)                                                                                                                                                                        |
| Search the user's service | [`POST /api/{integrationUserUUID}/search`](https://musicapi.com/docs/reference/search-user-service.md)                                                                                                                                                                                                                   |                                                                                                                                                                                                                                                                                                                                                                                            |
| Profile ("Me")            | [`GET /api/{integrationUserUUID}/users/profile`](https://musicapi.com/docs/reference/get-user-profile.md)                                                                                                                                                                                                                |                                                                                                                                                                                                                                                                                                                                                                                            |

Not every service supports every write. Check [Services and features](https://musicapi.com/docs/get-started/services-and-features.md). An unsupported call answers `418` with `internalCode: 15`.

## Pagination

Every list answer has the same shape:

```json
{
  "results": [{ "id": "1", "name": "Road trip", "totalItems": 42, "imageUrl": null, "isOwner": true }],
  "nextParam": "eyJvZmZzZXQiOjUwfQ==",
  "totalItems": 87
}
```

- `nextParam`: send it back as the `nextParam` query parameter to get the next page. `null` on the last page. It is opaque: do not build or change it.
- `limitParam`: the page size you want. The service's own maximum caps it (Spotify 50, Tidal 20, Amazon Music 100, Qobuz 500; see the **Page size max** column in [Services and features](https://musicapi.com/docs/get-started/services-and-features.md)). Some services ignore it.
- `totalItems`: the total, when the service tells it. It can be `null`.

```js
async function listAllPlaylists(integrationUserUUID) {
  const auth = Buffer.from(`${process.env.MUSICAPI_CLIENT_ID}:${process.env.MUSICAPI_CLIENT_SECRET}`).toString("base64");
  const playlists = [];
  let nextParam = null;
  do {
    const url = new URL(`https://api.musicapi.com/api/${integrationUserUUID}/playlists`);
    url.searchParams.set("limitParam", "50");
    if (nextParam !== null) url.searchParams.set("nextParam", nextParam);
    const response = await fetch(url, { headers: { Authorization: `Basic ${auth}` } });
    if (response.status === 412) throw new Error("The user must sign in again");
    if (!response.ok) throw new Error(`MusicAPI ${response.status}`);
    const page = await response.json();
    playlists.push(...page.results);
    nextParam = page.nextParam;
  } while (nextParam !== null);
  return playlists;
}
```

```python
import os, requests

def list_all_playlists(integration_user_uuid):
    auth = (os.environ["MUSICAPI_CLIENT_ID"], os.environ["MUSICAPI_CLIENT_SECRET"])
    playlists, next_param = [], None
    while True:
        params = {"limitParam": 50}
        if next_param is not None:
            params["nextParam"] = next_param
        response = requests.get(
            f"https://api.musicapi.com/api/{integration_user_uuid}/playlists", params=params, auth=auth
        )
        response.raise_for_status()
        page = response.json()
        playlists.extend(page["results"])
        next_param = page["nextParam"]
        if next_param is None:
            return playlists
```

```bash
curl "https://api.musicapi.com/api/3f0b8c1e-6a4d-4c7a-9a55-2f1e7d9b0c11/playlists?limitParam=50&nextParam=eyJvZmZzZXQiOjUwfQ==" \
  -u "$MUSICAPI_CLIENT_ID:$MUSICAPI_CLIENT_SECRET"
```

## Writes: at most 20 items per call

`itemIds` and `entryIds` take **at most 20 ids** per request. A longer list fails with `400`. Split it:

```js
for (let i = 0; i < trackIds.length; i += 20) {
  const response = await fetch(`https://api.musicapi.com/api/${integrationUserUUID}/playlists/${playlistId}/items`, {
    method: "POST",
    headers: { Authorization: `Basic ${auth}`, "Content-Type": "application/json" },
    body: JSON.stringify({ itemIds: trackIds.slice(i, i + 20) }),
  });
  if (!response.ok) throw new Error(`MusicAPI ${response.status}`);
}
```

## Ids per service

Ids are the music service's own ids. A Spotify track id works only for Spotify users.

- `id`: the track, album, artist or playlist on that service. Add and like calls take `itemIds` of track ids.
- `entryId`: one entry of a track inside a playlist or the liked tracks. A playlist can hold the same track twice, with two entry ids. Remove calls take `entryIds`. Read them from the list calls.
- To move a track to another service, find its id there with the [catalog match](https://musicapi.com/docs/catalog/overview.md) or with the user search.

## Search inside the user's service

[`POST /api/{integrationUserUUID}/search`](https://musicapi.com/docs/reference/search-user-service.md) searches the service of this user, with their account and their country:

```bash
curl -X POST "https://api.musicapi.com/api/3f0b8c1e-6a4d-4c7a-9a55-2f1e7d9b0c11/search" \
  -u "$MUSICAPI_CLIENT_ID:$MUSICAPI_CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"type": "track", "track": "Numb", "artist": "Linkin Park", "isrc": "USWB10300474"}'
```

`type` is `track`, `album` or `artist`. `isrc` works where the service supports a search by ISRC.

## When a call fails

- `412`: the user must [sign in again](https://musicapi.com/docs/get-started/rate-limits-and-errors.md#the-user-must-sign-in-again-412).
- `404` with `internalCode: 19`: the playlist or album is gone.
- `422` with `internalCode: 18`: the item is already in the collection.
- `429`: wait `Retry-After` seconds. See [Rate limits](https://musicapi.com/docs/get-started/rate-limits-and-errors.md).

Each library call that reaches the music service counts as one billed request, failed calls included. See [Billing and usage](https://musicapi.com/docs/billing/billing-and-usage.md).
