> ## Documentation Index
> Fetch the complete documentation index at: https://monitoryt.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors and rate limits

> Handle API failures, retries, and request budgets safely.

API errors use a consistent JSON shape:

```json theme={null}
{
  "error": "bad_request",
  "message": "Invalid request body",
  "details": {
    "videoIds": ["videoIds must not contain duplicates"]
  }
}
```

`details` is optional and is most commonly present for request validation errors.

## Status codes

| Status | Error code                      | Meaning                                                         |
| ------ | ------------------------------- | --------------------------------------------------------------- |
| `400`  | `bad_request`                   | The JSON body is missing, malformed, or fails field validation. |
| `401`  | `unauthorized`                  | The API key is missing, malformed, revoked, or unknown.         |
| `403`  | `upgrade_required`              | The key is valid, but the account is not on Pro.                |
| `404`  | `not_found`                     | The requested API path does not exist.                          |
| `405`  | `method_not_allowed`            | The route only accepts POST.                                    |
| `429`  | `rate_limited`                  | A request budget was exhausted.                                 |
| `500`  | `route_error` or `server_error` | An unexpected server failure occurred.                          |

## Request limits

Authenticated public requests are limited to 20 requests/minute per API key. The
public endpoints also currently share a stricter budget of 5 requests/minute per
client IP. A client operating from one IP should therefore budget for no more than
5 public requests/minute.

Cloudflare rate-limit counters can be eventually consistent. Do not depend on an
exact remaining-request count at the edge.

## Retry behavior

A `429` response includes a `Retry-After` header, currently expressed in seconds.
Wait for that duration before trying again.

For `500` responses, retry only idempotent read requests and use exponential
backoff with jitter. For example: 1 second, 2 seconds, 4 seconds, then stop and
surface the failure. Do not retry `400`, `401`, or `403` without first correcting
the request or account state.

```javascript theme={null}
if (response.status === 429) {
  const retryAfter = Number(response.headers.get("Retry-After") ?? "60");
  await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
}
```

## Health check

`POST /api/public/health` requires no authentication but does require an empty JSON
object body:

```bash theme={null}
curl -H "Content-Type: application/json" \
  -d '{}' \
  "https://monitoryt.com/api/public/health"
```

Successful response:

```json theme={null}
{ "status": "ok" }
```
