Skip to main content

How to Normalize Music Data Across Spotify, Apple Music, YouTube Music, and More

Published on May 27, 2026

How to Normalize Music Data Across Spotify, Apple Music, YouTube Music, and More

Every streaming service speaks its own dialect of JSON. Spotify calls it track_name. Apple Music calls it attributes.name. YouTube Music nests it three levels deep. If you are building a cross-platform music app, you either normalize that data yourself or watch your codebase turn into a sprawling mess of service-specific adapters. This guide walks you through the problem, the DIY fix, and the faster path with a unified API.

Why Music Data Normalization Matters

Music data normalization is the process of transforming inconsistent response formats from multiple streaming APIs into a single, predictable schema. Without it, every new service you add multiplies your mapping code, your test surface, and your bug count. Normalized data keeps your application logic clean and your team shipping features instead of chasing field-name differences.

The Problem: Every Service Returns Different JSON

Ask three streaming services for the same track and you get three wildly different JSON shapes. Field names differ. Nesting depth differs. Some services include duration in milliseconds, others in seconds, and some omit it entirely for certain content types.

Here is what a track response looks like from three major services:

Service A (REST API)

{
  "track": {
    "id": "6rqhFgbbKwnb9MLmUQDhG6",
    "name": "Bohemian Rhapsody",
    "artists": [
      { "id": "1dfeR4HaWDbWqFHLkxsg1d", "name": "Queen" }
    ],
    "album": {
      "name": "A Night at the Opera",
      "images": [
        { "url": "https://i.scdn.co/image/abc123", "height": 640, "width": 640 }
      ]
    },
    "duration_ms": 354947,
    "explicit": false,
    "external_urls": {
      "spotify": "https://open.spotify.com/track/6rqhFgbbKwnb9MLmUQDhG6"
    }
  }
}

Service B (MusicKit API)

{
  "data": [
    {
      "id": "1450330685",
      "type": "songs",
      "attributes": {
        "name": "Bohemian Rhapsody",
        "artistName": "Queen",
        "albumName": "A Night at the Opera",
        "durationInMillis": 354320,
        "artwork": {
          "url": "https://is1-ssl.mzstatic.net/image/{w}x{h}/abc.jpg",
          "width": 1400,
          "height": 1400
        },
        "contentRating": "clean",
        "url": "https://music.apple.com/us/album/bohemian-rhapsody/1450330680?i=1450330685"
      }
    }
  ]
}

Service C (Data API)

{
  "kind": "youtube#video",
  "items": [
    {
      "id": "fJ9rUzIMcZQ",
      "snippet": {
        "title": "Bohemian Rhapsody",
        "channelTitle": "Queen Official",
        "thumbnails": {
          "high": {
            "url": "https://i.ytimg.com/vi/fJ9rUzIMcZQ/hqdefault.jpg",
            "width": 480,
            "height": 360
          }
        }
      },
      "contentDetails": {
        "duration": "PT5M55S"
      }
    }
  ]
}

The Differences at a Glance

FieldService AService BService C
Track titletrack.namedata[0].attributes.nameitems[0].snippet.title
Artisttrack.artists[0].namedata[0].attributes.artistNameitems[0].snippet.channelTitle
Albumtrack.album.namedata[0].attributes.albumNameNot available
Durationduration_ms (ms)durationInMillis (ms)duration (ISO 8601)
Artwork URLDirect URLTemplate with {w}x{h}Nested by size key
Explicit flagexplicit (boolean)contentRating (string)Not available

That is six fields, three completely different access patterns, and three different data types for the same logical concept. Now multiply that by ten services and twenty endpoints. The mapping code alone becomes a maintenance nightmare.

Building a Normalization Layer from Scratch

If you choose the DIY route, you need a target schema and a transformer for each service. Here is how that typically looks.

Step 1: Define Your Canonical Schema

interface NormalizedTrack {
  id: string;
  serviceId: string;
  title: string;
  artists: { id: string; name: string }[];
  album: string | null;
  durationMs: number | null;
  artworkUrl: string | null;
  isExplicit: boolean | null;
  sourceUrl: string | null;
}

Step 2: Write a Transformer Per Service

function normalizeServiceA(raw: any): NormalizedTrack {
  return {
    id: raw.track.id,
    serviceId: "service_a",
    title: raw.track.name,
    artists: raw.track.artists.map((a: any) => ({ id: a.id, name: a.name })),
    album: raw.track.album?.name ?? null,
    durationMs: raw.track.duration_ms ?? null,
    artworkUrl: raw.track.album?.images?.[0]?.url ?? null,
    isExplicit: raw.track.explicit ?? null,
    sourceUrl: raw.track.external_urls?.spotify ?? null,
  };
}

function normalizeServiceB(raw: any): NormalizedTrack {
  const song = raw.data[0];
  const attrs = song.attributes;
  return {
    id: song.id,
    serviceId: "service_b",
    title: attrs.name,
    artists: [{ id: "", name: attrs.artistName }],
    album: attrs.albumName ?? null,
    durationMs: attrs.durationInMillis ?? null,
    artworkUrl: attrs.artwork?.url?.replace("{w}", "640").replace("{h}", "640") ?? null,
    isExplicit: attrs.contentRating === "explicit" ? true : false,
    sourceUrl: attrs.url ?? null,
  };
}

function normalizeServiceC(raw: any): NormalizedTrack {
  const item = raw.items[0];
  return {
    id: item.id,
    serviceId: "service_c",
    title: item.snippet.title,
    artists: [{ id: "", name: item.snippet.channelTitle }],
    album: null,
    durationMs: parseISO8601Duration(item.contentDetails.duration),
    artworkUrl: item.snippet.thumbnails?.high?.url ?? null,
    isExplicit: null,
    sourceUrl: `https://youtube.com/watch?v=${item.id}`,
  };
}

Handling Missing Fields and Service-Specific Extras

Every service has gaps. Some do not return album names for singles. Others skip explicit content flags entirely. Your normalization layer needs safe defaults and null handling for every field.

Then there are service-specific extras: audio quality tiers, lyrics availability, spatial audio support, content ratings by region. You have two choices: drop them or maintain a metadata escape hatch on your schema. Either way, the mapping table grows every time a service updates its API.

This is where the real cost hides. Building the initial transformers takes a few days. Maintaining them across API version changes, new services, deprecated fields, and edge cases takes months of ongoing work.

Using a Unified API Instead of DIY Normalization

MusicAPI solves this problem at the infrastructure layer. Instead of writing and maintaining transformers for each service, you call one endpoint and get a consistent JSON response regardless of the source platform.

Here is what a normalized track response looks like from MusicAPI:

{
  "id": "6rqhFgbbKwnb9MLmUQDhG6",
  "type": "track",
  "service": "spotify",
  "title": "Bohemian Rhapsody",
  "artists": [
    { "id": "1dfeR4HaWDbWqFHLkxsg1d", "name": "Queen" }
  ],
  "album": "A Night at the Opera",
  "durationMs": 354947,
  "artworkUrl": "https://i.scdn.co/image/abc123",
  "isExplicit": false,
  "sourceUrl": "https://open.spotify.com/track/6rqhFgbbKwnb9MLmUQDhG6"
}

That same shape comes back whether you query tracks from any of the 10+ supported streaming services. No per-service transformers. No null-checking gymnastics. No ISO 8601 duration parsing.

What MusicAPI Handles for You

ConcernDIYMusicAPI
OAuth + token refresh per serviceYou build and maintain itHandled automatically
Field name mappingCustom transformer per serviceUnified response schema
Duration format normalizationParse ms, seconds, ISO 8601Always milliseconds
Artwork URL templatesService-specific string replacementDirect URL, ready to use
Missing field handlingNull checks everywhereConsistent null semantics
Rate limiting per serviceTrack and throttle individuallyManaged at the API layer
New service supportBuild a new adapter from scratchAvailable on launch

You can query playlists, tracks, user profiles, and favorites across services with the same request structure. Check the full list of supported features to see what is available per service.

Ready to skip the mapping table and get consistent JSON from every streaming service? Start your free MusicAPI trial and connect to 10+ platforms with one integration.

Edge Cases: Live Tracks, Remixes, Multi-Artist Credits

Normalization gets harder at the edges. Here are the cases that break most DIY implementations.

Live Tracks and Alternate Versions

The same song can appear as a studio recording, a live version, a remastered edition, and an acoustic take. Some services append "(Live)" to the title. Others use a separate version field. A few bury this information in album metadata only. Your normalization layer needs a strategy for preserving version information without polluting the primary title field.

Remixes and Extended Mixes

Remix credits follow no standard convention. "Song Title (DJ X Remix)" might appear in the title, in a separate remix field, or only in the artist credits. Normalizing remix attribution requires parsing title strings, which is fragile and locale-dependent.

Multi-Artist Credits

Service A returns an array of artist objects. Service B concatenates artist names into a single string with "&" separators. Service C uses the channel owner as the sole artist. Splitting concatenated strings back into individual artists introduces edge cases around artist names that contain "&" or "feat." naturally.

Regional Availability and Content Restrictions

A track that exists on one service may be region-locked or unavailable on another. Your normalization layer needs to handle 404s and partial results gracefully. Returning a clean null or an availability flag beats crashing the entire request.

MusicAPI handles these edge cases across all supported services, so your application code stays focused on features rather than defensive parsing. You can also pull detailed playlist data per service: check playlist info for Apple Music or playlist tracks from SoundCloud without writing platform-specific code.

FAQ

What does music data normalization mean?

Music data normalization is the process of converting different JSON response formats from multiple streaming service APIs into a single, consistent schema. It ensures your application code works the same way regardless of which music service provided the data.

Why do streaming services return different JSON formats?

Each streaming service designed its API independently, with different naming conventions, nesting structures, data types, and available fields. There is no shared standard for music metadata APIs, so every service has its own response shape.

Can I normalize music data without building custom code?

Yes. A unified music API like MusicAPI handles normalization at the infrastructure level. You send one request and receive a standardized response, regardless of the source service. This eliminates the need to write and maintain per-service transformers.

How does MusicAPI handle authentication across multiple services?

MusicAPI provides a single authentication flow that covers all supported services. You initialize authentication once, handle the callback, and MusicAPI manages OAuth tokens, refresh cycles, and service-specific auth quirks behind the scenes.

What happens when a field is missing from one service but available on another?

A well-designed normalization layer returns null for missing fields instead of omitting them. This gives your application a predictable schema to work with. MusicAPI follows this pattern: every field in the response is always present, set to either a value or null.

Which music services can MusicAPI normalize data from?

MusicAPI supports 10+ streaming services including major platforms. The normalized response format stays identical across all of them. You can also review the supported features matrix to see which endpoints are available per service.

Is music data normalization only useful for track metadata?

No. Normalization applies to playlists, user profiles, albums, favorites, and search results. Any data type that differs between services benefits from a consistent schema. MusicAPI normalizes responses across all supported endpoint categories.

Start Building with Normalized Music Data

Every hour spent writing service-specific transformers is an hour not spent on your product. The gap between "it works with one service" and "it works with ten" is wider than most teams expect, and the maintenance cost compounds with every API update.

Ready to skip months of OAuth and SDK work? Start your free MusicAPI trial and connect 10+ streaming services with one unified API. Check out the getting started guide to have your first normalized response in minutes.