Most SaaS apps price their features with flat-rate plans.
But what if you could scale your revenue with how your customers actually use your product?
That’s the magic of usage-based billing — and why Stripe, AWS, and Twilio are thriving.
But designing this type of system?
Not so magical.
Let’s dive into how to design a Stripe-style usage-based billing system that’s flexible, scalable, and revenue-smart — without getting overwhelmed by edge cases.
Why Usage-Based Billing Is the Future of SaaS
✅ More revenue from power users
✅ Lower barrier for new users
✅ Aligns value with pricing
✅ Encourages fair use and adoption
Startups are moving to usage-based pricing models because it just makes sense — customers pay for what they use, not what they might use.
If you’re building a platform or API product, this is a must-have.
🧠 Core Concepts Before You Begin
To build this right, understand these key parts:
- Metered usage tracking
- Pricing rules per metric (e.g., \$0.01 per API call)
- Customer-level aggregation
- Invoicing cycle (monthly, real-time, etc.)
- Integrations with payment processors (like Stripe Billing)
🛠️ System Design Overview
Your system needs to:
- Track usage events in near real-time
- Store them efficiently (with timestamp, user ID, resource type, units)
- Aggregate usage by billing period
- Calculate charges based on defined rates
- Generate invoices and send to Stripe
Here’s a simplified example of how your usage event schema might look:
{
"user_id": "cus_123456",
"event_type": "api_call",
"units": 1,
"timestamp": "2025-07-21T09:34:21Z"
}
⏱️ Tracking Usage in Real-Time
You can use a queue like Kafka or RabbitMQ to push usage events.
Or go lean with event tracking via a REST API.
Here’s a minimal example in Node.js:
app.post('/track', async (req, res) => {
const { user_id, event_type, units } = req.body;
await db.collection('usage_events').insertOne({
user_id,
event_type,
units,
timestamp: new Date()
});
res.status(200).send({ success: true });
});
📊 Pricing Models: Flat, Tiered, Volume
Choose the pricing style that fits:
- Flat-rate: \$0.01 per call
- Tiered: First 1,000 free → next 9,000 at \$0.01 → above that \$0.005
- Volume-based: Entire usage priced based on total volume (like AWS)
Here’s a Tiered Billing logic sample in JavaScript:
function calculateCharge(units) {
if (units <= 1000) return 0;
else if (units <= 10000) return (units - 1000) * 0.01;
else return (9000 * 0.01) + ((units - 10000) * 0.005);
}
🔗 For deep dive: Stripe Tiered Pricing Docs
🧾 Aggregating Usage and Generating Invoices
Schedule a daily or hourly aggregation job:
cron: 0 0 * * * /aggregate-usage.sh
Use something like:
const usage = await db.collection('usage_events').aggregate([
{ $match: { user_id: 'cus_123456', event_type: 'api_call' } },
{ $group: { _id: null, total_units: { $sum: "$units" } } }
]);
🔐 Common Pitfalls (and How to Avoid Them)
- Double-charging users → Deduplicate events
- Clock drift between services → Always use UTC
- Overload on database → Batch inserts & async queues
- Delayed usage data → Consider usage finalization windows
⚙️ Technologies You Might Use
- Backend: Node.js / Python
- Queue: Kafka / RabbitMQ / Redis Streams
- DB: MongoDB / PostgreSQL
- Billing: Stripe
- Scheduler: Cron, Airflow, Temporal.io
🔗 Temporal is great for complex workflows: Temporal Billing Workflows
🧩 Bonus: What If I Don’t Want to Build This From Scratch?
There are SaaS tools that do this out-of-the-box:
- Stripe Billing (of course)
- Metronome
- Orb
- Keen.io for event-based analytics
Great if you're scaling and don’t want to maintain your own infra.
💬 Your Turn
Have you built a metered billing system before?
What tech stack did you use? What mistakes did you make?
👇 Let’s discuss in the comments.
And if you found this helpful…
🔔 Follow [DCT Technology] for more deep-dives on web dev, system design, and SaaS infrastructure.
#webdev #systemdesign #stripe #billing #saas #api #node #javascript #startups #programming #backend #infrastructure #payments #developer #dcttechnology
Top comments (0)