Published on July 1, 2026

Users do not care which streaming service hosts a song. They care about finding it. If your app only searches one catalog, you miss tracks that exist on other platforms, and you lose users who expect results from their preferred service. Cross-platform music search gives your app broader catalog coverage and lets users connect whichever service they already pay for.
Each streaming service exposes its own search API with its own quirks. The response shapes differ. The metadata fields differ. Even the way you paginate through results differs.
Here is what you are dealing with when you integrate three services natively:
| Aspect | Service A | Service B | Service C |
|---|---|---|---|
| Auth method | OAuth 2.0 PKCE | Developer token + user token | OAuth 2.0 with refresh |
| Search endpoint | GET /v1/search | GET /v1/catalog/{storefront}/search | POST /youtubei/v1/search |
| Track title field | name | attributes.name | title |
| Artist field | artists[0].name | attributes.artistName | artist.name |
| Album art field | album.images[0].url | attributes.artwork.url | thumbnails[0].url |
| Duration format | milliseconds (integer) | milliseconds (integer) | seconds (string) |
| Pagination | cursor-based (next URL) | offset + limit | continuation token |
That is three separate OAuth implementations, three response parsers, and three sets of error handling and rate limit logic. Add a fourth or fifth service and your search layer becomes a maintenance project of its own.
The real cost is not the initial build. It is the ongoing maintenance. When a service changes its API (and they do), you patch one integration without breaking the others. When a service deprecates a field, you update your normalizer. This busywork compounds with every service you add.
A unified music search API sits between your app and the streaming services. It handles the per-service authentication, sends the search query to each connected service, normalizes the responses into a single consistent format, and returns the results to you.
Your app sends one request. The API fans it out to every service your user has connected, collects the results, maps each service's proprietary fields to a standard schema, and returns a single array of tracks (or albums, or artists) with consistent field names.
This means you write one search function, parse one response shape, and handle one set of errors. MusicAPI does exactly this: it normalizes search responses across 20+ streaming services so your code stays clean regardless of how many services you support. You authenticate once through MusicAPI's SSO flow, and every connected service becomes searchable through the same endpoint.
Here is a working example using the MusicAPI search endpoint. The endpoint accepts a query string and returns normalized results from all services the user has connected.
Request:
curl -X POST https://api.musicapi.com/api/{userUUID}/search \
-H "Authorization: Token YOUR_CLIENT_ID" \
-H "Content-Type: application/json; charset=utf-8" \
-d '{
"query": "Bohemian Rhapsody",
"type": "track",
"limit": 5
}'
JavaScript example:
const searchTracks = async (userUUID, query) => {
const response = await fetch(
`https://api.musicapi.com/api/${userUUID}/search`,
{
method: "POST",
headers: {
"Authorization": "Token YOUR_CLIENT_ID",
"Content-Type": "application/json; charset=utf-8"
},
body: JSON.stringify({
query: query,
type: "track",
limit: 10
})
}
);
const data = await response.json();
return data;
};
// Search across all connected services with one call
const results = await searchTracks("user-uuid-here", "Bohemian Rhapsody");
console.log(results);
Normalized response shape:
{
"tracks": [
{
"service": "spotify",
"id": "4u7EnebtmKWzUH433cf5Qv",
"name": "Bohemian Rhapsody",
"artist": "Queen",
"album": "A Night at the Opera",
"duration": 354320,
"isrc": "GBUM71029604",
"artwork": "https://i.scdn.co/image/...",
"previewUrl": "https://p.scdn.co/mp3-preview/..."
},
{
"service": "apple_music",
"id": "1440833238",
"name": "Bohemian Rhapsody",
"artist": "Queen",
"album": "A Night at the Opera (Deluxe Edition)",
"duration": 354320,
"isrc": "GBUM71029604",
"artwork": "https://is1-ssl.mzstatic.com/image/...",
"previewUrl": "https://audio-ssl.itunes.apple.com/..."
}
]
}
Every result follows the same structure. No per-service conditional parsing. No if (service === 'spotify') { ... } else if (service === 'apple') { ... } blocks cluttering your frontend code.
The hardest part of cross-platform search is not sending the query. It is making sense of what comes back. Each service uses different field names, different data types, and different levels of detail for the same piece of information.
Here is how key metadata fields differ across services and how a unified API normalizes them:
| Metadata Field | Spotify | Apple Music | YouTube Music | Normalized (MusicAPI) |
|---|---|---|---|---|
| Track name | name | attributes.name | title | name |
| Artist | artists[0].name | attributes.artistName | artist.name | artist |
| Album | album.name | attributes.albumName | album.name | album |
| Duration | duration_ms (int, ms) | attributes.durationInMillis (int, ms) | lengthSeconds (string, sec) | duration (int, ms) |
| Album art | album.images[0].url | attributes.artwork.url (template) | thumbnails[0].url | artwork (direct URL) |
| ISRC | Not returned by default | attributes.isrc | Not available | isrc (when available) |
| Preview URL | preview_url | attributes.previews[0].url | Not available | previewUrl (when available) |
A few things stand out:
Duration formats vary. Some services return milliseconds as integers, others return seconds as strings. MusicAPI normalizes everything to milliseconds as an integer so your UI math stays consistent.
Album art requires post-processing. One service returns a template URL with {w}x{h} placeholders you need to replace. Another returns a direct URL at a fixed resolution. MusicAPI resolves these to direct, usable URLs.
ISRC availability is inconsistent. The International Standard Recording Code is useful for matching the same recording across services, but not every service returns it in search results. MusicAPI includes it when the underlying service provides it and supports ISRC-based search on services that allow it.
Preview URLs are not universal. Some services provide 30-second audio previews, others do not. The normalized response includes previewUrl when available and omits it cleanly when it is not, so your code can handle both cases without crashing.
This normalization layer is what makes cross-platform search practical at scale. Without it, every new service you add means another branch in your response parser. With it, you handle one response shape regardless of how many services sit behind it.
You can explore which services support specific features on the supported features page. For working with results after search (fetching full track details, reading playlists, or pulling playlist tracks), the same normalization applies across all endpoints.
MusicAPI supports 20+ streaming services through public endpoints. A single search call queries every service the user has connected. You do not need to specify which services to search; the API handles routing automatically.
You provide your own developer credentials for each service you want to support, but MusicAPI handles the per-user authentication and token management through its SSO system. Your app code only interacts with MusicAPI's auth flow, not each service's OAuth implementation individually.
Yes. Several services support ISRC-based search, which is useful for finding exact recordings across catalogs. Check the supported features matrix to see which services support ISRC lookup. Services that support it include Tidal, Apple Music, Deezer, and others.
The search only runs against services the user has authorized through MusicAPI's authentication flow. If a user has only connected two services, the search returns results from those two. No errors, no empty results from unconnected services. You can check which services a user has connected and prompt them to add more through the authentication flow.
Each streaming service enforces its own rate limits. MusicAPI handles per-service rate limiting server-side, queuing and retrying requests as needed so your app does not get 429 errors from individual services. You work within MusicAPI's own rate limits, which are documented on the rate limiting page.
Yes. Every result includes a service field that identifies which platform it came from. You can filter, group, or sort results by service on your end. This is useful for showing users which of their connected services has a given track.
The search endpoint requires a user context because it searches across that user's connected services. For general catalog browsing without user auth, check the public endpoints in the API documentation. Public endpoints use simpler token-based authorization and cover catalog data across supported services.
Ready to skip months of OAuth and SDK work? Start your free MusicAPI trial and connect 10+ streaming services with one unified API.