# Authentication

**API keys are not being issued right now.** Each cache miss hits a paid upstream provider, so issuing keys broadly isn't sustainable at the moment. Cached responses cover the vast majority of songs and need no key. If you hit a song that isn't cached, see [Loading uncached songs](https://docs.betterlyrics.org/docs/authentication#loading-uncached-songs) below for a workaround.

The Better Lyrics API uses a **cache-first authentication model**. Anyone can read a cached response. A fresh fetch may need an API key.

## When you need an API key

| Scenario | API key required? |
| --- | --- |
| Cache hit (lyrics already cached) | No |
| Cache miss (fresh fetch needed) | Yes* |
| Revalidation (`/revalidate` endpoint) | Yes |
| Rate limit bypass | Yes |

\*Only on `/getLyrics` and `/ttml/getLyrics`, and only when `API_KEY_REQUIRED` is enabled on the server. `/kugou/getLyrics` and `/qq/getLyrics` never need a key. Keys are currently not being issued to new users.

## Using your API key

Pass your API key in the `X-API-Key` header:

   

```bash
curl -H "X-API-Key: your-api-key" \
  "https://api.betterlyrics.org/getLyrics?s=E85&a=Don%20Toliver&al=OCTANE&d=154"
```

```javascript
const res = await fetch("https://api.betterlyrics.org/getLyrics?s=E85&a=Don%20Toliver&al=OCTANE&d=154", {
  headers: { "X-API-Key": "your-api-key" },
});
const data = await res.json();
```

```python
import requests

res = requests.get(
    "https://api.betterlyrics.org/getLyrics?s=E85&a=Don%20Toliver&al=OCTANE&d=154",
    headers={"X-API-Key": "your-api-key"},
)
data = res.json()
```

```go
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	req, err := http.NewRequest(http.MethodGet, "https://api.betterlyrics.org/getLyrics?s=E85&a=Don%20Toliver&al=OCTANE&d=154", nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("X-API-Key", "your-api-key")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	body, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(body))
}
```

## API key benefits

-   Fetch lyrics for songs that are not in the cache yet
-   Skip rate limiting entirely
-   Force a refresh of cached lyrics with the `/revalidate` endpoint

## Response headers

The API reports authentication state in these headers:

| Header | Description |
| --- | --- |
| `X-Auth-Mode` | Current auth mode: `authenticated` (valid API key), `invalid` (invalid API key provided), or `cache` (no API key, so only cached lyrics are served). Sent only on JSON responses from `/getLyrics`, `/ttml/getLyrics` and `/revalidate`, and only when the server enforces API keys. |
| `X-RateLimit-Bypass` | Set to `true` on every response when rate limits are bypassed via API key |

## Error responses

### Missing API key (cache miss)

```json
{
  "error": "API key required",
  "message": "Uncached queries require a valid API key via X-API-Key header"
}
```

Status: `401 Unauthorized`

### Invalid API key

```json
{
  "error": "Invalid API key",
  "message": "The provided API key is not valid"
}
```

Status: `401 Unauthorized`

## Loading uncached songs

First check that your request sends all four parameters: the cache key includes album and duration, so a request without them often misses an entry that is already cached. If a song you need still isn't cached, play it once with the [Better Lyrics](https://better-lyrics.boidu.dev/) extension installed. The extension fetches lyrics through the same upstream the API uses, so after that one playback the response is cached and any caller can get it without a key.

This only works if the song exists upstream. If the extension can't find it either, playing it again won't help.

If you have the lyrics yourself, you can upload them directly at [unison.boidu.dev](https://unison.boidu.dev/).

## Cache-first model explained

Each request goes through these steps:

1.  Request comes in for lyrics
2.  API checks if lyrics are already cached
3.  If cached: return immediately (no API key needed)
4.  If not cached: check for valid API key
5.  With valid API key: fetch from source, cache, and return
6.  Without API key: return 401 error

So anyone can read a song that is already cached. Only fresh fetches, which cost an upstream request, need a key.

Source: https://docs.betterlyrics.org/docs/authentication
