# Account quota

Read how much of your plan quota and coupon budget is left, in whole Rupiah, so an agent or script can check before it starts a long job. The endpoint is specific to kenari and has no OpenAI or Anthropic equivalent.

`GET /v1/account/quota`

## Request

The endpoint takes no query parameters and no body. It needs an API key in the `Authorization` header.

| Header | Required | Description |
| --- | --- | --- |
| `Authorization` | yes | `Bearer kn-...`. A shared API key and a restricted key are refused. See [Authentication and API keys](/en/docs/authentication). |

## Response

The response has three fields, `plan`, `coupon` and `alert_pct`. `plan` and `coupon` are each an object, or `null` when they do not apply.

| Field | Type | Description |
| --- | --- | --- |
| `plan` | object or null | Your active plan. `null` when you have never subscribed, the plan has ended, or the plan is no longer offered. |
| `plan.name` | string | The plan name. |
| `plan.windows` | object | The quota windows that have a ceiling. Possible keys are `five_hour`, `week` and `month`. A window with no ceiling is left out, so the object can be empty. |
| `plan.windows.<key>.budget_rp` | integer | The ceiling of this window in whole Rupiah, rounded down. |
| `plan.windows.<key>.used_rp` | integer | Rupiah used in this window, rounded up. |
| `plan.windows.<key>.remaining_rp` | integer | Rupiah left in this window, rounded down and never below 0. |
| `plan.windows.<key>.used_frac` | number | The fraction of the ceiling that is used, from 0 to 1 with 4 decimals. |
| `plan.windows.<key>.resets_at` | string | When this rolling window resets, in RFC 3339 UTC. |
| `coupon` | object or null | The active coupon. `null` when you have none or it has expired. It is reported separately from the plan, so you can have a coupon without a plan. |
| `coupon.name` | string | The coupon name. |
| `coupon.used_rp` | integer | Rupiah used from the coupon, rounded up. |
| `coupon.remaining_rp` | integer or null | Rupiah left on the coupon: the budget rounded down to whole Rupiah, minus `used_rp`, and never below 0. `null` when the coupon has no spending limit. |
| `coupon.expires_at` | string | When the coupon expires, in RFC 3339 UTC. |
| `coupon.scope_models` | array of strings | The model ids the coupon covers. |
| `alert_pct` | integer | Your quota alert threshold: 50, 80 or 90. Compare it with `used_frac * 100` to see when the quota alert fires. You set it under **Settings**, **Notifications**. See [Notifications](/en/docs/notifications#thresholds). |

```json
{
  "plan": {
    "name": "Indie",
    "windows": {
      "week": {
        "budget_rp": 75000,
        "used_rp": 12000,
        "remaining_rp": 63000,
        "used_frac": 0.16,
        "resets_at": "2026-10-13T07:12:00Z"
      },
      "month": {
        "budget_rp": 300000,
        "used_rp": 12000,
        "remaining_rp": 288000,
        "used_frac": 0.04,
        "resets_at": "2026-10-30T07:12:00Z"
      }
    }
  },
  "coupon": null,
  "alert_pct": 80
}
```

For how windows, coupons and covered models work, see [Subscriptions](/en/docs/subscriptions).

## Examples

### curl

```bash
curl https://kenari.id/v1/account/quota \
  -H "Authorization: Bearer $KENARI_API_KEY"
```

### Python

```python
import os
import requests

response = requests.get(
    "https://kenari.id/v1/account/quota",
    headers={"Authorization": f"Bearer {os.environ['KENARI_API_KEY']}"},
)
response.raise_for_status()
quota = response.json()

for name, window in (quota["plan"] or {}).get("windows", {}).items():
    alert = window["used_frac"] * 100 >= quota["alert_pct"]
    print(name, window["remaining_rp"], window["used_frac"], window["resets_at"], "ALERT" if alert else "")
```

### JavaScript

```javascript
const response = await fetch("https://kenari.id/v1/account/quota", {
  headers: { Authorization: `Bearer ${process.env.KENARI_API_KEY}` },
});
if (!response.ok) throw new Error(await response.text());
const quota = await response.json();

for (const [name, window] of Object.entries(quota.plan?.windows ?? {})) {
  const alert = window.used_frac * 100 >= quota.alert_pct;
  console.log(name, window.remaining_rp, window.used_frac, window.resets_at, alert ? "ALERT" : "");
}
```

## MCP

The MCP tool `kenari_quota` returns the same plan and coupon data as plain text. See [MCP server](/en/docs/mcp).

## Billing

Reading your quota is free. See [How billing works](/en/docs/billing).

## Headers on chat replies

A successful chat reply can also carry the remaining fraction of each window in the `x-kenari-plan-remaining` header, so an agent does not need a separate call. See [Notifications](/en/docs/notifications#response-headers).

## Errors

| Status | Code | When |
| --- | --- | --- |
| 401 | None | The key is missing, invalid or expired. The body is a short plain-text message, not JSON. |
| 403 | `shared_key_not_allowed` | The key is a shared API key. A shared key cannot read the owner's quota. |
| 403 | `restricted_key_not_allowed` | The key has a model restriction, a spend cap or a token cap. Use an unrestricted key from the account itself. The key's own usage is still readable from [`GET /v1/usage`](/en/docs/usage). |
| 500 | `internal_error` | Reading the quota failed in kenari. Retry once. |

See [Errors](/en/docs/errors) for the error format and the remaining codes.

## See also

- [Account overview](/en/docs/account)
- [Account balance](/en/docs/account-balance)
- [Subscriptions](/en/docs/subscriptions)
- [Notifications](/en/docs/notifications)
- [MCP server](/en/docs/mcp)
- [Usage](/en/docs/usage)
- [Authentication and API keys](/en/docs/authentication)
