async
const
await
null
=>
return
200 OK
{}
import
try { }
docker
JWT
.then()
POST
node
schema
REST API
NestJS
deploy
mongo
$~`<</ !8 * ^!~797<8
Backend · Node.js · NestJS · API_
Backend

Usage-Based Billing in SaaS: Designing a Scalable Metering System

GOKUL B S
GOKUL B S
Backend Developer
Mar 24, 20266 min read

Stripe handles payments, not your usage logic. Learn how to design a proper metering system for SaaS with real backend examples.

Usage-Based Billing in SaaS: Designing a Scalable Metering System

Most developers assume SaaS billing is just integrating Stripe. In reality, Stripe only handles payments. The real challenge is tracking usage accurately and enforcing limits in your backend.

If your metering system is weak, you either lose revenue or break user experience. This becomes critical as your SaaS scales.

What is Metering?

Metering is the process of tracking how much a tenant uses your system. This can include API calls, storage, feature usage, background jobs, or any billable action.

Every billing system depends on this layer. Without reliable metering, billing logic becomes inaccurate.

Core Flow of Usage-Based Billing

A typical backend flow looks like this: request comes in → validate tenant → check usage → process request → record usage → sync billing.

  • Validate tenant from authentication
  • Check current usage against plan limits
  • Allow or reject request
  • Track usage event
  • Aggregate usage for billing

Tracking Usage Per Tenant

Usage tracking must be fast and scalable. Redis is commonly used because it supports atomic increments and low latency.

// No TS snippet available for this block

This approach works well for real-time enforcement but should be backed by persistent storage for billing records.

Enforcing Limits Before Execution

Limits should always be checked before executing the main business logic. This prevents unnecessary load and ensures predictable system behavior.

// No TS snippet available for this block

You can also implement soft limits (warnings) and hard limits (blocking requests).

Async Usage Tracking with Queues

Writing usage directly to a database for every request does not scale. Instead, emit events and process them asynchronously using queues or Kafka.

// No TS snippet available for this block

Workers can batch and persist this data efficiently, reducing database load.

Handling Idempotency and Duplicates

In distributed systems, duplicate events are common. Your metering system must handle retries safely.

  • Use idempotent event processing
  • Store unique request IDs
  • Avoid double-counting usage
  • Design consumers to be retry-safe

Stripe Integration (Where It Fits)

Stripe should only handle billing and invoicing. Your backend should calculate usage and send aggregated data to Stripe.

Never depend on Stripe to track your usage logic. It does not understand your domain.

Scaling Considerations

  • Use Redis for real-time counters
  • Use Kafka or queues for async aggregation
  • Persist usage data in database for billing history
  • Separate billing service from core business logic
  • Monitor usage per tenant for anomaly detection

Common Mistakes

  • Not tracking usage from day one
  • Mixing billing logic with application logic
  • Relying only on Stripe for metering
  • Ignoring retries and duplicate events
  • Not designing for scale early

Conclusion

Usage-based billing is a system design problem, not just a payment feature. If your metering is accurate, scalable, and reliable, your billing system becomes simple and predictable.

If it is not, you will either lose revenue or damage user trust.

SaaSBillingNode.jsSystem DesignMicroservicesBackend
GOKUL B S
GOKUL B S
Backend Developer · Ortmor Technology Agency Pvt Ltd
More articles →