# Rate Limits

300 requests per minute, per API key.

Source: https://youtubetranscriptapi.com/docs/rate-limits

## The basics

- Default rate limit: **300 requests per minute** per API key.
- The window is rolling — counts decay continuously, not at fixed-second boundaries.
- Each key has an independent budget. Adding more keys does not raise your account-level limit.

## Headers

Once the rate limit has been checked, every response carries its current state, errors included (`402`, `404`, `408`, `422`, `429`, `503`). Requests turned away before that check don't have these headers: `401`, `403`, `405`, a `404` for an unknown path, and a `503` when the account check itself fails.

```http
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1735689600
Retry-After: 7   # on 429; also on 408 and 503, where it is 3
```

## Credit headers

Every successful (`200`) response also says what the call cost and what is left, so you can watch your balance without an extra request:

```http
X-Credits-Charged: 1
X-Credits-Remaining: 99
```

- `X-Credits-Remaining` is the balance you can spend right now: free and top-up credits, plus plan credits while your plan is active. If it can't be read reliably the header is left out rather than estimated.
- Free endpoints send `X-Credits-Charged: 0`. Errors are never charged and carry neither header.

## Alert before you run out

Read `X-Credits-Remaining` after each successful call and warn when it drops below a threshold you pick, so a batch job doesn't stop on a `402` halfway through. Swap the warning for Slack, email or whatever your team watches.

```python
import logging
import requests

LOW_BALANCE = 100  # warn below this many credits

def get_transcript(video_url: str) -> dict:
    r = requests.get(
        "https://youtubetranscriptapi.com/api/v2/youtube/transcript",
        params={"video_url": video_url},
        headers={"Authorization": f"Bearer {API_KEY}"},
        timeout=60,
    )
    r.raise_for_status()
    remaining = r.headers.get("X-Credits-Remaining")  # absent on errors
    if remaining is not None and int(remaining) < LOW_BALANCE:
        logging.warning("YouTubeTranscriptAPI: %s credits left", remaining)
    return r.json()
```

```javascript
const LOW_BALANCE = 100; // warn below this many credits

async function getTranscript(videoUrl) {
  const url = new URL("https://youtubetranscriptapi.com/api/v2/youtube/transcript");
  url.searchParams.set("video_url", videoUrl);
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.API_KEY}` },
    signal: AbortSignal.timeout(60_000),
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const remaining = res.headers.get("X-Credits-Remaining"); // null on errors
  if (remaining !== null && Number(remaining) < LOW_BALANCE) {
    console.warn(`YouTubeTranscriptAPI: ${remaining} credits left`);
  }
  return res.json();
}
```

## Checking your balance

`GET /account/whoami` returns the plan and balance of the account that owns the key. It is free and answers at a zero balance too, so it's safe to call before a batch job. See the [API reference](https://youtubetranscriptapi.com/docs/api#account).

```bash
curl "https://youtubetranscriptapi.com/api/v2/account/whoami" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```json
{
  "user_id": "…",
  "plan": "monthly",
  "plan_status": "active",
  "credits": { "spendable": 1240, "plan": 740, "topup": 500 }
}
```

## Recommended client behavior

1. On `200`: read `X-RateLimit-Remaining`; throttle if it dips below 10% of your limit.
2. On `429`: sleep for `Retry-After` seconds and retry once.
3. On `503` / `408`: wait `Retry-After` seconds (3), then back off exponentially and stop after 3 attempts.

## Need more headroom?

Enterprise plans get dedicated rate limits — get in touch via the contact form.
