Published on July 19, 2026

Every music streaming platform assigns its own internal ID to every track. The same song by the same artist has a completely different identifier on Spotify than it does on Apple Music, YouTube Music, Tidal, or Deezer. This makes cross-platform matching one of the hardest problems in music app development.
ISRCs (International Standard Recording Codes) were supposed to solve this. In theory, every recording gets a unique 12-character code that works across all platforms. In practice, ISRC data is inconsistent, sometimes missing, and occasionally wrong.
This post explains how music metadata identifiers work, why cross-platform matching is harder than it looks, and how to build reliable matching into your application without maintaining per-service lookup logic.
Each streaming platform maintains its own content catalog with its own identifier system. When a label or distributor delivers a track to Spotify, Apple Music, and Tidal, each platform ingests it independently and assigns its own internal ID.
Here is the same track across four platforms:
| Platform | Track ID Format | Example |
|---|---|---|
| Spotify | Base-62 hash | 4iV5W9uYEdYUVa79Axb7Rh |
| Apple Music | Numeric catalog ID | 1440818231 |
| YouTube Music | 11-char video ID | dQw4w9WgXcQ |
| Tidal | Numeric track ID | 77814764 |
| Deezer | Numeric track ID | 3135556 |
These IDs have zero overlap. You cannot derive one from another. They are not based on any shared standard. Each platform built its identifier system independently, and there is no universal registry mapping them together.
For developers, this creates three immediate problems:
The music industry has two standard identifiers designed to work across platforms. Neither is perfect.
An ISRC is a 12-character alphanumeric code assigned to a specific recording. Format: CC-XXX-YY-NNNNN (country, registrant, year, designation).
Example: USUM71703861 identifies one specific master recording. In theory, this code is the same on every platform that carries that recording.
Where ISRCs work well:
Where ISRCs break down:
UPCs identify albums/releases, not individual tracks. A 12-digit barcode standard. Useful for matching albums across platforms, but not granular enough for track-level matching.
Each platform also maintains additional internal identifiers:
| Identifier | Platform | Scope |
|---|---|---|
| Spotify URI | Spotify | Track, album, artist, playlist |
| Apple Music Catalog ID | Apple Music | Track, album, artist |
| Video ID | YouTube Music | Track (as video) |
| Tidal Track ID | Tidal | Track |
| Deezer Track ID | Deezer | Track |
| SoundCloud Track ID | SoundCloud | Track |
These are reliable within their own platform but useless for cross-platform matching.
MusicAPI normalizes metadata from all supported services into a consistent response format. When you fetch track data through MusicAPI, you get the same field names, the same nesting, and the same data types regardless of the source service.
Here is what a normalized track response looks like:
{
"id": "musicapi-normalized-id",
"title": "Never Gonna Give You Up",
"artist": "Rick Astley",
"album": "Whenever You Need Somebody",
"duration": 213,
"isrc": "USUM71703861",
"service": "spotify",
"serviceId": "4iV5W9uYEdYUVa79Axb7Rh"
}
Key normalization details:
title, artist, album, duration across all services. No more mapping track.name vs. attributes.name vs. snippet.title.This normalization means your cross-platform matching code does not need per-service field mappers. You write one set of matching logic, and it works for data from all 12+ supported services.
Dealing with metadata headaches across multiple music services? MusicAPI normalizes track data, ISRCs, and identifiers from 12+ platforms into one consistent format. No per-service parsing required.
Here is a practical pattern for matching a track across services. The approach: use MusicAPI to fetch track data from multiple services, then match using ISRC (when available) or fuzzy metadata matching (as fallback).
// Authenticate via MusicAPI's unified OAuth first
// See: https://musicapi.com/docs/user-authentication/getting-started
const SERVICES = ['spotify', 'apple_music', 'youtube_music', 'tidal', 'deezer'];
// Step 1: Search for a track on all services
async function findTrackAcrossServices(query, userToken) {
const results = {};
for (const service of SERVICES) {
const response = await fetch(
`https://api.musicapi.com/v1/search/tracks?q=${encodeURIComponent(query)}`,
{
headers: {
'Authorization': `Bearer ${userToken}`,
'X-Music-Service': service
}
}
);
const data = await response.json();
results[service] = data.tracks || [];
}
return results;
}
// Step 2: Match by ISRC (strongest signal)
function matchByIsrc(resultsByService) {
const isrcMap = {};
for (const [service, tracks] of Object.entries(resultsByService)) {
for (const track of tracks) {
if (track.isrc) {
if (!isrcMap[track.isrc]) {
isrcMap[track.isrc] = [];
}
isrcMap[track.isrc].push({
service,
serviceId: track.serviceId,
title: track.title,
artist: track.artist
});
}
}
}
return isrcMap;
}
// Step 3: Fuzzy fallback for tracks without ISRCs
function fuzzyMatch(track, candidates) {
const normalize = (str) => str.toLowerCase().replace(/[^a-z0-9]/g, '');
const targetTitle = normalize(track.title);
const targetArtist = normalize(track.artist);
return candidates.filter(candidate => {
const titleMatch = normalize(candidate.title) === targetTitle;
const artistMatch = normalize(candidate.artist).includes(targetArtist)
|| targetArtist.includes(normalize(candidate.artist));
return titleMatch && artistMatch;
});
}
// Usage
const results = await findTrackAcrossServices('Never Gonna Give You Up Rick Astley', token);
const isrcMatches = matchByIsrc(results);
// isrcMatches['USUM71703861'] contains the track on every service that has the ISRC
console.log(isrcMatches);
// {
// 'USUM71703861': [
// { service: 'spotify', serviceId: '4iV5W9uYEdYUVa79Axb7Rh', ... },
// { service: 'apple_music', serviceId: '1440818231', ... },
// { service: 'tidal', serviceId: '77814764', ... },
// { service: 'deezer', serviceId: '3135556', ... }
// ]
// }
This pattern gives you a reliable cross-platform track mapping. ISRC matching is preferred because it is deterministic. Fuzzy matching serves as a fallback for tracks where ISRCs are missing or inconsistent.
Cross-platform metadata matching has several failure modes that catch developers off guard. Here are the most common ones and how to handle them.
Some recordings have multiple ISRCs assigned by different distributors or labels. When this happens, the "same" track may have different ISRCs on different platforms. Your matching logic should:
Streaming platforms often have different catalog entries for the same recording in different regions. A track available in the US catalog may have a different ID (and sometimes a different ISRC) than the same track in the EU catalog. Handle this by:
Not every platform returns every metadata field for every track. Common gaps:
| Field | Often Missing On |
|---|---|
| ISRC | SoundCloud, Audius, Audiomack |
| Album name | YouTube Music (for singles/videos) |
| Duration | Rare, but format varies |
| Release date | Varies by service |
| Genre | Most platforms (via track endpoint) |
Your matching pipeline should handle missing fields gracefully. Do not fail the entire match if one field is null. Instead, weight your confidence score based on which fields are available and matching.
Artist and track names can contain accents, non-Latin characters, and special characters that render differently across platforms. Always normalize Unicode (NFC normalization), strip diacritics for comparison, and compare lowercase versions.
Here is a battle-tested matching strategy that combines ISRC lookup, fuzzy matching, and confidence scoring.
function calculateMatchConfidence(trackA, trackB) {
let confidence = 0;
// ISRC match (strongest)
if (trackA.isrc && trackB.isrc && trackA.isrc === trackB.isrc) {
confidence = 0.95;
}
// Title + artist matching
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9\s]/g, '').trim();
const titleA = normalize(trackA.title);
const titleB = normalize(trackB.title);
const artistA = normalize(trackA.artist);
const artistB = normalize(trackB.artist);
const titleMatch = titleA === titleB;
const artistMatch = artistA === artistB
|| artistA.includes(artistB)
|| artistB.includes(artistA);
if (titleMatch && artistMatch && confidence < 0.85) {
confidence = 0.85;
}
// Duration check (bonus confidence)
if (trackA.duration && trackB.duration) {
const durationDiff = Math.abs(trackA.duration - trackB.duration);
if (durationDiff <= 3 && confidence >= 0.7) {
confidence = Math.min(confidence + 0.05, 0.98);
}
}
return confidence;
}
Once you match a track across services, cache the mapping. Store tuples of (serviceA_id, serviceB_id, isrc, confidence) so you do not repeat expensive cross-service lookups for tracks you have already matched.
MusicAPI's normalized responses make this caching straightforward because the serviceId field always contains the original platform-specific identifier, and the response format is consistent across all services.
A music metadata API provides structured access to information about songs, albums, and artists: titles, ISRCs, durations, album art, and identifiers. Each streaming platform has its own metadata API. MusicAPI normalizes metadata from 12+ services into one consistent format.
An ISRC (International Standard Recording Code) is a 12-character code that uniquely identifies a sound recording. Format: CC-XXX-YY-NNNNN. Use ISRCs to match the same track across different streaming platforms. Most major label tracks have ISRCs; independent releases may not. MusicAPI includes ISRCs in normalized responses when the source platform provides them.
Each streaming platform maintains its own content catalog and assigns internal IDs independently. There is no shared registry. A label delivers the same track file to each platform, and each platform creates its own database entry with its own identifier. This is why cross-platform matching requires either ISRCs or fuzzy metadata comparison.
ISRC matching is the most reliable method, with 95%+ accuracy for major label content. However, ISRCs can be inconsistent: some tracks have multiple ISRCs from different distributors, remastered versions may share ISRCs with originals, and independent releases often lack ISRCs entirely. Always combine ISRC matching with a fuzzy metadata fallback.
Yes. MusicAPI normalizes track data from Spotify, Apple Music, YouTube Music, Tidal, Deezer, SoundCloud, and more into one response format. You can search for a track across all services through the same endpoint and use ISRCs or normalized metadata for matching.
Title, artist, album, and duration are available on nearly every platform. ISRCs are available on most major platforms but may be missing on SoundCloud, Audius, and Audiomack. Genre data is inconsistent across services. MusicAPI's supported features page shows exactly which fields are available per service.
Regional licensing restrictions mean some tracks are available in one country's catalog but not another. Your matching logic should handle "no match found" as a valid outcome, not an error. Track the match result per service and surface availability information in your UI so users understand why a track is missing.
Ready to skip months of OAuth and SDK work? Start your free MusicAPI trial and connect 10+ streaming services with one unified API.