# Usage based billing architecture saas

# Pragmatic Usage-Based Billing Architecture for Modern SaaS

A usage-based billing system splits into five layers: ingestion, aggregation, rating, entitlements and invoicing. Each has its own latency and correctness requirements, and most billing failures come from merging two that should stay apart. This piece covers the event schema, the three bugs that cost real money, and two implementation patterns: a bootstrap build on a relational ledger, and a scale build on an event log plus a dedicated rating engine.

**Key takeaways**

- Metering and invoicing run on different clocks. Decouple them.
- Idempotency keys at ingestion prevent the most expensive class of bug.
- Entitlement checks are a real-time safety system, not a billing feature.
- Build the event log and the entitlement check. Buy the rating and invoicing.

## Why seat-based billing stacks break under consumption pricing

A background job loops. An LLM-backed feature retries on every failure. By the time the month-end invoice runs, a customer owes thousands of dollars they never meant to spend, and you get to choose between eating the cost and losing the account.

The invoice was correct. The architecture was wrong. It treated billing as a monthly batch job when the product needed a real-time control surface.

Subscription billing is simple because it stores a plan and charges it on a cycle. Consumption billing needs three things that a plan record cannot provide: an auditable record of every unit consumed, a way to price those units, and a way to stop consumption before the money is gone. Seat-based software could skip the third one because the marginal cost of one more user was close to nothing. AI-native products cannot. Every request carries real inference and compute cost, which means a flat subscription can quietly be sold at a loss.

The pricing shift is well documented, if you read the numbers as directional rather than precise. OpenView's SaaS benchmarks put the share of SaaS companies with a usage-based component at roughly 41 to 46 percent, mostly hybrid plans pairing a base subscription with metered consumption. Metronome's *State of Usage-Based Pricing* report found that 78 percent of SaaS companies using the model adopted it within the previous five years, with close to half of those moving in the previous two.

What matters architecturally is the clock. Metering and invoicing answer different questions on different schedules, and nearly every billing bug traces back to a system that conflated them.

## The five layers and what each one owns

LayerWhat it ownsClockIngestionRaw events, deduplicationMilliseconds, write-optimisedAggregationBillable totals per metricMinutes to hoursEntitlementsWhether a request may proceedSingle-digit millisecondsRatingUnits converted to currencyBatch, re-runnableInvoicingLine items, tax, collectionMonthly, plus settlement window### Ingestion captures what happened, and nothing else

This layer records consumption and discards duplicates. It should be write-optimised and boring. No pricing logic, no plan lookups, no joins against customer records.

The event schema is the most copyable artefact in the whole design, so get it right early. Five required fields: tenant ID, metric name, quantity, timestamp, and an idempotency key. Add a dimensions object for anything you might price on later, such as model name, region or endpoint. Dimensions you capture today cost nothing. Dimensions you failed to capture cannot be reconstructed.

### Aggregation turns a stream into a number

Raw time-series events are useless for billing until they are grouped. This layer produces billable totals per metric per period, and it runs on a clock measured in minutes or hours rather than milliseconds.

Four aggregation functions cover most pricing: count, sum, max for peak concurrency, and unique for distinct active resources. The choice constrains your storage. Count and sum roll up incrementally, so you can keep cheap hourly summaries. Max and unique generally need the underlying rows or a sketch structure, which pushes you toward a columnar store.

### Entitlements decide whether a request may proceed

This is a synchronous read on the hot path, answered in single-digit milliseconds: does this tenant have credits left, and has it crossed a spend cap? Nothing about this question belongs to the invoice cycle.

Teams merge entitlements into billing because both involve money. The result is a system that discovers overspend thirty days late. [Stigg's breakdown of billing system design](https://www.stigg.io/blog-posts/billing-system-design) treats the separation as structural, and for anything with real marginal cost per request it is.

### Rating turns units into currency

Graduated tiers, volume pricing, matrix rates, committed minimums, per-token costs. All of it lives here, and none of it should live in application code.

Rating runs as a batch process, and the important property is that it is re-runnable. If a plan changes retroactively or a pricing bug slips into production, you recalculate from immutable usage rather than patching numbers in place. [Chargebee's write-up on usage-based billing architecture](https://www.chargebee.com/blog/usage-based-billing-architecture-pricing-agility/) makes the same point from the other direction: decoupling rating from metering is what keeps pricing changes from becoming deploys.

### Invoicing collects the money

Line items, tax, payment gateways, revenue recognition. This layer runs monthly, with a settlement window after the cycle boundary so late-arriving usage still lands on the right bill. If your revenue recognition has to survive an audit under ASC 606, this is the layer where that requirement bites, and it is the strongest argument for not writing it yourself.

## The three failure modes that cost real money

### Double billing

Message queues and webhook deliveries guarantee at-least-once delivery, not exactly-once. SQS, Kafka and retrying HTTP clients will all hand you the same event twice under the right conditions. Without a deterministic key, those retries become revenue you have to refund, and the customers who notice are the ones you least want to annoy.

The fix is an idempotency key enforced at write time. Build it from values the client can reproduce, typically tenant ID plus request ID plus a timestamp window. Two reasonable implementations: a unique constraint in the relational store, which is durable and simple but puts the check on your database's critical path, or a Redis `SET NX` check in front of the write, which is fast but depends on eviction policy and key TTLs being set deliberately. [This engineering account of building a usage engine from scratch](https://dev.to/ajithmanmu/i-built-a-usage-based-billing-engine-from-scratch-heres-how-it-works-2l58) walks through the Redis approach in practice.

Pick one and make it mandatory at the ingestion boundary. A deduplication strategy that some producers skip is not a deduplication strategy.

### Unbilled usage from late events

Offline workers, edge devices and batch jobs deliver usage hours after it happened. If the invoice closes at midnight on the last day of the cycle, that usage either disappears or lands on the wrong month.

Two fixes, and you need both. Record the time usage occurred, not the time your server received it. Then keep the cycle open for a settlement window before finalising, commonly something in the range of 24 to 72 hours depending on how late your slowest producer runs. Measure your actual arrival lag before picking the number rather than copying someone else's.

### Runaway spend

Back to the looping agent. The cause is a system that relies on monthly invoicing to govern real-time consumption, which is like fitting a car with an annual speed report instead of a brake.

The fix is the entitlement layer doing its job independently of the rating pipeline: a synchronous read against a prepaid balance or a hard spend ceiling on every expensive request, plus warning webhooks at threshold crossings so customers see the burn while they can still stop it. Hard ceilings feel unfriendly until the first time one saves an account.

## Pattern A: the bootstrap build

For teams with one or two metrics and modest volume. You do not need a data pipeline yet, and building one now will cost you months you should spend on the product.

Use a PostgreSQL ledger table, or prepaid credits if your pricing allows it. Decrement balances with optimistic locking or a row-level lock. Keep Redis counters in front for fast entitlement reads, with PostgreSQL as the durable source of truth. Roll usage up hourly and push the summaries to Stripe Billing as pre-aggregated meter events rather than firing one call per request.

That last point is not stylistic. Stripe rate-limits direct meter event ingestion, with documented throughput in the region of 100 requests per second, and raw per-request calls hit that ceiling faster than teams expect. Rate limits change, so check the current Stripe documentation before sizing anything around a specific figure. Pre-aggregation sidesteps the issue entirely and reduces your API costs besides.

Be honest about the ceiling on this pattern. It handles simple metered pricing well. It handles multi-dimensional pricing, retroactive plan changes, complex proration and customer-facing usage analytics badly, and each of those will arrive as a request from sales eventually.

## Pattern B: the scale build

For multiple metrics, AI workloads, or contract-level pricing with commitments and drawdowns.

The durable record becomes an append-only event log, written to object storage or a columnar database and never mutated. Put an event bus between the application and the pipeline so a slow billing path cannot slow a user request. Redis Streams, RabbitMQ or a managed queue like SQS will carry you a long way. Aggregation moves to ClickHouse or an equivalent OLAP engine. Rating moves to a dedicated engine, which is the whole point: pricing rules stop living in application code.

Resist Kafka and Flink until something forces your hand. A reference architecture showing them is not a reason. Sustained volume beyond what a managed queue absorbs, multiple independent consumers of the same stream, or a genuine need to replay history through new logic are reasons. Measure first.

On rating engines, the realistic options separate cleanly:

- **Lago** is open source under AGPLv3 and self-hostable, which suits teams that want the event data to stay in their own infrastructure. Confirm the current licence terms yourself before committing, since licensing models shift.
- **Orb** lets you define metrics in SQL, which fits teams whose pricing logic is genuinely query-shaped.
- **Metronome** targets enterprise contracts, commitments and drawdowns.
- **Stripe's metering API** is the path of least resistance if your payments already live there.

A [side-by-side comparison of Metronome, Orb and Lago](https://fintechspecs.com/blog/metronome-vs-orb-vs-lago/) covers the architectural tradeoffs between hosted and self-hosted in more detail.

## Build, buy, or extend

Three checks, answered honestly, settle most of this. Count the metrics you realistically expect to price on within a year. Decide whether customers need to see their own usage in your product. Establish whether your revenue recognition has to satisfy an auditor.

Any one of those tipping toward complexity means you should not be writing a rating engine.

Our working view at AWcode is simple enough to state in two sentences. Build the event log and the entitlement check, because both are tightly coupled to your product, cheap to own, and impossible to outsource cleanly. Buy the rating and invoicing, because proration, tiered discounts, tax handling and revenue recognition will absorb months of engineering and produce nothing a customer will ever thank you for.

The trap on the other side is just as real. Enterprise billing platforms priced for Series C companies rarely make sense for a bootstrapped product with forty customers. Start with the cheapest thing that enforces correctness, and move when a specific requirement breaks it.

## A build order, not a conclusion

Four steps, in this order, starting Monday.

1. Define the event schema and freeze it. Tenant, metric, quantity, occurrence timestamp, idempotency key, dimensions.
2. Enforce the idempotency key at the ingestion boundary, with no exceptions for internal producers.
3. Split the entitlement check out of the billing path and make it synchronous.
4. Introduce a rating engine when your pricing needs tiers, a second metric, or anything retroactive.

AWcode builds and runs its own SaaS products and helps founders take web apps from MVP to production, and billing architecture is one of the places where early shortcuts compound fastest. The hard part of usage-based billing was never the counting. It is counting exactly once, knowing when to stop, and keeping pricing rules out of your product code.

## FAQ

**Should usage events go through the same database as product data?**Not for long. Product databases are tuned for state updates, while usage events are immutable, high-volume and append-only, and they will bloat tables and degrade query performance as volume grows. Starting in PostgreSQL is fine at low volume. Plan the move to a separate store before it becomes urgent.

**How long should the settlement window be before finalising an invoice?**Something in the range of 24 to 72 hours works for most systems, but the right answer depends on your slowest producer. Measure how late your offline workers and edge clients actually deliver, then set the window above the observed tail.

**Can Stripe Billing alone handle usage-based pricing, or is a separate engine required?**Stripe Billing handles straightforward metered pricing well, provided you pre-aggregate usage rather than sending raw per-request events. Once you need real-time entitlement enforcement, multi-dimensional rating or prepaid drawdowns, you will want a dedicated engine in front of it.

---

**How this post looks on the live site:** Rendered in a windowed news reader inside the AWcode OS desktop, alongside other posts.

---

**Canonical HTML version:** https://awcode.co.uk/news/usage-based-billing-architecture-saas

**About this document:** This is a plain-Markdown mirror of an AWcode.com page, served so that LLMs and agents can read the content without executing the site's retro-OS JavaScript UI. The HTML page at the canonical URL above carries the same content and is also fully indexable.

## Machine-readable

Resources for AI agents, LLMs and integrations:

- [https://awcode.co.uk/llms.txt](https://awcode.co.uk/llms.txt) — index of markdown mirrors
- [https://awcode.co.uk/llms-full.txt](https://awcode.co.uk/llms-full.txt) — every page + post concatenated
- [https://awcode.co.uk/sitemap.xml](https://awcode.co.uk/sitemap.xml) — full sitemap
- [https://awcode.co.uk/robots.txt](https://awcode.co.uk/robots.txt) — crawl + Content-Signal policy
- [https://awcode.co.uk/ai.txt](https://awcode.co.uk/ai.txt) — AI access policy
- [https://awcode.co.uk/openapi.json](https://awcode.co.uk/openapi.json) — OpenAPI 3.1 spec
- [https://awcode.co.uk/.well-known/api-catalog](https://awcode.co.uk/.well-known/api-catalog) — RFC 9264 / 9727 link set
- [https://awcode.co.uk/.well-known/mcp.json](https://awcode.co.uk/.well-known/mcp.json) — MCP discovery
- [https://awcode.co.uk/mcp](https://awcode.co.uk/mcp) — MCP server endpoint (POST JSON-RPC 2.0)
- [https://awcode.co.uk/.well-known/agent-skills/index.json](https://awcode.co.uk/.well-known/agent-skills/index.json) — Agent Skills index

### Public API — concrete examples

- [GET https://awcode.co.uk/api/posts](https://awcode.co.uk/api/posts) — list recent published posts
- [GET https://awcode.co.uk/api/posts/how-startup-studios-de-risk-saas-builds-before-the-first-sprint](https://awcode.co.uk/api/posts/how-startup-studios-de-risk-saas-builds-before-the-first-sprint) — fetch one post
- [GET https://awcode.co.uk/api/pages/about](https://awcode.co.uk/api/pages/about) — fetch the about page

### Markdown mirrors — concrete examples

- [https://awcode.co.uk/index.md](https://awcode.co.uk/index.md) — homepage
- [https://awcode.co.uk/about.md](https://awcode.co.uk/about.md) — about page
- [https://awcode.co.uk/news/how-startup-studios-de-risk-saas-builds-before-the-first-sprint.md](https://awcode.co.uk/news/how-startup-studios-de-risk-saas-builds-before-the-first-sprint.md) — one news post
