Skip to main content

Month-to-date usage

Read workspace usage before the invoice, interpret coverage states and watermarks, and reconcile estimates against billing.

Share
Last updated on September 23, 2026
On this page

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. 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. For time-series telemetry instead of billing meters, see Metrics.

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 see estimate-only payloads. See Workspaces, members, & roles.

Read the current month

Authenticate as in REST & GraphQL API (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.

KindUnitMetered on
instance_secondssecondsServices and datastores; tier names the plan tier
egress_bytesbytesServices and datastores
build_secondssecondsServices (builds)
storage_gb_secondsGB-seconds of average used volumeDatastores
sandbox_compute_secondsmilli-vCPU-equivalent secondsSandboxes
disk_gb_secondsGB-seconds of provisioned diskService 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.state describes the evidence behind the totals. Never present a non-complete state as a settled amount.

StateMeaningWhat to do
completeEvery source stream is contiguous through throughReconcile against the invoice
partialSome meters lag; degradedSources names them and through bounds the contiguous prefixTreat every total as provisional; retry later for the remainder
unknownNo 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

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

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 or scale down with Scaling.

The usage-report example (also listed in the examples catalog) 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 (checksum). Source pin: Bex 6f2975248.

Was this page helpful?

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex