GuidesMUSIC APIS

Free lyrics APIs compared

LRCLIB is the free lyrics API to call from browser JavaScript: no key, Access-Control-Allow-Origin: *, plain and time-synced (LRC) lyrics, and about 45 requests a minute per IP for unidentified clients. lyrics.ovh also sends CORS headers and returns plain lyrics with no key. The Genius API returns song metadata but not the full lyrics, its public web search sends no CORS header, so a browser needs a proxy such as the free proxy.cors.dev (no signup, GET and HEAD, about 600 requests a minute per IP), and Musixmatch lists no free plan: Basic costs $49 a month for 500 lyrics calls a day.

Lyrics APIs checked for browser CORS

We sent browser-style requests with an Origin header to 5 lyrics endpoints on 28 October 2026. Only LRCLIB and lyrics.ovh return lyrics text without a key and send Access-Control-Allow-Origin: *, so call both directly. Genius returns metadata and annotated fragments only, and Musixmatch has no free plan.

Lyrics APIs and browser CORS, checked October 2026
APIKeyLyrics textBrowser CORSThrough cors.dev
LRCLIBnoneplain and time-syncedyes429 on the shared IP so call directly
lyrics.ovhnoneplainyesworks but not needed
Genius APIaccess tokenmetadata and annotated fragmentsGET yes, preflight 403not needed with access_token in the URL
Genius web searchnonesearch results onlyno header429 from Genius
Musixmatchsecret key, paid plans onlystatic on Basic, synced from Growno headerworks, but the key is visible

For other keyless APIs and their CORS status, see free public APIs.

LRCLIB: free synced lyrics with no key

LRCLIB (lrclib.net) is a free lyrics database with user-contributed lyrics and no API key. It sends Access-Control-Allow-Origin: *, so fetch() works straight from the browser, and the LRCLIB API documentation lists three read endpoints:

  • /api/get?artist_name=<artist>&track_name=<title> returns one record, or 404 when nothing matches. Optional album_name and duration (seconds, matched within 2 seconds either way) tighten the match.
  • /api/get/<id> returns one record by ID.
  • /api/search?q=<text> returns matching records: daft punk one more time gave 20 records in 303,707 bytes.

Each record has plainLyrics, syncedLyrics (LRC, one line per timestamp, like [00:35.66] Look at the stars), instrumental, duration and albumName. Coldplay's "Yellow" with duration=269 returned a 267-second record with 994 characters of plain lyrics and 49 synced lines, and Daft Punk's "One More Time" returned 15,381 bytes.

Limits depend on how your client identifies itself. Browsers don't let JavaScript set User-Agent, so LRCLIB also reads X-User-Agent or Lrclib-Client, and its preflight allows both plus lrclib-app-id and lrclib-api-key. A generic client (no User-Agent, a browser, curl or a common HTTP library) gets about 45 requests a minute per IP, an identified client about 90, and a registered client-side app sending Lrclib-App-Id about 180 per IP plus an app-wide allowance of about 6,000 a minute.

Over the limit, LRCLIB answers 429 with a Retry-After header and a JSON body naming the ip or application scope; in our burst test the 429 carried Access-Control-Allow-Origin: * and exposed Retry-After to JavaScript. Its docs require waiting at least that long plus up to 30% random jitter, and warn that ignoring Retry-After may get you temporarily banned. Through https://proxy.cors.dev/ our test got a 238-byte 429 RateLimitExceeded, because the proxy's shared IP was already over the per-IP limit, so call LRCLIB directly and each visitor uses their own allowance.

Show lyrics in time with playback

The helper calls LRCLIB directly with an Lrclib-Client header, parses the LRC timestamps into {seconds, text} lines next to the plain text, and reports Retry-After on a 429.

syncedLyrics(): LRCLIB plain and time-synced lyrics
async function syncedLyrics(artist, track, durationSeconds) {
  const params = new URLSearchParams({ artist_name: artist, track_name: track });
  if (durationSeconds) params.set('duration', String(Math.round(durationSeconds)));

  // LRCLIB sends CORS headers and reads Lrclib-Client because browsers cannot set User-Agent
  const res = await fetch(`https://lrclib.net/api/get?${params}`, {
    headers: { 'Lrclib-Client': 'my-lyrics-app/1.0 (https://example.com)' },
  });
  if (res.status === 404) return null;
  if (res.status === 429) throw new Error(`LRCLIB rate limit, retry in ${res.headers.get('Retry-After')} s`);
  if (!res.ok) throw new Error(`LRCLIB answered ${res.status}`);

  const record = await res.json();
  const lines = (record.syncedLyrics ?? '').split('\n').flatMap((row) => {
    const match = row.match(/^\[(\d+):(\d+(?:\.\d+)?)\]\s?(.*)$/);
    return match ? [{ seconds: Number(match[1]) * 60 + Number(match[2]), text: match[3] }] : [];
  });
  return { plain: record.plainLyrics ?? '', lines, instrumental: record.instrumental };
}

console.log(await syncedLyrics('Coldplay', 'Yellow', 269));

To sync the parsed lines with an <audio> element in your page:

  1. Fetch the track

    Call syncedLyrics(artist, track, duration) when a song loads and keep the returned array of {seconds, text} lines.

  2. Listen for playback updates

    Attach a listener to the <audio> element's timeupdate event.

  3. Highlight the current line

    On each timeupdate event, compare audio.currentTime with each line's seconds value and highlight the last line whose time has passed.

lyrics.ovh, Genius and Musixmatch

For static text only, https://api.lyrics.ovh/v1/<artist>/<title> returns JSON with a lyrics field and sends Access-Control-Allow-Origin: *, so no key or proxy is needed: Coldplay/Yellow returned 1,153 bytes. Its /suggest/<query> endpoint returned 30,566 bytes of song matches in Deezer's search format; the Deezer API guide covers that API.

The official Genius API (api.genius.com) returns song metadata, annotations and the URL of the song's page, where the lyrics are shown; it does not return the full lyrics, only the lyric fragments that annotations are attached to. It needs an access token from a registered API client and answered 401 (113 bytes) with Access-Control-Allow-Origin: * without one. A browser can't send that token in an Authorization header, because the OPTIONS preflight answers 403; the access_token query parameter skips the preflight but shows the token to anyone using the page.

Genius's undocumented web search at https://genius.com/api/search/multi?q=<text> can change without notice and returns search results, not lyrics; genius.com/api/songs/<id> through the proxy returned 322,486 bytes with a lyrics_state field and no lyrics text. The search answered a 403 Cloudflare challenge to a datacenter IP and 200 JSON without Access-Control-Allow-Origin to residential IPs, so a browser can't read it either way. Prefixed with https://proxy.cors.dev/ it answered 429 from Genius in 4 of 4 re-tests; the 25,780 bytes of JSON for yellow coldplay came from a residential IP. The free tier needs no signup or key and allows about 600 requests a minute per IP, answering 429 with Retry-After past that.

Musixmatch (api.musixmatch.com/ws/1.1/) sends no Access-Control-Allow-Origin header. Without a valid apikey it answers HTTP 200 with content type text/plain and a JSON body whose message.header.status_code is 401, so check that field rather than the HTTP status. Its pricing page lists no free plan:

Musixmatch API plans, checked October 2026
PlanPrice a monthCalls a dayTime-synced lyricsLyrics caching
Basic$495,000 (500 lyrics)nonot included
Grow$19920,000 (2,000 lyrics)yesnot included
Scale$499100,000 (10,000 lyrics)yesnot included
Enterprisefrom $2,000customyesincluded

Through https://proxy.cors.dev/ Musixmatch returned 200 with the caller's origin allowed, but the apikey sits in the URL where any visitor can read it, and Musixmatch says the key must be kept secret, so call it from your own server.

Lyrics licensing

Lyrics are copyrighted by their songwriters and publishers, and an API that answers does not grant display rights. LRCLIB's lyrics are contributed by its users, and lyrics.ovh is an unofficial service.

Genius's API docs say commercial use is not allowed without a license, and Musixmatch sells licensed lyrics, with lyrics caching only on its Enterprise plan. Check the rights you need before showing lyrics in a public or paid product.

Good questions.

Does the Genius API return song lyrics?

No. The official API returns song metadata, annotations and the URL of the song's page on Genius, where the lyrics are shown. Genius's undocumented genius.com/api/songs/<id> returned 322,486 bytes for a song on 28 October 2026, with a lyrics_state field but no lyrics text.

Is there a free plan for the Musixmatch API?

No. As of October 2026 Musixmatch's pricing page lists no free plan; the cheapest is Basic at $49 a month for 5,000 calls and 500 lyrics calls a day, with static lyrics only and no caching. Time-synced lyrics start on Grow at $199 a month.

Why did LRCLIB return 429 when called through cors.dev?

LRCLIB limits requests per IP address, starting at about 45 requests a minute for generic unidentified clients. When you route requests through https://proxy.cors.dev/, you share an outbound IP with other proxy users, which pushes that IP over LRCLIB's limit. Because LRCLIB sends Access-Control-Allow-Origin: *, you should call it directly from the browser so each user gets their own per-IP allowance.

How can I double my LRCLIB rate limit from browser JavaScript?

Browsers block JavaScript from changing the User-Agent header, so browser fetch() calls fall into LRCLIB's generic bucket of about 45 requests a minute per IP. Send an Lrclib-Client or X-User-Agent header with your request, both of which LRCLIB allows in its CORS preflight. Identifying your client raises the limit to about 90 requests a minute per IP, or about 180 per IP if you register a client-side app and send Lrclib-App-Id.

Can I use cors.dev to hide my Musixmatch or Genius API key?

No. The free tier refuses Authorization and other credential headers with 400 header_not_allowed, and query parameters like apikey or access_token are forwarded as-is, so they stay visible in your frontend code and the network tab. cors.dev Pro ($5 a month for 500,000 requests) forwards Authorization, but a key sent from the browser is still readable by visitors, so keep secret keys on your own server.