# Caching

The API caches heavily, which keeps upstream requests down and responses fast. Knowing how the cache works helps you tune your integration and read the response headers.

## Cache types

### Positive cache

Lyrics that are found stay cached until someone clears them manually. Positive cache entries include the TTML data, track duration, and match score.

### Negative cache

When lyrics are _not_ found, that result is cached too, so the API does not repeat the lookup. Negative cache entries expire after 7 days by default.

If you receive a `NEGATIVE_HIT` response and believe lyrics should exist, use the `/revalidate` endpoint (requires API key) to force a fresh lookup.

## Cache key format

`/getLyrics` normalizes cache keys so equivalent queries share an entry:

```bash
ttml_lyrics:{song} {artist} {album} {duration}s
```

-   All text is converted to lowercase
-   Leading/trailing whitespace is trimmed
-   Album and duration are part of the key, so always send them. A request without them builds a shorter key that often misses the cached entry and, without an API key, returns `401`
-   `/getLyrics` also accepts a cached entry whose duration differs by a small amount. The provider-specific endpoints need an exact match

For example, searching for "E85" by "Don Toliver" on "OCTANE" and "e85" by "don toliver" on "octane", both with duration 154, hit the same cache entry: `ttml_lyrics:e85 don toliver octane 154s`.

## Cache status header

Responses from the lyrics endpoints include an `X-Cache-Status` header:

| Value | Meaning |
| --- | --- |
| `HIT` | Lyrics served from cache |
| `MISS` | Not in the cache. The lyrics were fetched fresh and cached, or the request failed |
| `NEGATIVE_HIT` | Previously searched, lyrics not found (cached negative result) |
| `STALE` | Backend failed, serving older cached version as fallback. Only on `/getLyrics` |

## Why duration matters

The `duration` parameter improves both cache hit rates and match accuracy:

-   Helps distinguish between different versions of the same song (radio edit vs album version)
-   Improves matching when song/artist names have variations
-   Makes cache keys more specific, so there are fewer false positives

Always send the duration, together with the album, in every request.

## Cache fallback behavior

When the upstream fetch for `/getLyrics` fails, the API tries to serve stale cached data:

1.  Primary cache key lookup fails (fresh fetch attempted)
2.  Upstream provider returns error
3.  If an album was sent, the API checks the cache for the same query without the album
4.  If found, serves stale cache with `X-Cache-Status: STALE`
5.  If not found, returns the original error

## Revalidation

To force a cache refresh, use the [`/revalidate`](https://docs.betterlyrics.org/reference/revalidate) endpoint (requires API key). It refreshes only entries that already exist, and only the default TTML entry that `/getLyrics` serves:

   

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

```javascript
const res = await fetch("https://api.betterlyrics.org/revalidate?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/revalidate?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/revalidate?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))
}
```

Use it when:

-   You know lyrics have been corrected at the source
-   A negative cache entry should be re-checked
-   You want to verify the cache is up-to-date

## Client-side caching

Cache on the client too, to cut down on API calls:

-   Cache successful responses locally for the session or longer
-   Use the song, artist, album and duration as your local cache key
-   Respect `NEGATIVE_HIT`: don't retry songs without lyrics right away

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