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

# Rate Limits & Quotas

> Throttling, usage quotas, response headers and how to handle 429 responses

Each Data API key can carry a **usage plan** set by your environment administrator, made up of:

* **Throttle** — a requests-per-second rate limit with a burst allowance (sliding window).
* **Quota** — a total request allowance per calendar period (`DAY`, `WEEK` or `MONTH`), resetting at the period boundary (UTC).

Keys without a plan are not throttled or quota-limited, but all usage is still metered and visible to your environment administrator.

## Response headers

Every successful (and throttled) response includes headers describing where you stand:

| Header                  | Present when         | Meaning                                      |
| ----------------------- | -------------------- | -------------------------------------------- |
| `X-RateLimit-Limit`     | key has a throttle   | Allowed requests per second                  |
| `X-RateLimit-Remaining` | key has a throttle   | Requests remaining in the current window     |
| `X-Quota-Limit`         | key has a quota      | Total requests allowed in the current period |
| `X-Quota-Remaining`     | key has a quota      | Requests remaining in the current period     |
| `X-Quota-Resets-At`     | key has a quota      | ISO 8601 timestamp when the period resets    |
| `Retry-After`           | throttled `429` only | Seconds to wait before retrying              |

Watch `X-Quota-Remaining` in your integration and slow down proactively as it approaches zero.

## 429 responses

There are two distinct `429 Too Many Requests` cases:

<AccordionGroup>
  <Accordion title="Rate limit exceeded (throttle)">
    You sent requests faster than the key's per-second allowance. The response
    includes a `Retry-After` header (in seconds) and a body with the precise
    wait in milliseconds:

    ```json theme={null}
    {
      "message": "Rate limit exceeded. Please slow down your requests.",
      "retryAfterMs": 740
    }
    ```

    **Recovery**: wait `Retry-After` seconds (or `retryAfterMs`) and retry.
    Throttled requests do **not** consume quota.
  </Accordion>

  <Accordion title="Quota exceeded">
    The key has used its full allowance for the current period. The body tells
    you when the period resets:

    ```json theme={null}
    {
      "message": "Quota of 10,000 requests exceeded. Resets at 2026-08-01T00:00:00.000Z.",
      "resetsAt": "2026-08-01T00:00:00.000Z"
    }
    ```

    **Recovery**: retrying sooner will not help — wait until `resetsAt`, or ask
    your environment administrator about the key's usage plan.
  </Accordion>
</AccordionGroup>

## Worked retry example

A resilient client retries throttled requests with the server-provided delay, and stops when the quota is exhausted:

```bash theme={null}
#!/usr/bin/env bash
# Fetch projects with automatic retry on throttling.
API_KEY="n3_data_xxxxxxxxxxxxxxxxxxxxxxxx"
URL="https://data.n-3.co.uk/data/projects?limit=200"

for attempt in 1 2 3 4 5; do
  response=$(curl -s -w '\n%{http_code}\n%{header_json}' \
    -H "x-api-key: ${API_KEY}" "${URL}")

  status=$(echo "${response}" | sed -n '2p')

  if [ "${status}" = "200" ]; then
    echo "${response}" | sed -n '1p'   # the JSON body
    exit 0
  fi

  if [ "${status}" = "429" ]; then
    retry_after=$(echo "${response}" | sed -n '3,$p' \
      | jq -r '."retry-after"[0] // empty')

    if [ -z "${retry_after}" ]; then
      # No Retry-After header => quota exhausted, not throttled. Do not retry.
      echo "Quota exhausted — wait for the period reset." >&2
      exit 1
    fi

    echo "Throttled — retrying in ${retry_after}s (attempt ${attempt})" >&2
    sleep "${retry_after}"
    continue
  fi

  echo "Request failed with status ${status}" >&2
  exit 1
done

echo "Giving up after 5 attempts" >&2
exit 1
```

The same pattern in any language: on `429`, retry after `Retry-After` seconds if the header is present; if it is absent, the quota is exhausted — back off until `resetsAt`.

## Checking your status — `GET /data/quota`

`GET /data/quota` returns your key's current quota and throttle status. It is the **free status check**: it never consumes quota, and it requires no scope — any valid Data API key can call it. Poll it as often as you like.

```bash theme={null}
curl "https://data.n-3.co.uk/data/quota" \
  -H "x-api-key: n3_data_xxxxxxxxxxxxxxxxxxxxxxxx"
```

**Response**:

```json theme={null}
{
  "api_key_id": "0b2f6c1e-9a44-4c1d-8f3a-2f9d1c5e7a10",
  "environment_id": "42",
  "scopes": ["data:projects", "data:benchmarks", "data:reports"],
  "quota": {
    "used": 1240,
    "limit": 10000,
    "remaining": 8760,
    "resets_at": "2026-08-01T00:00:00.000Z",
    "period": "MONTH"
  },
  "throttle": {
    "rate_limit_per_second": 10,
    "burst_limit": 20
  }
}
```

`quota` and `throttle` are `null` when the key has no quota or throttle plan respectively. The `scopes` array is also the easiest way to confirm exactly what your key can access.
