# Billed usage over a window, broken down by one dimension

> Reads the durable billing rollup (`usage_daily`) for the key's organization.

Source: https://docs.spicrawl.com/api-reference/usage/usage-get

## GET /v1/usage

Operation ID: `usageGet`. API key scope: `read`.

Reads the durable billing rollup (`usage_daily`) for the key's organization. `totals` is the billed
quantity; `groups` plus `unattributed` equals `totals` per metric. If they do not, a `warnings` entry
says so; trust `totals`.

Pitfalls:
- `to` is EXCLUSIVE. One day 2026-08-01 is `from=2026-08-01&to=2026-08-02`. Days are UTC.
- Default window: the last 30 UTC days including today. Maximum 400 days per call.
- A key filed under a project is pinned to it: `project_id` naming another project is 404, and
  omitting it reads only the key's project. A key with no project may name any project in the org.
- `group_by=key` returns a different body, `UsageByKey`: per API key (`key_id`, `name`, `prefix`,
  `project_id`, `revoked`), counters per metric per UTC day, from the per-key rollup. Requests made
  without a key (the dashboard) are not in it. No caller IPs or countries are returned.
- `group_by=engine`: the per-engine table is often empty today, so the whole quantity may appear in
  `unattributed` with a warning. That is missing attribution, not missing usage.
- `group_by=feature` cannot attribute `requests`, `engine_ms`, byte counters or the non-batch part of
  `credits`; they land in `unattributed`. Batch spend is attributed to the `batch` group.
- `credits` is total credit spend, batch items included, in every grouping.
- Check `stale_seconds`: a large value means the rollup is behind.

### Example

```bash
curl "https://api.spicrawl.com/v1/usage" \
  -H "Authorization: Bearer $SPICRAWL_API_KEY"
```

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `from` | query | string (date) | no | First UTC day, inclusive, `YYYY-MM-DD`. Defaults to 29 days before today. |
| `to` | query | string (date) | no | End UTC day, EXCLUSIVE, `YYYY-MM-DD`. Defaults to tomorrow. Must be after `from`, and the span must be at most 400 days. |
| `group_by` | query | string: `day`, `project`, `engine`, `feature`, `key`; default `"day"` | no | Breakdown dimension. |
| `project_id` | query | string | no | Restrict to one project in the organization (ULID or UUID). Omitted means every project. |
| `metrics` | query | array of string: `requests`, `credits`, `engine_ms`, `bytes_egress`, `bytes_ingress`, `proxy_bytes_dc`, `proxy_bytes_resi`, `extractions`, `ai_extractions`, `batch_items`, `requests_failed`, `requests_timeout`, `feature_actions`, `feature_ai_extract`, `feature_autoparse`, `feature_extract`, `feature_extract_preset`, `feature_js_render`, `feature_links`, `feature_network_capture`, `feature_pdf`, `feature_proxy_country`, `feature_proxy_custom`, `feature_screenshot`, `feature_session`, `client_cli`, `client_mcp`, `client_sdk`, `client_other` | no | Comma-separated metric names to include. Omitted means all. An unknown name is refused with 400. |

### Responses

#### 200

The breakdown.

`application/json`.



Example `byKey`

```json
{
  "period": {
    "start": "2026-09-01",
    "end": "2026-09-03",
    "days": 2,
    "bounds": "[start, end)"
  },
  "group_by": "key",
  "keys": [
    {
      "key_id": "01J9Z3K6V1Q2W3E4R5T6Y7U8I9",
      "name": "ci-pipeline",
      "prefix": "spicrawl_live_a1b2",
      "project_id": "01J9Z3K6V1Q2W3E4R5T6Y7U8P0",
      "revoked": false,
      "days": [
        {
          "day": "2026-09-01",
          "metrics": {
            "requests": 120,
            "client_cli": 120,
            "feature_screenshot": 4
          }
        }
      ],
      "totals": {
        "requests": 120,
        "client_cli": 120,
        "feature_screenshot": 4
      }
    }
  ]
}
```

Example `byDay`

```json
{
  "period": {
    "start": "2026-09-01",
    "end": "2026-09-03",
    "days": 2,
    "bounds": "[start, end)"
  },
  "group_by": "day",
  "groups": [
    {
      "key": "2026-09-01",
      "metrics": {
        "requests": 1200,
        "credits": 4800000000
      }
    },
    {
      "key": "2026-09-02",
      "metrics": {
        "requests": 800,
        "credits": 3100000000
      }
    }
  ],
  "totals": {
    "requests": 2000,
    "credits": 7900000000
  },
  "unattributed": {
    "requests": 0,
    "credits": 0
  },
  "usage_as_of": "2026-09-22T11:55:00Z",
  "stale_seconds": 300
}
```

#### 400

The request is invalid. Not retryable; fix the request using `code` and `diagnostics.hint`.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

#### 401

Missing, invalid, revoked or expired key. Not retryable with the same key.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

#### 403

The key lacks the scope this route requires (`ERR::AUTH::INSUFFICIENT_SCOPE`), or the action is not permitted.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

#### 500

Internal error. Retryable when `retryable` is true.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

Full OpenAPI spec: https://docs.spicrawl.com/openapi.yaml
