- Docs
- Read and write a user's library
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.
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:
{
"results": [{ "id": "1", "name": "Road trip", "totalItems": 42, "imageUrl": null, "isOwner": true }],
"nextParam": "eyJvZmZzZXQiOjUwfQ==",
"totalItems": 87
}
nextParam: send it back as thenextParamquery parameter to get the next page.nullon 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 benull.
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:
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 takeitemIdsof 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 takeentryIds. 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 -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.404withinternalCode: 19: the playlist or album is gone.422withinternalCode: 18: the item is already in the collection.429: waitRetry-Afterseconds. See Rate limits.
Each library call that reaches the music service counts as one billed request, failed calls included. See Billing and usage.