---
id: platform/usage
title: Month-to-date usage
description: Read workspace usage before the invoice, interpret coverage states and watermarks, and reconcile estimates against billing.
keywords: [bex, usage, metering, billing, estimate, coverage, invoice]
last_updated: 2026-09-23
---

`GET /v1/usage` returns the calling workspace's month-to-date usage: per-resource
meter totals, an advisory cost estimate, the real billing projection when the
caller may see it, and a coverage record describing how complete the totals are.
Read it before the invoice arrives to reconcile what you consumed against what
you will be charged.

This endpoint is a Bex extension — Render's public REST API has no
usage or billing endpoints — so it is documented here rather than in the
Render-parity [API reference](./api-reference.md). The same month-to-date verb
also answers GraphQL `Query.usage` and the MCP `get_usage` tool with identical
semantics; the REST and MCP surfaces return identical JSON.

For payment readiness and who can bind a payment method, start with
[Billing setup](./billing-setup.md). For time-series telemetry instead of
billing meters, see [Metrics](./metrics.md).

## Who can read usage

Any workspace member can read usage. The `billing` projection — real cost,
finalized invoices, and credit balance — is attached only for callers holding
the billing relation (the **admin** or **billing** role, `can_manage_billing`).
Everyone else still gets usage rows and the advisory estimate.

A third-party human token carrying only `bex.read` is treated as not privileged
even when the delegated user holds a billing role, so integrations authorized
through [User-authorized OAuth](./oauth-integrations.md) see estimate-only
payloads. See [Workspaces, members, & roles](./members.md).

## Read the current month

Authenticate as in [REST & GraphQL API](./api.md) (an API key exchanged for a
short-lived Bearer [REDACTED] then:

```bash
curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer $BEX_TOKEN" \
  "$BEX_API_URL/v1/usage" | jq .
```

The response echoes the period it covers along with one entry per metered
resource:

```jsonc
{
  "workspaceId": "wks_123",
  "period": "2026-09",
  "services": [
    {
      "serviceId": "srv_abc",
      "serviceName": "web",
      "resourceKind": "service",
      "rows": [
        { "kind": "instance_seconds", "tier": "standard", "total": 521013 },
        { "kind": "egress_bytes", "total": 88173902 },
      ],
    },
  ],
  "estimatedCost": { "totalUsd": "4.17", "meters": [/* … */], "resources": [/* … */] },
  "billing": { "currentCost": {/* … */}, "invoices": [] },
  "coverage": { "state": "complete", "through": "2026-09-18T11:00:00Z", "degradedSources": [] },
}
```

Query another workspace with `?ownerId=<workspace-id>`; an id the caller is
not a member of is refused with `403`. Read a past month with
`?period=YYYY-MM` (below).

Resources you already deleted keep the name their usage accrued under, marked
with `"deleted": true`, so a charge keeps naming its resource after deletion.
When Bex never recorded a name for an id, presenters fall back to the id.

## Meters and units

Totals are in the natural unit of each kind. `resourceKind` is one of
`service`, `postgres`, `key_value`, or `sandbox`.

| Kind | Unit | Metered on |
| --- | --- | --- |
| `instance_seconds` | seconds | Services and datastores; `tier` names the plan tier |
| `egress_bytes` | bytes | Services and datastores |
| `build_seconds` | seconds | Services (builds) |
| `storage_gb_seconds` | GB-seconds of average *used* volume | Datastores |
| `sandbox_compute_seconds` | milli-vCPU-equivalent seconds | Sandboxes |
| `disk_gb_seconds` | GB-seconds of *provisioned* disk | Service disks (reserved capacity, running or not) |

Underneath, meters are recorded hourly and folded into monthly aggregates
once they age out of the hot window (three calendar months by default); the
endpoint always answers month-to-date totals, never hourly detail.

Each meter advances on an independent cursor, so one stalled source never holds
the other meters back — which is exactly what the coverage record reports.

## Coverage states {#coverage-states}

`coverage.state` describes the evidence behind the totals. Never present a
non-complete state as a settled amount.

| State | Meaning | What to do |
| --- | --- | --- |
| `complete` | Every source stream is contiguous through `through` | Reconcile against the invoice |
| `partial` | Some meters lag; `degradedSources` names them and `through` bounds the contiguous prefix | Treat every total as provisional; retry later for the remainder |
| `unknown` | No evidence record (including every historical month) | Do not reconcile; the totals carry no completeness claim |

`through` is the common end of every active source stream's contiguous hourly
prefix. It is absent for `unknown` and never inferred for historical periods:
a past month always reports `unknown` because the evidence record is only kept
for the current month. A failed window is retried on the next hourly pass
without a restart, so `partial` usually converges to `complete` on its own.

## Estimate semantics {#estimate-semantics}

`estimatedCost` is advisory. `totalUsd` is always present (`"0.00"` when there
is no billable usage); the `meters` breakdown omits lines whose rounded cost is
under a cent, while the `resources` breakdown keeps every metered line,
including free tiers and sub-cent costs — it is the only place that shows how
much was consumed, not just what it costs.

Absent `billing` means **no billing contract data**, never zero: the workspace
may have no contract, be comped or excluded, have billing disabled, or the
Stripe read may have degraded. When `billing` is present, `currentCost` is the
real Stripe-computed cost and `invoices` holds finalized invoices; reconcile
the estimate against those rather than treating the estimate as the charge.

## Read a past month {#read-a-past-month}

```bash
curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer $BEX_TOKEN" \
  "$BEX_API_URL/v1/usage?period=2026-08" | jq .coverage
```

`period` must be a calendar month as `YYYY-MM`. A malformed value is a `400`
— Bex refuses to substitute a different month for a misspelled one, because
usage figures drive billing conversations. A current or future month clamps to
now (month-to-date); a past month returns that month's end. The response echoes
the period actually used.

## Reconcile before the invoice

1. Read the current month and check `coverage.state`. Proceed only on
   `complete`; on `partial`, note `through` and the degraded sources and retry
   after the next hourly pass.
2. Compare `estimatedCost.totalUsd` with `billing.currentCost`. A gap usually
   means credits, a comped period, or pricing the estimate does not model —
   the billing projection is authoritative.
3. Walk `estimatedCost.resources` to find which service or datastore dominates,
   then confirm against [Metrics](./metrics.md) or scale down with
   [Scaling](./scaling.md).

The [`usage-report`](/examples/usage-report/README.md) example (also listed
in the [examples catalog](/examples?task=ops)) automates this read: it prints
the per-resource table with a coverage banner, labels non-complete totals as
provisional, and never mistakes absent billing data for zero. Download it as
[`usage-report-latest.zip`](/examples/usage-report-latest.zip)
([checksum](/examples/usage-report-latest.zip.sha256)). Source pin: Bex
`6f2975248`.
