# GET /v1/usage

Balance and usage for the current period

The credits available to the key's account and the scans made in the current usage period, counted by status. Every settled scan is counted, whether or not its result was stored, so the figures do not decay as retention windows pass. A key with no account behind it (the public sandbox key) reports a null balance and zero counters.

Reports the balance in credits and the counters for the current period. One credit is one US cent, and one recognized document draws one, so the balance is also the number of documents left.

A sandbox key reads its own account's real figures. Its own scans are never charged, so they move the counters and not the balance.

## Request

| | |
|---|---|
| Method | `GET` |
| Path | `/v1/usage` |
| Authentication | `Authorization: Bearer <api key>` |

## Responses

### 200

Balance and counters.

Body: `Usage`.

| Field | Type | Description |
|---|---|---|
| `balance_credits` | integer \| null | Credits currently available to the account; null for keys without a balance. |
| `period` | object | Bounds of the current usage period (UTC calendar month). |
| `scans` | object |  |
| `credits_spent` | integer | Credits charged within the period. |

```json
{
  "balance_credits": 11,
  "period": {
    "start": "2026-09-01T00:00:00Z",
    "end": "2026-10-01T00:00:00Z"
  },
  "scans": {
    "total": 12,
    "billed": 9,
    "by_status": {
      "recognized": 9,
      "no_document_found": 1,
      "unreadable": 1,
      "unsupported_document": 1,
      "rejected": 0
    }
  },
  "credits_spent": 9
}
```

### 401

Missing or malformed `Authorization: Bearer <api key>` header, or an unknown key.

Body: `ErrorResponse`.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Send your API key as `Authorization: Bearer <key>`.",
    "docs_url": "https://doc.cheap/docs/errors/unauthorized",
    "request_id": "req_9e6b1f7c-2d4a-4b83-9c51-7f0ad3e8b642",
    "event_id": null
  }
}
```

### 429

Rate limit exceeded for this key or IP.

Body: `ErrorResponse`.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Sandbox limit of 10 requests per hour per IP reached.",
    "docs_url": "https://doc.cheap/docs/errors/rate_limited",
    "request_id": "req_9e6b1f7c-2d4a-4b83-9c51-7f0ad3e8b642",
    "event_id": null
  }
}
```

### 500

Unexpected failure on the server; the scan was not charged.

Body: `ErrorResponse`.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Unexpected error.",
    "docs_url": "https://doc.cheap/docs/errors/internal_error",
    "request_id": "req_9e6b1f7c-2d4a-4b83-9c51-7f0ad3e8b642",
    "event_id": null
  }
}
```

### 503

Temporarily unable to serve the request: a store this service depends on is unreachable. Nothing was charged and it may be retried, after the `Retry-After` seconds where that header is present.

Body: `ErrorResponse`.

```json
{
  "error": {
    "code": "service_unavailable",
    "message": "The service is temporarily unable to handle this request; nothing was charged. Retry shortly.",
    "docs_url": "https://doc.cheap/docs/errors/service_unavailable",
    "request_id": "req_9e6b1f7c-2d4a-4b83-9c51-7f0ad3e8b642",
    "event_id": null
  }
}
```

## Error codes reachable here

| Code | When | Page |
|---|---|---|
| `internal_error` | The service failed in a way it does not model. | [internal_error](/errors/internal_error) |
| `rate_limited` | The caller is over the rate limit for its key kind. | [rate_limited](/errors/rate_limited) |
| `service_unavailable` | A store this endpoint reads from is unreachable, so the answer could not be produced. Nothing was charged; retry after the `Retry-After` seconds. | [service_unavailable](/errors/service_unavailable) |
| `unauthorized` | The `Authorization` header is missing, malformed or names an unknown key. | [unauthorized](/errors/unauthorized) |

Every error body carries the same shape; the full list is on [Errors](/reference/errors).
