---
title: "Usage and limits"
method: GET
path: "/v1/usage"
tags: ["Account"]
---

# Usage and limits

`GET /v1/usage`

## Request

- Base URL: `https://api.kawaa.com`
- URL: `https://api.kawaa.com/v1/usage`
- Auth: one of:
  - API key in header `X-Api-Key`
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.kawaa.com/oauth/authorize`, token URL `https://api.kawaa.com/oauth/token`, scopes: `analytics:read`)

The older usage shape, kept for existing consumers. `GET /v1/usage/summary` is the one to build against.

Two figures here are deliberately not what they look like: `verifications.daily.used` is `null` when today's count could not be read (never 0, which would read as a measured idle day), and `verifications.daily.percent` is ALWAYS `null`: there is no enforced daily quota to be a percentage of, and `used` counts emails while `limit` counts credits, which are not the same unit.

## Response `200`

Usage this billing period.

- object
  - `success` unknown, required
  - `data` object, required
    - `plan` object, required: `{id: "sub_account", name: "Sub-account"}` for a white-label sub-account, which has no plan of its own.
      - `id` string
      - `name` string
    - `credits` object, required
      - `remaining` number
      - `used` number
      - `total` number
      - `allowance_basis` 'billing_period' | 'no_billing_period' | 'one_time_grant' | 'provider_allocation'
    - `verifications` object, required
      - `monthly` object
        - `used` number
        - `limit` number
        - `remaining` number: The spendable WALLET, not `limit - used`: the balance is the only thing enforcement checks, and purchased packs sit outside the plan grant.
        - `percent` number, nullable
        - `resets_at` string, date-time, nullable: When the allowance refills: the end of the billing period. `null` with no billing period, and `null` when the plan is set to end at the close of this period, since nothing refills it then. A time already past means a renewal is due; the refill lands when its payment is recorded.
      - `daily` object
        - `used` number, nullable: `null` when today's count could not be read. Not zero: a fabricated idle day is worse than an honest unknown.
        - `limit` number: The plan's published daily credit number. NOT enforced anywhere; do not size work from it.
        - `remaining` number: The wallet again, for the same reason as the monthly figure.
        - `percent` unknown
        - `resets_at` string, date-time, nullable
    - `api` object
      - `rate_limit` number: Requests per minute the limiter holds this credential to. When no live window describes the request, the plan's limit, or for a white-label sub-account its own limit (never its parent's plan's).
      - `rate_limit_window` string
      - `rate_limit_enforced` boolean: False means the number beside it is the configured limit rather than something being applied.
    - `billing_period` object: The period `credits.used` and `verifications.monthly.used` cover. For a short time after a renewal, until its payment is recorded, the counter has not been reset: it holds the previous period's spend and anything spent since, and this covers the same stretch, from when the counter was last reset up to now.
      - `start` string, date-time, nullable
      - `end` string, date-time, nullable

## Other responses

- `401`: No credential, or one that is not accepted. `code` distinguishes `API_KEY_SUSPENDED` (subscription ended; restartable) from `API_KEY_REVOKED`, `SESSION_EXPIRED` and `OAUTH_TOKEN_EXPIRED` (a connected app's one-hour access token lapsed: refresh it and retry, or sign in again if no refresh token was issued or the refresh is refused).
- `403`: Refused, and **the code says which of three problems it is**, and they need different remedies: - `INSUFFICIENT_SCOPE`: the credential is valid but not scoped for this route. With an API key, issue one that carries the scope named in the message. With an OAuth access token, connect the app again asking for that scope as well as the ones it has, unless the message says a connected app can never be given it; then use an API key with every scope the message names, or have the account owner do it in the dashboard. - `PLAN_UPGRADE_REQUIRED`: the account's plan does not include the feature. **No key of any scope reaches it**, so issuing another is wasted work; the remedy is a plan change. Returned by the verification routes for `options.enrich`, and by the finder, the monitors, the audit log, auto-clean and the white-label routes. - `PLAN_LIMIT_EXCEEDED`: the plan includes the feature and the account is at its ceiling (webhook endpoints, team seats, integrations, auto-clean lists). Delete one or upgrade. - `FORBIDDEN`: refused for a reason none of the above covers. `ErrorCode` is an open set, so branch on the code with a default rather than exhaustively.
- `429`: Too many requests. When the server knows how long to wait, `Retry-After` says so and `error.retry_after_seconds` carries the same number; the `X-RateLimit-*` headers say where you stand.
- `default`: Any status this operation does not list. Kawaa's own errors carry the error envelope. The gateway in front of the API can answer any operation with its own bare body (`GatewayError`): a `503` after about 29 seconds, when the request may still have run and been charged, a `413` for a body over about 6 MB, or a `400` for a path with malformed percent-encoding.

---

[API](https://skmtc.dev/kawaa/apis/kawaa-api.md) · [All operations](https://skmtc.dev/kawaa/apis/kawaa-api/llms.txt) · [OpenAPI document](https://skmtc.dev/kawaa/apis/kawaa-api/revisions/d27c9c33f6ba?raw)
