How to Implement Usage Tracking and Limits

Jun 2, 2025

Ayush, Autumn Co-Founder

A guide on when to use usage limits vs usage pricing, how to set it up easily and connect to Stripe.

Most SaaS companies implement usage limits wrong, then spend months fixing billing disputes.

We've seen this pattern repeatedly at Autumn. Companies add usage tracking as an afterthought. They pick arbitrary limits. They forget about edge cases. Then customers complain about unexpected charges or locked accounts.

Here's how to implement usage tracking properly from the start.

Understanding Usage-Based Pricing

Usage-based pricing charges customers based on what they consume [2]. It sounds simple. The implementation rarely is.

When to Use Limits vs Pure Usage Charging

This is where most companies mess up. Not all usage should have limits.

Use limits when:

  • Users trigger discrete actions (API calls, email sends, report generations)

  • You want to encourage plan upgrades

  • Usage is unpredictable and spiky

Skip limits and charge pure usage when:

  • Resources run continuously in the background (cloud storage, compute hours)

  • Usage is predictable and steady

  • Limits would frustrate users without adding value

Cloud storage is the classic example. Nobody wants their files deleted because they hit a limit. Just charge per GB stored.

Setting Up Usage Tracking

First, identify what actually matters for your pricing model.

Common Pricing Axes

From our experience, these are the most common [1]:

  • API calls

  • Active users

  • Data processed

  • Storage consumed

  • Compute hours

  • Messages sent

Types of Limits

Hard limits: Service stops working

  • Good for preventing abuse

  • Bad for customer experience

Soft limits: Service continues, but triggers actions

  • Send warning emails

  • Auto-upgrade plans

  • Charge overages

Grace Periods

Give users time to react:

  • Send warning at 80% usage

  • Send alert at 100% usage

  • Give 24-48 hours before enforcing

Technical Implementation

This is where things get tricky. You need to reset usage counters at the right time.

Option 1: Cron Jobs

Simple but effective for most use cases:

// Run daily at midnight UTC
async function resetMonthlyUsage(): Promise<void> {
  const today = new Date().getDate();
  
  // Find all users whose billing period ended
  const users = await db.users.find({
    billingCycleDay: today
  });
  
  for (const user of users) {
    // Archive old usage data
    await archiveUsageData(user.id);
    
    // Reset counters
    await db.usageCounters.update(
      { userId: user.id },
      { $set: { currentPeriodUsage: {} } }
    );
    
    // Create new billing period record
    await createBillingPeriod(user.id);
  }
}
Option 2: Stripe Webhooks

Better for accuracy, more complex to implement:

import Stripe from 'stripe';
import { Request, Response } from 'express';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

app.post('/stripe/webhook', async (req: Request, res: Response) => {
  const payload = req.rawBody;
  const sig = req.headers['stripe-signature'] as string;
  
  let event: Stripe.Event;
  
  try {
    event = stripe.webhooks.constructEvent(
      payload,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err) {
    return res.status(400).send('Invalid payload');
  }
  
  if (event.type === 'invoice.created') {
    // New billing period started
    const invoice = event.data.object as Stripe.Invoice;
    const user = await getUserByStripeId(invoice.customer as string);
    
    // Reset usage for new period
    await resetUserUsage(user.id);
    
  } else if (event.type === 'invoice.payment_succeeded') {
    // Log successful payment
    const invoice = event.data.object as Stripe.Invoice;
    await recordPayment(invoice);
  }
  
  res.status(200).send('Success');
});
Edge Cases to Handle

These will bite you in production:

  • Timezone mismatches between your system and Stripe

  • Users who upgrade mid-cycle

  • Refunds and credits

  • Failed payments that succeed later

  • Customers in different timezones

Integration with Billing Systems

Connecting usage data to billing is where most implementations fail [3].

Real-time vs Batch Processing

Real-time:

  • Update Stripe immediately on each usage event

  • Good for critical limits

  • Can overwhelm Stripe's rate limits

Batch processing:

  • Aggregate usage hourly/daily

  • More efficient

  • Slight delay in enforcement

Most companies use batch processing with real-time checks for critical limits.

Using Autumn

Autumn handles the Stripe integration complexity in 2 functions. Just check if a user has access to something, then track their usage.

// Check if user can perform action
if (await autumn.check({featureId: 'api_calls'})) {
  // Perform action
  await processApiCall();
  autumn.track({featureId: 'api_calls'})
} else {
  // Handle limit exceeded
  return res.status(429).json({ error: 'Usage limit exceeded' });
}

Common Pitfalls

We've seen these mistakes repeatedly:

Not handling concurrent requests: Two API calls at the exact limit both succeed, going over the limit.

Forgetting about timezones: User's billing resets at midnight their time, but your cron job runs at midnight UTC.

No grace period: User hits limit at 2 AM, wakes up to a broken service.

Incorrect aggregation: Counting usage wrong, leading to billing disputes.

No usage history: Can't debug billing issues or show users their historical usage.

Conclusion

Usage tracking seems simple until you implement it. The technical details matter. Start with clear rules about what to track and when to enforce limits. Build proper reset mechanisms. Handle edge cases before they hit production.

Most importantly, remember that usage limits should improve the user experience, not frustrate users. If a limit doesn't encourage upgrades or prevent abuse, you probably don't need it.

Autumn handles most of this complexity for you. But whether you build or buy, get the fundamentals right.

Citations

[1] https://www.zenskar.com/blog/implementing-usage-based-pricing
[2] https://www.maxio.com/blog/benefits-of-a-usage-based-pricing-model
[3] https://bizbot.com/blog/how-to-implement-usage-based-billing-step-by-step-guide/