ServicesMeter

Meter

Usage metering for billable or quota-managed events: admins define metrics (unit, dimensions, quota, graduated pricing), producers ingest idempotent usage events into time buckets, and callers query totals, top consumers, forecasts, cost estimates, and durable reports. Budget enforcement - preflight checks, reserve-then-commit, quota alarms - has its own page: Budgets and quotas. Every example on this page was executed against a live server.

When to reach for it: LLM token accounting with budget cutoff, daily billable totals for API calls or storage, per-user quotas with soft/hard limits, top-K consumers, usage forecasts.

When not to: limiting request rate is the platform rate limiter or a custom Throttle policy - Meter counts cumulative usage against budgets, not requests per window. Virtual currency with transfers and holds belongs to Treasury.

Concepts

  • A metric declares a usage stream: name (llm.tokens), unit, aggregation (sum, max, min, unique, last, top_k) into time buckets, declared dimensions, quota, pricing, retention. Creating one requires an admin or billing_admin token; create is an upsert by name - the met_... id is derived from the name and re-creating bumps version.
  • Events are increments, not rows - each event adds value to the current bucket. Individual events are not stored or queryable.
  • Idempotency keys are required on every event, so retries never double-count.
  • Dimensions (model=gpt-5-mini) attribute usage for top-K breakdowns. Declared required dimensions are enforced at ingest.
  • Everything is scoped per user. Events, usage, quotas, and reservations default to the caller; naming another user_id requires admin or billing_admin, otherwise 403 insufficient_permissions ("cannot act on another user" - verified).
  • Reports are durable rollups - generated snapshots of totals, top consumers, quota status, and estimated cost, listable later.

Define a metric

snug meter metrics create --file ./llm-tokens.json   # admin or billing_admin
snug meter metrics list -q docs4
snug meter metrics get --metric docs4.tokens

The file is a CreateMetricRequest; only name and unit are required:

{
  "name": "docs4.tokens",
  "unit": "token",
  "aggregation": { "kind": "sum", "bucket": "minute", "rollups": ["hour", "day"] },
  "dimensions": [ { "name": "model", "required": true, "cardinality": "medium" } ],
  "quota": { "enabled": true, "soft_limit": 1000, "hard_limit": 2000, "period": "day" },
  "pricing": { "currency": "USD", "tiers": [
    { "up_to": 1000, "unit_price": 0.002 },
    { "up_to": null, "unit_price": 0.001 } ] }
}

With a regular token, create fails with 403 insufficient_permissions; listing and reading metrics is open to any caller. Listing speaks the shared search grammar - verified: --filter "unit:eq:call" narrows to metrics with that unit. There is no delete endpoint - retire a metric by ceasing to ingest it.

Record usage

snug meter log @me tokens:500 --metric docs4.tokens \
  --dimension model=gpt-5-mini --idempotency-key req-789
snug meter batch-log --file events.json     # JSON array of event objects

Over HTTP, single and batch ingest are the same endpoint:

curl -X POST -H "Authorization: Bearer $SNUG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"events":[{"metric":"docs4.tokens","value":120,"idempotency_key":"req-790",
       "dimensions":{"model":"gpt-5-mini","feature":"chat"}}]}' \
  http://localhost:4000/api/v1/meter/events
{
  "status": 202,
  "msg": "Accepted",
  "data": {
    "accepted": 1,
    "duplicates": 0,
    "rejected": 0,
    "results": [
      { "idempotency_key": "req-790", "status": "accepted",
        "bucket": "2026-08-28T12:46:00+00:00" }
    ]
  }
}

Per-event status is accepted, duplicate, rejected_quota, or rejected_schema. Verified behaviors:

  • Replaying the same key with the same payload returns duplicate and adds nothing - safe to retry.
  • The same key with a different payload is 409 idempotency_conflict.
  • Omitting a required dimension is 400 schema_rejected (missing required dimension: model).
  • A rejected event does not fail the request - the batch still returns 202 and the CLI exits 0. Check rejected and results[].status.

Query usage

snug meter usage @me --metric docs4.tokens --period day
snug meter top --metric docs4.tokens --dimension model --period today
snug meter forecast --metric docs4.tokens

usage returns time buckets plus a total; --period selects the resolution (minute, hour, billing_cycle, anything else means day) with a lookback of 60 minutes, 48 hours, or 35 days respectively. top ranks dimension values by usage (gpt-5-mini: 720.0), and forecast projects end-of-period usage from current velocity with a confidence score - all captured live.

Pricing and reports

estimate prices units through the graduated tiers - verified: 1500 units against the tiers above is 1000 x 0.002 + 500 x 0.001 = 2.5 USD:

snug meter estimate --metric docs4.tokens --units 1500
snug meter generate-report --metric docs4.tokens --period day   # admin or billing_admin
snug meter reports --metric docs4.tokens

A report snapshots total, unique_count, estimated_cost, quota status, and top_consumers at generation time. Generating requires admin or billing_admin (verified 403 otherwise); listing existing reports is open to any caller.

Budgets, reservations, and alarms

Preflight checks, the reserve-then-commit arc, over-budget refusals, and quota alarms are on the Budgets and quotas page.

Limits and configuration

Batches up to 1000 events, 12 dimensions per event, 35-day idempotency and hot-bucket retention, and 1-hour reservation TTL by default - all tunable via the METER_* variables in the Meter CONFIG reference.

Reference

On this page