An Apple Music API add to library call is not the mirror image of a catalog read. A read needs one credential and hands you a body to parse. A write needs a credential tied to one listener, a resolved catalog id for every track, and a follow-up read before you can honestly say what landed. The status code on a write tells you the request was accepted. It does not tell you that 847 of 900 tracks are sitting in the playlist.
This post is the write path end to end: the two credentials, catalog id resolution, the create-then-append split, verification by read-back, and what breaks when an import runs for an hour. For the wider picture of what the platform exposes, read what you can build with the Apple Music API beyond MusicKit. This is the half that stalls an onboarding import.
Two credentials, not one
Apple's own API is MusicKit, and it needs a paid Apple Developer Program membership, an ES256 developer token and a user token per listener. The developer token identifies your app. The user token is the one a write needs, because it represents a specific person authorising you to touch their library.
That split explains a common planning error. A team prototypes against the catalog, sees clean responses, scopes the import at a week, then meets the write path: a credential per listener, a failure mode per listener and an expiry per listener.
The same split exists on MusicAPI, with the custody moved. Public catalogue routes need no listener auth; user routes are addressed by the userUUID returned from the auth callback. You send the listener to the hosted Apple Music authorisation page:
GET https://app.musicapi.com/you/appleMusic/auth
?returnUrl=https://you.example/connected
They come back to your whitelisted returnUrl with ?data64=<base64> carrying:
{
"integrationUserUUID": "6a002d4b-f95b-471f-a27d-609a42e10cdb",
"authModel": { "status": "success", "error": null },
"integration": { "type": "appleMusic" }
}
That UUID is the write credential for every call below. It is a secret, so it belongs in your backend and never in a client bundle or a URL you log.
Resolve the catalog id before you add anything
You cannot add a track you cannot name in Apple's vocabulary. Whatever the source is, a Spotify playlist, your own database, a generated tracklist, every item has to become an Apple Music catalog id before the write.
ISRC first. Catalogue routes return tracks, albums, artists and artwork from the public Apple Music catalogue with no listener auth needed, so the resolution pass can run before the listener has connected anything. Search resolves any track, album or artist to its Apple Music ID, by ISRC or by text, with a confidence score.
# catalogue search, no listener auth
POST /public/search
# or resolve against a connected listener
POST /api/{userUUID}/search
Authorization: Basic <clientId:clientSecret>
The confidence score is the part to design around. An exact ISRC hit and a loose text match are not the same event, and an import that treats them identically will quietly put the wrong live version in someone's library. Keep the score with the resolved id, set a floor below which an item goes to an unmatched list with its best guess, and carry that list through to the results screen. The mechanics of identifier matching across providers are covered in working with ISRCs, track IDs and cross-platform matching.
Do not cache a resolved id globally and assume it holds for the next listener. Storefront selection is part of the same server-side handling, and resolution is cheap compared to writing the wrong track.
Create the playlist, then append
Creation and population are two calls. The create endpoint creates a new playlist in the authenticated user's music library, and the playlist is initially empty and can be populated with tracks using the add tracks endpoint.
POST /api/{userUUID}/playlists
Content-Type: application/json
{
"name": "Viva Loca New",
"description": "Description of your playlist",
"visibility": "public"
}
name is required. description and visibility, public or private, are optional. The response comes back on the normalised playlist shape:
{
"name": "Viva Playlist",
"id": "1",
"totalItems": 25,
"imageUrl": "https://www.example-image.com/978263478234",
"dateAdded": "2024-10-16T07:52:07.000Z",
"isOwner": true
}
Keep id. It is the handle for every append and for the verification read. Created playlists appear in the listener's Apple Music account, which is also the first place your support team will look when someone reports a failed import.
The two-call shape is an advantage for an import. If the append dies halfway you still hold a playlist id, so the retry appends the remainder instead of creating a second playlist called "Road Trip (1)". Write the playlist id into your job record before the first append, not after the last one. That single ordering decision is the difference between a resumable import and a duplicate generator. Full endpoint details are on the Apple Music create playlist page.
Library ids are not catalog ids
The id you searched for and the handle for the copy sitting in the listener's library are not the same thing. MusicKit token handling, storefront selection and library-vs-catalogue ID mapping are all handled server-side when you go through MusicAPI. Build direct and that mapping table is yours to keep.
The consequence is specific, and it bites at verification time. Comparing the ids you sent against the ids that come back from the library is not a valid diff. You will see a pile of mismatches that are the same recordings under different handles, and you will spend a day chasing a match-rate bug that does not exist.
Diff on ISRC, or on the normalised track shape, and treat raw provider ids as opaque pointers rather than as identity.
Verify a write by reading the playlist back
The only honest confirmation of a write is a read. The items endpoint fetches all tracks and items from a specific playlist on Apple Music.
GET /api/{userUUID}/playlists/{playlistId}/items
Authorization: Basic <clientId:clientSecret>
Items come back with ISRC codes, preview URLs and release dates, so the returned set can be diffed against your intended set on ISRC. The endpoint pages, and it is built to handle playlists with thousands of tracks, so walk every page before you declare a count. A verification step that reads page one and stops will report a 25 item playlist as complete when it holds 900 tracks.
The diff gives you three numbers worth storing per job: intended, written, unmatched. Those numbers are the results screen. "847 of 900, here are the 53 we could not match and what we think each one was" is a better product than a green checkmark that is occasionally a lie, and it turns support tickets into a CSV the user can act on. The read-back shape is documented on the Apple Music get playlist tracks page.
Token lifecycle on a long import
A large library does not move in one burst of requests. An import can easily run long enough that the credential it started with is not the credential it needs at the end, and the failure arrives mid-run, after thousands of successful writes.
Treat mid-run expiry as a normal state, not an exception. Persist job state per batch, make every batch resumable from the playlist id plus an offset, and on an auth failure pause the job and re-prompt the listener rather than burning the retry budget on a credential that will never work again. A job that cannot resume is a job that restarts, and a restart on a write path means duplicates.
This is the piece MusicAPI takes off the Apple side. One hosted redirect; we hold clients and refresh tokens, used only for the transfers listeners authorise. No Apple developer account to maintain, no per-listener token table in your database, no renewal logic to get wrong at 3am. If you are keeping your own provider clients across several services, the patterns are in how to handle OAuth token refresh across music streaming APIs.
If a write needs a field the normalised schema does not carry, passthrough gives you that listener's underlying Apple Music token when your transfer flow needs a field we don't normalise.
What you cannot write
Write support on Apple Music means create and append to playlists in a listener's Apple Music account. It does not mean edit. Apple Music does not let third parties rename or re-describe an existing playlist.
Design around it rather than discovering it in QA. Get the name and the description right at creation time, which means resolving the final playlist title before the create call, not after. If your product lets a user rename an imported playlist later, that rename lives in your database only, and it will not appear in Apple Music. Say so in the UI, once, at the point of the rename.
Rate limits and partial failure
A write path fails in units of items, not in units of requests. One batch of 100 appends can return a success and still leave you with 94 tracks, so the per-item record is the thing you build the job around.
Throttling is the other half. Standardised 429s with Retry-After, retries and idempotency built in mean your worker needs a batch size, a backoff that honours the Retry-After header, and a record of which items in the batch were already committed before the throttle hit. Retrying a batch blindly is how a 900 track playlist ends up with 1,100 entries.
FAQ
Can I write liked songs the same way I write playlists?
Check per service before you promise it. On the read side, Apple Music gives full paged access to liked tracks, albums and followed artists in Apple Music. On the write side, the documented capability is create and append to playlists in a listener's Apple Music account. Capabilities differ by service, so confirm the specific read or write you need on the services and features page before it reaches a roadmap.
How long before a write is actually running against a real account?
A sandbox key is instant and reading a live library takes about an hour. The sandbox gives you 5 test listeners across services, for example 3 Apple Music and 2 Spotify, so plan your test accounts before you start wiring destinations.
Does this code work for the other services?
Mostly. Swap the musicService value and the calls behave identically, so the create call, the items read and the search resolution keep the same shapes on another service, as long as that service supports the read or write you are making. The write semantics above are the Apple specifics; the request shape is not. More on the Apple side is on the Apple Music API page.
Skip months of OAuth and per-provider SDK work. Sandbox keys are free and instant, and one API moves libraries between 11 streaming services. Start with the Apple Music create playlist endpoint.