Skip to main content

How to Build a Music Recommendation Engine with Streaming API Data

Published on May 25, 2026

How to Build a Music Recommendation Engine with Streaming API Data

What Is a Music Recommendation Engine?

A music recommendation engine analyzes a listener's behavior (favorite tracks, playlist patterns, skip rates) and predicts what they want to hear next. It turns raw listening data into ranked suggestions, powering features like "Discover Weekly" or "Made for You" playlists inside your app.

How Streaming Services Power Recommendations

Every streaming platform collects signals about what users play, save, skip, and repeat. These signals feed two core recommendation strategies that you can implement on top of streaming API data.

Collaborative Filtering vs Content-Based Filtering

ApproachHow It WorksBest For
Collaborative filteringFinds users with similar listening patterns and recommends what those "neighbors" enjoySurfacing unexpected discoveries across genres
Content-based filteringAnalyzes track attributes (genre, tempo, energy, key) and recommends similar-sounding tracksKeeping recommendations within a user's comfort zone
HybridCombines both signals with weighted scoringProduction-grade engines that balance novelty and relevance

Most production recommendation engines use a hybrid approach. Collaborative filtering catches patterns that metadata misses ("people who listen to this jazz playlist also love this electronic artist"), while content-based filtering prevents wild outliers.

The Role of Listening History and Favorites Data

Your recommendation engine is only as good as its input signals. The highest-value data points from streaming services include:

  • Favorite/saved tracks: Strong positive signal. A user explicitly chose to keep this.
  • Playlist composition: Reveals genre clusters, mood patterns, and contextual listening habits.
  • Play frequency: Tracks played repeatedly carry more weight than one-off listens.
  • Recency: Recent favorites matter more than tracks saved three years ago.

The challenge: each streaming service exposes this data through different endpoints, authentication flows, and response formats. Building against one service is straightforward. Building against ten is a full-time job.

Fetching User Data Across Services with a Unified API

To build a recommendation engine that works for users regardless of their streaming service, you need normalized access to listening data. This means handling OAuth flows for each platform, paginating through different response structures, and mapping fields like "saved tracks" (Spotify) to "library songs" (Apple Music) to "liked videos" (YouTube Music).

MusicAPI solves this by providing a single REST interface across 10+ streaming services. One authentication flow, one response format, one set of endpoints.

Here is how you fetch a user's favorite tracks after they have authenticated through MusicAPI:

// Fetch user's favorite tracks (works across all connected services)
const response = await fetch('https://api.musicapi.com/user/favorite-tracks', {
  headers: {
    'Authorization': `Bearer ${userAccessToken}`,
    'x-api-key': process.env.MUSICAPI_KEY
  }
});

const { data } = await response.json();

// Response shape is identical regardless of streaming service:
// {
//   "data": [
//     {
//       "id": "track_abc123",
//       "name": "Bohemian Rhapsody",
//       "artist": "Queen",
//       "album": "A Night at the Opera",
//       "genre": ["Rock", "Progressive Rock"],
//       "duration": 354,
//       "isrc": "GBUM71029604"
//     }
//   ]
// }

You can pull playlist data the same way to understand how users organize their listening:

// Get all user playlists
const playlists = await fetch('https://api.musicapi.com/user/playlists', {
  headers: {
    'Authorization': `Bearer ${userAccessToken}`,
    'x-api-key': process.env.MUSICAPI_KEY
  }
});

// Then fetch tracks from each playlist
const playlistTracks = await fetch(
  `https://api.musicapi.com/playlist/${playlistId}/tracks`,
  {
    headers: {
      'Authorization': `Bearer ${userAccessToken}`,
      'x-api-key': process.env.MUSICAPI_KEY
    }
  }
);

Normalizing Taste Signals from Spotify, Apple Music, YouTube Music

With MusicAPI, normalization happens at the API layer. You receive the same response shape whether the user connected Spotify, Apple Music, YouTube Music, Tidal, Deezer, or any other supported service. This means your recommendation logic stays clean:

// Build a taste profile from normalized data
function buildTasteProfile(favoriteTracks, playlistTracks) {
  const genreCounts = {};
  const artistCounts = {};
  
  const allTracks = [...favoriteTracks, ...playlistTracks];
  
  for (const track of allTracks) {
    // Genre frequency
    for (const genre of track.genre || []) {
      genreCounts[genre] = (genreCounts[genre] || 0) + 1;
    }
    // Artist frequency
    artistCounts[track.artist] = (artistCounts[track.artist] || 0) + 1;
  }
  
  return {
    topGenres: Object.entries(genreCounts)
      .sort((a, b) => b[1] - a[1])
      .slice(0, 10),
    topArtists: Object.entries(artistCounts)
      .sort((a, b) => b[1] - a[1])
      .slice(0, 20),
    totalTracks: allTracks.length
  };
}

No per-service adapters. No field mapping. No "if Spotify, use track.artists[0].name; if Apple, use track.attributes.artistName" conditionals scattered through your codebase.

Building a Simple Recommendation Algorithm

With a taste profile built from real user data, you can score candidate tracks using genre overlap and artist affinity. Here is a scoring function that ranks tracks by how well they match a user's established preferences:

function scoreTrack(candidateTrack, tasteProfile) {
  let score = 0;
  const { topGenres, topArtists } = tasteProfile;
  
  // Genre overlap scoring (max 50 points)
  for (const genre of candidateTrack.genre || []) {
    const genreEntry = topGenres.find(([g]) => g === genre);
    if (genreEntry) {
      const [, frequency] = genreEntry;
      score += Math.min(frequency * 5, 50);
    }
  }
  
  // Artist affinity scoring (max 30 points)
  const artistEntry = topArtists.find(([a]) => a === candidateTrack.artist);
  if (artistEntry) {
    const [, frequency] = artistEntry;
    score += Math.min(frequency * 10, 30);
  }
  
  // Novelty bonus: slight boost for tracks by new artists (max 20 points)
  if (!artistEntry && score > 0) {
    score += 20; // Genre match but new artist = discovery potential
  }
  
  return score;
}

// Generate recommendations from a candidate pool
function recommend(candidateTracks, tasteProfile, limit = 25) {
  return candidateTracks
    .map(track => ({ track, score: scoreTrack(track, tasteProfile) }))
    .filter(({ score }) => score > 0)
    .sort((a, b) => b.score - a.score)
    .slice(0, limit)
    .map(({ track }) => track);
}

This gives you a working recommendation engine in under 50 lines. The candidate pool can come from curated catalogs, trending charts, or tracks found in other users' playlists (collaborative filtering). For production, you would layer in additional signals: tempo matching, release recency, and explicit diversity constraints to avoid recommending the same artist repeatedly.

Scaling Recommendations Across 10+ Services

Building recommendations for one streaming service is a weekend project. Building them across Spotify, Apple Music, YouTube Music, Tidal, Deezer, Amazon Music, and others is an engineering commitment that can consume months.

Why a Single Integration Point Matters

Each streaming service you add multiplies your maintenance surface:

ConcernPer-Service WorkWith MusicAPI
OAuth implementationCustom flow per service (PKCE, server-side, refresh logic)One unified auth flow
Token refreshDifferent expiry times, refresh endpoints, error codesHandled automatically
Rate limitingDifferent limits, headers, backoff strategiesManaged at the API layer
Response normalizationMap each service's schema to your internal modelPre-normalized responses
New service supportFull integration cycle (weeks to months)Available on day one

Every hour you spend on OAuth plumbing or response mapping is an hour not spent improving your recommendation algorithm. MusicAPI handles the unified authentication, normalized responses, and rate limit management so your team focuses on the ML and product work that differentiates your app.

You can pull favorite tracks from Spotify, playlist tracks from YouTube, and profile data from any supported service with the same code. Your recommendation engine stays service-agnostic by design.

FAQ

How much listening data do I need to generate good recommendations?

A minimum of 20 to 30 favorite tracks or 3 to 5 playlists gives you enough genre and artist signals to produce meaningful recommendations. More data improves accuracy, but even a small set of strong positive signals (explicitly saved tracks) outperforms hundreds of passively logged plays.

Can I build a music recommendation engine without machine learning?

Yes. The scoring algorithm shown in this article uses straightforward frequency-based matching with no ML framework required. Genre overlap and artist affinity scoring work well for small to medium catalogs. You only need ML (matrix factorization, neural collaborative filtering) when operating at scale with millions of users and needing sub-second personalization.

How do I handle users who listen on multiple streaming services?

Use a unified API like MusicAPI to pull listening data from all connected services into a single taste profile. When a user connects both Spotify and YouTube Music, you merge their favorites and playlists into one normalized dataset before running your scoring algorithm. This gives you a more complete picture of their taste than any single service provides.

What is the difference between collaborative filtering and content-based filtering for music?

Collaborative filtering recommends tracks based on what similar users enjoy. It finds patterns across listening behaviors without analyzing the music itself. Content-based filtering recommends tracks based on audio attributes and metadata (genre, tempo, mood) that match what a user already likes. Most production systems combine both for better results.

How do I keep recommendations fresh and avoid repetition?

Apply a recency bias that weights recently saved tracks higher than old ones. Add diversity constraints: cap the number of recommendations from any single artist, require a minimum genre spread, and inject a small percentage of random "exploration" tracks. Track which recommendations a user has already seen and exclude them from future batches.

Can I use this approach to build personalized playlists?

Absolutely. Once you have scored and ranked tracks, you can create playlists directly through MusicAPI. Fetch recommendations using the algorithm above, then use the playlist creation endpoints to push a curated list back to the user's streaming service. This powers features like "Your Weekly Mix" or "Discovery Playlist" inside your app.

Start Building Your Recommendation Engine

The foundation of any music recommendation engine is quality listening data, delivered in a consistent format, across every service your users care about. You have seen how to fetch that data, build taste profiles, and score tracks with a simple algorithm that scales.

Ready to skip months of OAuth and SDK work? Start your free MusicAPI trial and connect 10+ streaming services with one unified API.