GuidesMUSIC APIS
Free music APIs compared
Five of the 8 music APIs we tested on 28 October 2026 send Access-Control-Allow-Origin: * and work straight from browser JavaScript: iTunes Search, MusicBrainz, Last.fm, Audius and Discogs. Deezer's API is keyless but sends no CORS header, so route it through the free proxy.cors.dev (no signup, GET and HEAD, about 600 requests a minute per IP); one search came back as 52,846 bytes. Spotify allows CORS but needs OAuth, and its Development Mode now requires a Premium account and caps each app at 5 users.
8 music APIs checked for browser CORS
We sent browser-style requests with an Origin header to 8 music APIs on 28 October 2026. Five sent Access-Control-Allow-Origin: * and work from browser JavaScript without a proxy: iTunes Search, MusicBrainz, Last.fm, Audius and Discogs.
Deezer sent no CORS header, and Jamendo sent it on some responses but not others. Spotify allows CORS but needs an OAuth token before it returns anything.
| API | Key | Free limit | Browser CORS | Through cors.dev |
|---|---|---|---|---|
| iTunes Search | none | about 20 calls a minute | yes | 429 so call directly |
| Deezer | none | 50 requests per 5 seconds | no | 200 so use the proxy |
| MusicBrainz | none | 1 request a second | yes | 503 so call directly |
| Audius | none | unconfirmed | yes | works but not needed |
| Last.fm | free key in the URL | non-commercial use | yes | not needed |
| Discogs | none for search | 25 requests a minute | yes | 429 so call directly |
| Jamendo | free client_id | set by your plan after signup | inconsistent | 200 so use the proxy |
| Spotify | OAuth token | 5 users per Development Mode app | yes | not needed |
Keyless catalogs: iTunes, Deezer, MusicBrainz and Audius
iTunes Search needs no API key and works straight from fetch(). Calling https://itunes.apple.com/search?term=<text>&entity=song&limit=5 returned 7,657 bytes of JSON with trackName, artistName, previewUrl (an audio preview) and artworkUrl100. Apple's Search API documentation puts the limit at about 20 calls per minute; through cors.dev it answered 429 with a Retry-After of 21 to 38 seconds, because Apple throttles the proxy's shared IPs.
Deezer's search at https://api.deezer.com/search?q=daft%20punk also needs no key and returned 52,846 bytes of JSON, but without an Access-Control-Allow-Origin header, so the browser blocks it; through https://proxy.cors.dev/ it returned 200. Deezer's developer support documents 50 requests per 5 seconds per IP, and an exceeded quota comes back as HTTP 200 with error code 4 in the body; through the proxy that allowance is shared with other users, so keep volume low. Preview MP3s on cdnt-preview.dzcdn.net send Access-Control-Allow-Origin: * and expire after 900 seconds; the Deezer API guide covers both in detail.
MusicBrainz (https://musicbrainz.org/ws/2/artist/?query=<text>&fmt=json) returned 5,757 bytes with no key. Its rate limiting documentation allows 1 request per second per IP, answers 503 above that, and asks for a descriptive User-Agent such as MyApp/1.0 ( contact@example.com ). Chrome doesn't let JavaScript change User-Agent, and a request with Chrome's own returned 200 in our test. Through cors.dev it answered 503 in one test because the shared IP was over the rate, so call it directly; album art for releases comes from the Cover Art Archive (coverartarchive.org), which also sends *.
Audius needs no signup: https://api.audius.co/v1/tracks/search?query=lofi returned 61,778 bytes, and /v1/tracks/trending worked without a key or app name. We could not confirm a published rate limit.
Search two catalogs in one call
For tracks with cover art and audio previews and no keys to manage, query iTunes directly and Deezer through proxy.cors.dev at the same time. searchTracks() runs both with Promise.allSettled, so one failing catalog doesn't break the other, and returns source, title, artist, preview and art for each track.
const PROXY = 'https://proxy.cors.dev/';
async function searchTracks(query) {
const q = encodeURIComponent(query);
// iTunes sends CORS headers, Deezer does not, so only Deezer goes through the proxy
const [itunes, deezer] = await Promise.allSettled([
fetch(`https://itunes.apple.com/search?term=${q}&entity=song&limit=5`).then((r) => r.json()),
fetch(`${PROXY}https://api.deezer.com/search?q=${q}&limit=5`).then((r) => r.json()),
]);
const tracks = [];
for (const t of itunes.value?.results ?? []) {
tracks.push({ source: 'iTunes', title: t.trackName, artist: t.artistName, preview: t.previewUrl, art: t.artworkUrl100 });
}
for (const t of deezer.value?.data ?? []) {
tracks.push({ source: 'Deezer', title: t.title, artist: t.artist.name, preview: t.preview, art: t.album.cover_medium });
}
return tracks;
}
console.table(await searchTracks('daft punk'));On 28 October 2026 the query daft punk returned 5 tracks from each catalog, all with preview URLs.
Last.fm, Discogs and Jamendo: keys and limits
Last.fm (https://ws.audioscrobbler.com/2.0/?method=<method>&api_key=<key>&format=json) takes a free API key in the query string and sends Access-Control-Allow-Origin: * even on errors, returning 403 with error 10 for an invalid key and 400 with error 6 when the key is missing. Last.fm's API terms limit you to non-commercial use with attribution. Because the key goes in the URL, it is visible to every visitor who opens their browser devtools.
Discogs answers basic queries without a token: https://api.discogs.com/database/search?q=<text>&type=artist returned 200 (11,143 bytes) and https://api.discogs.com/artists/1289 returned 19,964 bytes, both with Access-Control-Allow-Origin: *. Responses carried X-Discogs-Ratelimit: 25, the per-minute allowance for unauthenticated requests. Discogs asks for a unique User-Agent, and Chrome's own worked in our test. Through cors.dev it answered 429 because the shared IP had used its allowance, so call Discogs directly.
Jamendo (https://api.jamendo.com/v3.0/tracks/?client_id=<id>&format=json) needs a free client_id from its developer portal, where you pick a usage plan at signup; we could not confirm the plan limits on a public page. Jamendo answers HTTP 200 even when a call fails, so check headers.status in the JSON ("failed" with code 5 means an invalid client_id). Its CORS header was inconsistent in our tests, so route it through proxy.cors.dev, which returned 200 with the caller's origin allowed.
Spotify after the 2026 Development Mode changes
The Spotify Web API (https://api.spotify.com/v1/search) sends CORS headers (it echoes your origin and allows Authorization in its preflight) and answers 401 without an OAuth access token. A browser app gets a token with Spotify's Authorization Code with PKCE flow and calls the API directly, with no proxy involved.
Access for new apps has shrunk since 2024. In November 2024 Spotify removed Related Artists, Recommendations, Audio Features, Audio Analysis, Featured Playlists, editorial playlists and 30-second preview URLs in multi-get responses for new apps and apps still in development mode, and since May 2025 extended quota (anything beyond Development Mode) is limited to established use cases. Development Mode is now meant for non-commercial personal projects, under these 2026 rules:
- Premium and 5 users: since 11 February 2026 for new apps and 9 March 2026 for existing ones, the app owner needs Spotify Premium, and at most 5 authorized users can use each Client ID.
- Fewer endpoints: new Development Mode apps lost endpoints including Get Several Tracks, Get Artist's Top Tracks and Get New Releases, and the search
limitmaximum dropped from 50 to 10. On 9 March Spotify postponed the endpoint changes for existing apps. - Shared quota: since 23 July 2026 a developer account can create up to 25 Client IDs, which share one quota; going over returns
429with reasonQUOTA_EXCEEDED.
Good questions.
Which free music API is easiest for getting song previews and album art in JavaScript?
iTunes Search and Deezer are the fastest way to get both. iTunes works directly from the browser with no key and gives you previewUrl and artworkUrl100 in the response. Deezer also requires no key and includes preview MP3s and cover images, though you have to prefix the search URL with https://proxy.cors.dev/ because Deezer does not send an Access-Control-Allow-Origin header.
Why did my request to iTunes or Discogs return 429 when I used the CORS proxy?
Both iTunes (about 20 calls per minute) and Discogs (X-Discogs-Ratelimit: 25 per minute unauthenticated) enforce their rate limits by IP address. When you route through proxy.cors.dev, you share the proxy's outbound IPs with every other developer using the free service. Because both APIs already send Access-Control-Allow-Origin: *, remove the proxy URL and call them directly so each visitor gets their own IP allowance.
Can I hide my Last.fm or Jamendo API key by calling through cors.dev?
No, cors.dev does not hide or protect keys in your browser code. Query-string parameters like Last.fm's api_key and Jamendo's client_id are sent straight from the visitor's browser in the request URL and forwarded as-is. Anyone who opens the browser network tab on your page can read the full URL and copy your key.
Can I pass a Spotify OAuth token through the free cors.dev proxy?
No, and you don't need to. The free tier refuses Authorization headers with 400 header_not_allowed, while Spotify's Web API already echoes your origin and allows Authorization in its CORS preflight. Call https://api.spotify.com/v1/search directly with a token from the Authorization Code with PKCE flow.
Why do Deezer and Jamendo return HTTP 200 when a request fails?
Both report application errors inside the JSON body. Over Deezer's 50 requests per 5 seconds per IP, you get HTTP 200 with error code 4 (Quota limit exceeded). Jamendo answers 200 too, so check headers.status in the JSON, where "failed" with code 5 means an invalid client_id.