Skip to main content

How Music Metadata Normalization Works Across 12 Streaming Services

Published on August 8, 2026

How Music Metadata Normalization Works Across 12 Streaming Services

How Music Metadata Normalization Works Across 12 Streaming Services

Every streaming service stores and returns track metadata differently. Spotify calls it name. Apple Music calls it attributes.name. YouTube Music buries it in snippet.title. If you are building an app that pulls data from multiple services, you either write custom parsers for each one or you use a normalized API that does it for you.

This post breaks down exactly how metadata differs across services, what a normalized response looks like, and why it matters for your codebase.

The Metadata Problem: Why Every Streaming Service Returns Different Data

Music metadata normalization is the process of converting each streaming service's unique data format into a single, consistent schema. Each of the 12+ major streaming platforms uses different field names, data types, and nesting structures for the same information. Without normalization, developers write and maintain separate parsing logic for every service they integrate.

Field Name Differences (trackName vs. title vs. name)

The most basic piece of metadata, a track's title, has a different field name on almost every service:

ServiceTrack title fieldNesting depth
SpotifynameTop-level
Apple Musicattributes.name1 level deep
YouTube Musicsnippet.title1 level deep
TidaltitleTop-level
DeezertitleTop-level
SoundCloudtitleTop-level
Amazon MusictitleVaries by endpoint
NapsternameTop-level

Four different field names for the same data point. Your code either handles all four, or you normalize upstream.

Artist Representation (String vs. Array vs. Nested Object)

Artist data is where things get really inconsistent:

  • Spotify returns an array of artist objects: [{"name": "Queen", "id": "1dfeR..."}, {"name": "David Bowie", "id": "0oSG..."}]
  • Apple Music returns a single string: "Queen & David Bowie"
  • YouTube Music returns a channel name that sometimes includes "- Topic": "Queen - Topic"
  • Tidal returns a nested object: {"name": "Queen", "id": 7553}
  • Deezer returns an object with separate fields: {"name": "Queen", "id": 412}

A collaboration between two artists looks fundamentally different on every platform. Spotify gives you structured data you can iterate. Apple Music gives you a string you have to split. YouTube Music gives you a channel name that may not even be the artist's real name.

Album Art, Duration, and ISRC: Where Formats Diverge

Beyond titles and artists, nearly every metadata field has service-specific quirks:

FieldSpotifyApple MusicYouTube MusicTidalDeezer
Album artalbum.images[] (array of sizes)attributes.artwork (template URL with {w}x{h})snippet.thumbnails (object with size keys)album.cover (base URL + size suffix)album.cover (base URL + ?size=)
Durationduration_ms (int, ms)attributes.durationInMillis (int, ms)Not in search resultsduration (int, seconds)duration (int, seconds)
ISRCexternal_ids.isrcattributes.isrcNot availableisrcisrc
Explicit flagexplicit (bool)attributes.contentRating (string)Not availableexplicit (bool)explicit_lyrics (bool)
Release datealbum.release_date (string, variable precision)attributes.releaseDate (ISO 8601)Not in search resultsstreamStartDate (ISO 8601)release_date (YYYY-MM-DD)

Duration alone requires a conditional: is it milliseconds or seconds? Album art requires different URL construction logic per service. ISRC, the one universal track identifier, is not even available on YouTube Music.

What a Normalized Music API Response Looks Like

A normalized music API response maps every service's metadata into a single schema with consistent field names, types, and formats. MusicAPI returns the same track object structure regardless of which streaming service the data came from. This means your frontend, database, and business logic code works with one data shape instead of twelve.

The Unified Track Object: Fields, Types, and Guarantees

Every track returned by MusicAPI follows this structure:

{
  "id": "track_abc123",
  "title": "Bohemian Rhapsody",
  "artist": "Queen",
  "artists": [{"name": "Queen", "id": "artist_xyz"}],
  "album": "A Night at the Opera",
  "album_id": "album_def456",
  "artwork_url": "https://cdn.musicapi.com/artwork/abc123/600x600.jpg",
  "duration_ms": 354320,
  "isrc": "GBUM71029604",
  "explicit": false,
  "release_date": "1975-10-31",
  "service": "spotify",
  "service_id": "7tFiyTwD0nx5a1eklYtX2J",
  "available": true
}

Key guarantees:

  • title is always a string, always the clean track title (no remaster/remix tags appended unless they are part of the official title)
  • artist is always a string with the primary artist name
  • artists is always an array, even for single-artist tracks
  • duration_ms is always in milliseconds, always an integer
  • artwork_url is always a resolved, CDN-backed URL (no template strings to construct)
  • isrc is present when the source service provides it, null otherwise
  • release_date is always ISO 8601 format

The Unified Playlist and Album Objects

Playlists and albums follow the same normalization principles. A playlist response always includes:

{
  "id": "playlist_ghi789",
  "name": "Chill Vibes",
  "description": "Relaxing tracks for focus time",
  "owner": "user_jkl012",
  "track_count": 47,
  "artwork_url": "https://cdn.musicapi.com/artwork/ghi789/300x300.jpg",
  "service": "apple_music",
  "service_id": "pl.a1b2c3d4",
  "public": true
}

Code Example: Raw Spotify Response vs. Normalized MusicAPI Response

Here is the same track as returned by Spotify's API vs. MusicAPI:

Raw Spotify response (trimmed):

{
  "name": "Bohemian Rhapsody - Remastered 2011",
  "artists": [{"name": "Queen", "id": "1dfeR4PH6GvB29R", "uri": "spotify:artist:1dfeR4PH6GvB29R"}],
  "album": {
    "name": "A Night at the Opera (Remastered 2011)",
    "images": [
      {"url": "https://i.scdn.co/image/ab67616d0000b273...", "width": 640, "height": 640},
      {"url": "https://i.scdn.co/image/ab67616d00001e02...", "width": 300, "height": 300}
    ],
    "release_date": "1975-10-31",
    "release_date_precision": "day"
  },
  "duration_ms": 354320,
  "explicit": false,
  "external_ids": {"isrc": "GBUM71029604"},
  "id": "7tFiyTwD0nx5a1eklYtX2J",
  "uri": "spotify:track:7tFiyTwD0nx5a1eklYtX2J"
}

Normalized MusicAPI response:

{
  "title": "Bohemian Rhapsody",
  "artist": "Queen",
  "artists": [{"name": "Queen", "id": "artist_1dfeR4"}],
  "album": "A Night at the Opera",
  "artwork_url": "https://cdn.musicapi.com/artwork/7tFiy/600x600.jpg",
  "duration_ms": 354320,
  "isrc": "GBUM71029604",
  "explicit": false,
  "release_date": "1975-10-31",
  "service": "spotify",
  "service_id": "7tFiyTwD0nx5a1eklYtX2J"
}

Notice: the remaster tag is stripped from the title. The artist is extracted as a clean string. The artwork URL is a direct link, not a Spotify CDN URL that changes format between endpoints. The album name is also cleaned.

How MusicAPI Normalizes Data Behind the Scenes

MusicAPI uses service-specific adapter modules that map each platform's raw API response into the unified schema. Each adapter knows the exact field paths, data types, and quirks of its target service. When a service changes its API (which happens regularly), only the adapter for that service needs updating. Your code stays the same.

Service-Specific Adapters and Field Mapping

Each adapter does three things:

  1. Field mapping: translates service-specific field names to the unified schema (e.g., Spotify's name to title, Apple Music's attributes.name to title)
  2. Type coercion: converts values to consistent types (seconds to milliseconds, template URLs to resolved URLs, nested objects to flat strings)
  3. Data cleaning: strips remaster/reissue tags, normalizes artist collaboration strings, resolves artwork to a CDN URL

Handling Missing Fields and Optional Data

Not every service provides every field. YouTube Music does not return ISRCs. SoundCloud does not return album information for most tracks. The normalized response uses null for missing fields instead of omitting them, so your code can always check if (track.isrc) without first checking if the field exists.

This is a deliberate API design choice. Nullable fields are safer than absent fields because they prevent undefined reference errors and make TypeScript/JSON Schema validation straightforward.

Keeping Normalized Responses Consistent as Services Change Their APIs

Streaming services update their APIs frequently. Spotify has changed their response format several times. Apple Music introduced MusicKit JS v3 with different field names. When these changes happen, MusicAPI updates the relevant adapter. The normalized response your app receives stays identical.

This is the core value of the normalization layer: it absorbs API instability so your code does not have to. Check the supported features page for the current field coverage per service.

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

Building on Normalized Data: Practical Developer Benefits

Normalized metadata is not just a convenience. It changes how you architect your application. When every track, playlist, and album follows the same schema regardless of source, your database models, search indexes, and frontend components all get simpler.

Write Once, Query Twelve Services

With normalized responses, your data access layer works the same for every supported service:

// This function works for Spotify, Apple Music, Tidal, Deezer, and more
async function getPlaylistTracks(playlistId, service) {
  const response = await musicapi.get(`/playlists/${playlistId}/tracks`, {
    params: { service }
  });
  // response.data.items always has the same shape
  return response.data.items;
}

No switch statements. No service-specific response handlers. One function handles all twelve services.

Simplifying Database Storage and Search Indexing

When every track follows the same schema, your database table matches the API response:

CREATE TABLE tracks (
  id TEXT PRIMARY KEY,
  title TEXT NOT NULL,
  artist TEXT NOT NULL,
  album TEXT,
  duration_ms INTEGER,
  isrc TEXT,
  artwork_url TEXT,
  service TEXT NOT NULL,
  service_id TEXT NOT NULL
);

One table. One index. One full-text search configuration. Compare that to maintaining separate tables or columns for each service's idiosyncratic field structure.

Cross-Service Deduplication Using ISRCs and Normalized Titles

ISRCs (International Standard Recording Codes) are the gold standard for identifying the same recording across services. When MusicAPI surfaces ISRCs from services that provide them, you can match tracks across platforms:

function findDuplicates(tracks) {
  const byIsrc = new Map();
  for (const track of tracks) {
    if (track.isrc) {
      if (!byIsrc.has(track.isrc)) byIsrc.set(track.isrc, []);
      byIsrc.get(track.isrc).push(track);
    }
  }
  return Array.from(byIsrc.values()).filter(group => group.length > 1);
}

For services that do not provide ISRCs, fall back to matching on normalized title + artist + duration (with tolerance). The normalized API makes this possible because titles and artists are already cleaned and formatted consistently.

FAQ

What is music metadata normalization?

Music metadata normalization converts each streaming service's unique data format into a single, consistent schema. Instead of handling different field names, types, and structures for Spotify, Apple Music, and YouTube Music, you work with one unified format.

Which streaming services does MusicAPI normalize data from?

MusicAPI normalizes metadata from 12+ streaming services, including Spotify, Apple Music, YouTube Music, Tidal, Deezer, SoundCloud, Amazon Music, Napster, and more.

What happens when a streaming service does not provide a field like ISRC?

The normalized response returns null for fields the source service does not provide. This applies to ISRCs on YouTube Music, album data on SoundCloud, and other service-specific gaps. Your code can safely check for null without worrying about missing keys.

Does normalization change the actual music data?

No. Normalization reformats and restructures metadata without changing its meaning. Track titles get cleaned (remaster tags stripped), artist names get extracted from complex objects, and durations get converted to a consistent unit. The underlying data stays accurate.

How does MusicAPI handle tracks with multiple artists?

The normalized response includes both an artist string (primary artist) and an artists array (all credited artists). This gives you a clean display name and structured data for linking to artist pages.

Can I still access the original service-specific data?

Yes. The service_id field in every normalized response lets you map back to the original resource on the source service. You can use this to construct deep links to the track on Spotify, Apple Music, or any other platform. For the raw auth tokens, see the original auth tokens endpoint.

How often do streaming services change their API response formats?

Frequently enough that it matters. Major services update their APIs multiple times per year. MusicAPI's adapter layer absorbs these changes so your integration stays stable. Check the API docs for the current normalized schema.