DEV Community

Cover image for Usage Metering in Cloud Services: Practices That Hold Up Under Load
Flexprice
Flexprice

Posted on Originally published at flexprice.io

Usage Metering in Cloud Services: Practices That Hold Up Under Load

A metering pipeline that works for the first hundred customers usually fails quietly at the next order of magnitude. Not with an outage. With a queue that retried during a traffic spike and billed a few thousand events twice, discovered three weeks later by a customer who kept their own logs.

Most billing mistakes are not pricing mistakes. They are ingestion mistakes that surface as pricing mistakes, and by the time an invoice is wrong the fix has to reach all the way back to the event stream.

This covers the design decisions that determine whether that happens.

Short answer

Reliable usage metering needs five things: raw events captured at the point of use rather than pre-aggregated summaries, an idempotency key on every event so retries cannot double charge, asynchronous ingestion through a queue so metering never blocks the product path, aggregation kept separate and versioned so historical charges stay reproducible, and monitoring on the pipeline itself.

What is usage metering?

Usage metering records and aggregates events tied to customer activity. Every endpoint call, stored file, or GPU job produces an event, and those events feed the billing system so charges map to actual consumption.

Without it you are estimating. Estimating what to charge, estimating whether the pricing model covers cost, and with no trail to follow when a customer disputes a number. Metering also does work beyond billing: it drives quota enforcement, pricing experiments, and the margin analysis that tells you which accounts are profitable.

Four principles

Record what happened, at the moment it happened. Capture the event at the point of use with the customer reference, timestamp, and usage value. A proxy metric or a delayed snapshot is a different measurement, and the difference shows up on invoices.

Make the system safe to retry. Distributed systems deliver events twice, out of order, and hours late. That is normal operation, not an incident. Every event carries a unique identifier the backend can check before recording, which means the same event delivered ten times is charged once.

Keep data fresh enough to trust. Streaming everything live is not always required. A customer looking at a dashboard showing three-day-old usage is a support ticket in waiting. Hourly aggregation is a reasonable balance for most products.

Be able to explain every charge. Store the raw events, the meter that aggregated them, and the exact pricing config that turned usage into currency. The full chain from event to meter to line item should be reconstructable on request.

Choosing what to meter

Pick the unit the customer already thinks in. A video platform can meter minutes streamed, API calls, or bandwidth. If customers think in processed videos, billing per API call generates confusion on every invoice. The same applies to AI products metering raw tokens without explaining how model choice multiplies them.

Check whether the customer can estimate it. GPU time is an honest metric and an unpredictable one, because duration varies with input size, model, and load. If a customer cannot look at last week and forecast this week, tag events with model type and duration and price in bands.

Map the meter to the cost it drives. If you meter API calls but your infrastructure cost is bandwidth, the pricing model drifts away from the cost curve as usage grows. The meter does not have to mirror spend exactly. It has to track closely enough that margin stays predictable.

Run more than one meter. API calls for billing, monthly actives for feature gating, token usage for internal cost reporting. Not all of these appear on an invoice. All of them should exist, because a metric you did not record is a question you cannot answer later.

Architecture

Events first, never summaries

Start from raw events. Do not write "API calls per hour" into the billing store and discard the detail. Once context is lost at ingestion there is no way to recover it, and replay becomes impossible.

A usage event needs a unique id, a customer reference, an ISO 8601 UTC timestamp, the meter type, the usage value, and optional metadata.

{

  "event_id": "evt_01J8Z2K4RQ",

  "event_name": "inference_request",

  "external_customer_id": "acct_2213",

  "timestamp": "2026-09-11T09:14:33Z",

  "properties": {

    "model": "llama-3-70b",

    "duration_ms": 1840,

    "region": "us-east-1",

    "project": "search-rerank"

  }

}
Enter fullscreen mode Exit fullscreen mode

The metadata is what lets a customer answer "which part of my product drove this bill" without opening a ticket. The sending events guide documents the ingest contract, and validating events covers schema checks before an event reaches the pipeline.

Process asynchronously

Usage tracking should never sit inside the request path. Do not write to a billing database in the middle of an API call.

Push events into a queue. Kafka, SQS, and Redis Streams all work. A metering service consumes, validates, deduplicates, and stores. This makes the system retry-safe, independently scalable, and survivable when the metering service is down while the product keeps serving traffic. Events accumulate in the queue and replay on recovery instead of disappearing.

Flexprice supports this pattern directly through collectors for Kafka, CDP, and OpenTelemetry sources.

Separate ingestion from aggregation

Once events are stored durably, aggregation runs as its own stage: hourly, daily, per customer, per meter. Keeping it separate means aggregation logic can be versioned, new meters can be tested without touching live ones, and any computed value can be audited by recomputing it.

Version everything

Meter definitions change. Pricing experiments ship. Without version tags in the event schema and meter configs stored as code, explaining why last month's charge differs from this month's becomes archaeology.

Choosing the aggregation

The aggregation function decides how the price feels to the customer, and picking the wrong one produces invoices that are technically correct and obviously unfair.

Sum when every event carries value on its own. API calls, minutes streamed, gigabytes transferred. A storage service sums bytes written across the month.

Count unique when distinct entities define the value. One customer generating millions of calls from 500 users is a different account from one generating fewer calls across 500,000 users. Per-active-user pricing and quotas both need this rather than a raw total.

Latest when current state is what matters. A customer uploads and deletes files all month. What matters for storage billing is what they hold at the end of each day. Summing every write would charge for data that no longer exists.

A derived quantity when the billable number is not a single property. A token on a frontier model is not a token on a small open-source model, and a duration in milliseconds is not a billable second. A custom expression evaluates a CEL formula per event and aggregates the result, so input_tokens + output_tokens or gpu_seconds * device_weight becomes the quantity without a second meter. The coefficient has to ride on the event as a property, since the formula can only read what the event carries. The aggregation reference covers the full set of functions.

Staying reliable at scale

Assume retries. At volume, retries are the normal case. Every event needs a deduplication key, typically derived from the event id, timestamp, and customer id. A queue that resends thousands of events during a spike should produce zero additional charges.

Absorb bursts. Usage does not grow linearly. A launch or a customer stress test can push traffic an order of magnitude higher overnight. Queues and buffers are what let ingestion slow down without dropping records.

Keep raw and aggregated data separate. Storing only pre-aggregated totals is convenient until something is wrong and there is nothing to replay. Raw events in an append-only log plus a separate aggregation pipeline means usage can be recomputed and reconciled.

Monitor the pipeline like production. Track ingestion rate against expected volume, duplicate and invalid event counts, lag from ingestion to aggregation, and reconciliation gaps between raw and billed totals. Teams that skip this find out about failures from customers. Monitoring covers what to watch.

Keep the logic boring. Every piece of custom logic in the pipeline is a thing that has to be understood during an incident. Well-defined events, clean schemas, and repeatable jobs scale better than clever ones.

What customers should be able to see

A live view. Usage by day, project, or feature, available without waiting for an invoice. Billing disputes usually come from missing visibility rather than from the price itself. When a customer cannot trace how usage accumulated, the default assumption is that the bill is wrong.

Alerts before the surprise. Notify at 75% of quota. Stop gracefully at a hard cap. This matters most for AI workloads, where a team experimenting with longer prompts can burn through millions of tokens in a day without noticing.

Context on the line item. "23.5M tokens" explains nothing. Broken down by model, by feature, and by project, it explains itself. Exportable usage widgets cover the customer-facing side of this.

Exports and APIs. Finance teams pull usage into their own systems. An export API and webhooks on threshold breaches mean customers can verify your numbers, which is the fastest way to stop them questioning your numbers.

Mistakes that produce disputes

Missing stop events. A long-running job with no clear end inflates usage indefinitely. Enforce timeouts or emit heartbeats.

Double billing from retries. Without idempotency keys, one action looks like ten.

Schema changes without versioning. Renaming a field silently breaks the aggregation that reads it, and the invoice is wrong before anyone notices.

Over-simplified metrics. Raw token counts or undifferentiated API calls stop reflecting value once workloads diverge. Weight by model, region, or project.

Treat metering like observability

Teams that add billing late end up rebuilding it under pressure, usually during the quarter when revenue depends on it working. Metering deserves the same design attention as logging and monitoring, and for the same reason: it is the system you need most when something has already gone wrong.

Flexprice is enterprise-grade, open source usage based billing infrastructure for AI and SaaS companies. It can be deployed in your own VPC, on-prem, or on Flexprice's managed cloud, and all three run the same engine. Event-level tracking, flexible aggregations, thresholds, and audit-ready logs ship in the open source build.

Getting started

The architecture overview covers how ingestion, aggregation, and rating are separated, which is the part worth reading before deciding whether to build this yourself. The source is on GitHub.

Top comments (0)