Rate limiting

The API uses two-tier rate limiting. When you go over the normal limit, cached responses still come through, up to a second, higher limit.

How it works

Tier 1: Normal

Standard rate limit for all requests. Can fetch fresh lyrics or serve from cache.

When exceeded
Tier 2: Cached

Higher rate limit, but only serves cached responses. Fresh fetches return 429.

When exceeded
Rate limited

All requests return 429 Too Many Requests.

Limits are per IP address. The limits can change, so do not hardcode them. Read X-RateLimit-Limit and X-RateLimit-Remaining on each response instead.

Response headers

The API reports rate limit, cache and authentication state in these headers:

Header Values When it is sent
X-RateLimit-Limit Burst size of the current tier. On exceeded it is the burst size of the cached tier. Every request without a valid API key
X-RateLimit-Remaining Requests left in the current tier, 0 when both tiers are exceeded Every request without a valid API key
X-RateLimit-Type normal, cached, exceeded, or bypass Every request without a valid API key. With a valid API key, bypass is sent on JSON responses from the lyrics endpoints, /revalidate and the /cache/* endpoints
X-RateLimit-Bypass true Requests with a valid X-API-Key header
Retry-After 1 when both tiers are exceeded, 60 when the cached tier has no cache entry for the query Only on 429 responses
X-Cache-Status HIT, MISS, NEGATIVE_HIT, or STALE (STALE only on /getLyrics) Lyrics endpoints: /getLyrics and the provider-specific endpoints. See Caching
X-Provider ttml, kugou, or qq Provider-specific endpoints only, for example /kugou/getLyrics
X-Auth-Mode authenticated, invalid, or cache /getLyrics, /ttml/getLyrics and /revalidate, when the API requires a key for uncached queries. See Authentication

Browsers cannot read these headers from cross-origin JavaScript. The API does not expose them through CORS on purpose, so they are meant for server-side callers and tools like curl.

Rate limit types

normal

Request processed normally. Can fetch fresh lyrics or serve from cache.

cached

Normal tier exceeded. The request is served only if the lyrics are cached. Otherwise it returns 429.

exceeded

Both tiers exceeded. All requests return 429 Too Many Requests.

bypass

The request has a valid API key, so rate limits do not apply.

API key bypass

Requests with a valid X-API-Key header bypass all rate limits. This is useful for production applications that need guaranteed access.

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

When bypass is active, every response includes X-RateLimit-Bypass: true, and has no X-RateLimit-Limit or X-RateLimit-Remaining. The lyrics endpoints, /revalidate and the /cache/* JSON responses also include X-RateLimit-Type: bypass:

X-RateLimit-Bypass: true
X-RateLimit-Type: bypass

Handling rate limits

When you receive a 429 response:

  1. Check the Retry-After header for when to retry
  2. Check X-RateLimit-Type to see which tier was exceeded
  3. If cached, the lyrics are not in the cache yet, so wait before you retry
  4. Use exponential backoff for retries

Rate limit responses

When both tiers are exhausted, any endpoint returns 429 Too Many Requests with Content-Type: text/plain, Retry-After: 1 and X-RateLimit-Type: exceeded:

Too Many Requests

When the cached tier serves a lyrics request that is not cached, the response is 429 with a JSON body, Retry-After: 60 and X-RateLimit-Type: cached:

{
  "error": "Rate limit exceeded. This request requires cached data, but no cache is available for this query.",
  "message": "Please try again later or reduce your request rate."
}

The provider-specific endpoints return {"error": "Rate limit exceeded. No cached data available.", "provider": "..."} in this case.