Skip to the content
  1. Docs
  2. Read and write a user's library

Read and write a user's library

CoreGuide · 2 min
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.

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

The URL shape
https://api.musicapi.com/api/{integrationUserUUID}/<resource>

Authenticate with Basic from your backend, or with a DevToken from your frontend. A Client ID token does not work here.

The endpoints

Not every service supports every write. Check Services and features. An unsupported call answers 418 with internalCode: 15.

Pagination

Every list answer has the same shape:

A page
{
  "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). Some services ignore it.
  • totalItems: the total, when the service tells it. It can be null.
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;
}

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:

Node: add 120 tracks in chunks of 20
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 or with the user search.

Search inside the user's service

POST/api/{integrationUserUUID}/search searches the service of this user, with their account and their country:

curl
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.
  • 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.

Each library call that reaches the music service counts as one billed request, failed calls included. See Billing and usage.