How to Build Usage-Based Billing Infrastructure That Survives Production
Every API-first company hits the same wall. The billing code your first engineer wrote in a weekend now takes a quarter to change. Finance closes the month in a spreadsheet. Product wants to launch a new pricing model. Nobody wants to touch the invoice generator.
Usage-based billing infrastructure is what you build once, so you never have that meeting again. This is the shape of the system that survives production.
What is usage-based billing infrastructure?
It is not a pricing page. It is the pipeline between a raw usage event, emitted somewhere in your product, and a correct line item on a customer invoice that finance can close on.
The pipeline has nine responsibilities, in order.
- Ingest the event with an idempotency key.
- Deduplicate against replays and retries.
- Aggregate the event into a meter, per customer, per period.
- Price the meter under the pricing model version active for that customer.
- Apply proration, commitments, credits, and discounts.
- Generate the draft invoice in real time.
- Finalize the invoice on its cycle boundary.
- Collect payment through Stripe or a bank rail.
- Post the recognition schedule into NetSuite or your GL.
Every system that fails, fails because two of these responsibilities got glued together. The most common glue point is between step 3 and step 4, where the meter and the pricing model share a database table. That is where pricing velocity goes to die.
What events should you actually meter?
Fewer than you think. Metering everything is the fastest way to make the system unmaintainable.
The rule: meter the units that show up on the invoice, plus one layer of dimensional context for future pricing. Nothing else.
For an API company, that usually looks like this.
| Meter | Unit | Context you emit |
|---|---|---|
| API calls | 1 per request | endpoint, method, status_code |
| Data transferred | bytes | region, direction |
| Compute time | milliseconds | instance_class |
| Storage | GB-hours | tier |
| Model tokens | input, output | model_id |
Notice what is missing. No user_id inside the event payload. No feature flags. No product analytics. Those live in your warehouse. If you conflate metering with analytics, your ingest layer becomes a bottleneck and your finance team stops trusting it.
How do you keep the meter and the pricing model separate?
This is the single design decision that determines whether you can ship a pricing change in an afternoon or a quarter.
The meter answers one question: how many billable units did this customer consume in this period, per meter. Nothing about price.
The pricing model answers a different question: given a set of meter totals, what is the invoice. Nothing about how the meters were counted.
Concretely, that means two data planes.
- Meter store. Immutable, append-only. Keyed by customer, meter, and event time. Never mutated when pricing changes.
- Pricing store. Versioned. Every customer contract points at a version. New versions can be drafted, back-tested, and rolled out without touching product code.
When those two planes share a table, a pricing change becomes a data migration, which is why it takes a quarter. When they are separate, a pricing change is a config edit, plus a back-test.
How do you back-test a new pricing model?
Replay every event from the last 30 or 90 days against the draft pricing model. Compare per-customer totals to what actually invoiced. Sort by absolute revenue delta.
You want three answers before you roll out.
- Aggregate delta. How much more or less revenue would you have booked. If the number is inside plus or minus 3% and you did not intend a raise, stop.
- Distribution. Which customers pay more. Which pay less. Which stay flat. A change that is net-neutral on aggregate but shifts 15% of customers by more than 20% is a communications problem, not a revenue problem.
- Outliers. The top ten deltas. Every one of them is a phone call the account manager has to make. If any of the top ten is a mistake in the model, catch it here, not on an invoice.
A metering layer that cannot back-test in under an hour, at full production event volume, is not billing infrastructure. It is a spreadsheet with worse latency.
How do you handle proration and mid-cycle plan changes?
Split the period at the moment of change. Price each segment under the correct model version. Show both segments on the invoice as separate, labeled line items.
The three inputs to a proration calculation.
- Effective time of change, to the second.
- Model version active before, and after.
- Commitment balance, if a drawdown contract, at the moment of change.
The two most common bugs: applying the new price to the entire cycle, and rounding the segment durations to whole days. The first steals from the customer. The second steals from you. Both create audit findings.
How should invoices flow into Stripe and NetSuite?
Stripe is your payment rail. NetSuite, or your ERP, is your ledger. They are not the same system, and treating them as such is what breaks the finance close.
- Stripe receives the finalized invoice as a single object, with itemized lines for display and dunning. It handles payment method, retries, and receipt.
- NetSuite receives the recognition schedule, not the invoice. For a monthly subscription paid in advance, that is a deferred-revenue booking and a monthly recognition entry. For consumption, it is straight-line recognition against the meter totals.
Every line item on the invoice must trace back to the exact events behind it. That traceability is what makes audit season boring, and what lets you defend a mystery number on a customer call.
What does the finance close look like when metering is right?
Thirty minutes, not three days.
- The metering layer publishes a per-customer, per-meter checksum for the closed period.
- Finance diffs that checksum against the warehouse count that your data team ships nightly.
- Any customer with a non-zero delta gets an adjustment note before the invoice finalizes.
- After finalization, only late events generate adjustments, on the next cycle, with a documented reason code.
If your close still runs on a spreadsheet, the metering layer is not the constraint. The absence of a per-meter checksum is.
The mistake to avoid
Most teams build usage-based billing the same way they build every other feature: a service that emits events, a table that stores them, a job that reads the table and mints invoices. That works up to about $2M in ARR, then quietly stops working. The pricing model gets encoded in the invoice job. The events get shaped by whoever needed a new dimension last quarter. Finance stops trusting the numbers and reconciles in Excel. The fix is not more engineering hours. It is a hard separation between the meter and the pricing model, with a back-test that runs against real usage before any change reaches a customer.
Frequently asked questions
What is usage-based billing infrastructure?
It is the layer between your product's raw usage events and a correct invoice line item. It handles ingestion, deduplication, aggregation, pricing, proration, commitments, credits, invoice generation, payment routing, and revenue recognition. In practice, most teams start with two of these and discover they need all nine within 18 months.
Do we need dedicated infrastructure, or can Stripe Billing handle it?
Stripe Billing handles simple per-seat and single-meter pricing well. It struggles with tiered plus prepaid credits, back-dated pricing changes, multi-meter invoices, and rev-rec into NetSuite. If your pricing has more than one meter, or you need to version pricing without a deploy, a dedicated layer usually pays for itself inside two quarters.
How fast should the metering layer be?
Ingest should sustain 100,000 events per second per account with a p99 write acknowledgment under 100 milliseconds. Query for draft-invoice totals should return in under 2 seconds for any customer, any period. Anything slower breaks the customer-facing spend dashboard and the finance close, in that order.
What is the biggest hidden cost of hand-rolled billing?
Pricing velocity, not engineering time. Teams that hand-roll billing report 3 to 6 months per pricing model change, because the price is entangled with the product code. That delay costs more in unmonetized experiments than the engineers ever cost in salary.
How do you keep events from being dropped or double-counted?
Every event carries an idempotency key you control, typically your request ID. The metering layer deduplicates on that key over a rolling window of at least 7 days. Ingestion is acknowledged only after the event is durably written. Reconciliation happens by comparing per-meter counts and checksums against your warehouse before an invoice finalizes.
Ship a pricing change in an afternoon
Orvarex turns raw usage events into metered invoices, versioned pricing models, and clean rev-rec entries in Stripe and NetSuite.
Request early access