How to Configure Apple MusicKit API With Developer Tokens to Programmatically Fetch User Playback History in iOS Apps

Integrating Apple Music directly into third-party iOS applications allows developers to build highly engaging, personalized audio experiences. Whether you are building a fitness app that analyzes workout BPM against listening habits, or a social network that shares heavy rotation playlists, you must leverage Apple’s MusicKit framework. However, querying the Apple Music catalog or accessing a user’s private playback history is not as simple as making an unauthenticated REST call. To programmatically fetch sensitive user data, iOS engineers must navigate a complex cryptographic authentication flow involving ECDSA-signed Developer Tokens and User Tokens via the StoreKit API.

Understanding the Dual-Token Architecture

Apple Music API requests require two distinct layers of authorization:

  1. The Developer Token: This is a JSON Web Token (JWT) that proves your iOS application is authorized by a legitimate Apple Developer account. It is used to query public catalog data (e.g., searching for “The Beatles”).
  2. The User Token (Music User Token): This token proves that the specific human user operating the iPhone has explicitly granted your app permission to access their private Apple Music subscription data (e.g., fetching their “Heavy Rotation” list).

You cannot obtain a User Token without first possessing a valid Developer Token.

Generating the Developer Token (JWT)

The Developer Token must be generated on a secure backend server; it should never be hardcoded into the iOS application binary, as it relies on a private cryptographic key that must remain secret.

First, navigate to your Apple Developer portal, create a new MusicKit Private Key (an .p8 file), and note the 10-character Key ID and your Team ID.

On your backend server (e.g., using Node.js), you generate the JWT. The token must be signed using the Elliptic Curve Digital Signature Algorithm (ES256).

const jwt = require('jsonwebtoken');
const fs = require('fs');

const privateKey = fs.readFileSync('AuthKey_YOURKEYID.p8');
const teamId = 'YOURTEAMID';
const keyId = 'YOURKEYID';

const token = jwt.sign({}, privateKey, {
  algorithm: 'ES256',
  expiresIn: '180d', // Tokens can be valid for up to 6 months
  issuer: teamId,
  header: {
    alg: 'ES256',
    kid: keyId
  }
});

console.log("Developer Token:", token);

Your iOS application must make a secure network request to your backend to fetch this Developer Token upon launch.

Requesting User Authorization

Once the iOS app has the Developer Token, it must ask the user for permission to access their Apple Music account. This utilizes the SKCloudServiceController from the StoreKit framework.

First, you must add the NSAppleMusicUsageDescription key to your Info.plist, explaining exactly why you need access.

Next, trigger the iOS system prompt to request authorization:

import StoreKit

func requestAppleMusicAccess(completion: @escaping (Bool) -> Void) {
    SKCloudServiceController.requestAuthorization { status in
        if status == .authorized {
            completion(true)
        } else {
            completion(false)
        }
    }
}

Fetching the Music User Token

If the user grants permission, you use the Developer Token to request the Music User Token directly from the StoreKit controller.

func fetchUserToken(developerToken: String) {
    let controller = SKCloudServiceController()
    
    controller.requestUserToken(forDeveloperToken: developerToken) { userToken, error in
        guard let userToken = userToken else {
            print("Failed to fetch User Token: \(error?.localizedDescription ?? "")")
            return
        }
        
        // You now have both tokens required for private API calls
        print("Music User Token: \(userToken)")
    }
}

Querying the Playback History

With both tokens securely in memory, you can execute an authenticated HTTP GET request to the Apple Music REST API to fetch the user’s private data, such as their Heavy Rotation history.

func fetchHeavyRotation(developerToken: String, userToken: String) {
    let url = URL(string: "https://api.music.apple.com/v1/me/history/heavy-rotation")!
    var request = URLRequest(url: url)
    
    // Inject the dual tokens into the authorization headers
    request.addValue("Bearer \(developerToken)", forHTTPHeaderField: "Authorization")
    request.addValue(userToken, forHTTPHeaderField: "Music-User-Token")
    
    URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data else { return }
        
        // The data contains a JSON payload of the user's most played albums and playlists
        print(String(data: data, encoding: .utf8)!)
    }.resume()
}

By correctly implementing this cryptographic handshake, iOS applications can securely interface with Apple’s backend infrastructure, unlocking highly personalized, data-driven audio features while adhering to Apple’s strict privacy requirements.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.