7 min read

How to Report Usage to StripeHow to implement robust usage reporting with exactly once guarantees

Peter Marton
Peter Marton@slashdotpeter
cover

Usage-based pricing is gaining traction among SaaS products. By charging customers based on their consumption, you can offer them greater control over their expenses while simplifying customer lifecycle management through organic growth. To adopt usage-based billing with Stripe, you must meter your customer's usage and report it on their Stripe subscription.

Reporting usage to Stripe comes with its own challenges around rate limits, errors, and exactly once delivery. This blog post will walk you through the steps to build robust usage reporting with Stripe, perfect for engineers tasked with integrating Stripe metered billing.

If you're starting from scratch with usage-based billing on Stripe, check out our in-depth article on implementing Usage-Based Pricing with Stripe. In it, we cover everything you need to know about structuring your pricing and packaging models in Stripe and how to implement billing effectively.

Usage Reporting Challenges

Reporting usage to Stripe's API comes with its own set of challenges:

  1. Workaround Stripe API Rate Limits
  2. Handle billing cycles and minimize usage drift into the next billing cycle
  3. Deliver usage exactly once to avoid under and over-billing
  4. Stripe requires to report usage on Subscription Items

Let's tackle these challenges one by one.

1. Stripe Rate Limits

Stripe APIs come with rate limits that require companies to meter their customers usage separately and only report the aggregated value to Stripe. When writing this article, Stripe APIs had a 100-write-per-second rate limit. It's easy to see how, even with 100 customers, you can't report every single usage into Stripe, and you need to use a separate usage metering solution.

Rate limits are particularly problematic for AI and DevOps companies that bill based on high-frequency events like API calls or token consumption, which can happen many times per second for a single customer. To ensure usage is always reported even when the rate limit is hit, the reporting logic must retry failed usage reporting attempts and avoid double reporting.

If you need help with accurate usage metering, check out the open-source OpenMeter on GitHub.

2. Handle billing cycles

Stripe's billing cycles can start and end at any time and are not necessarily aligned with calendar days or months. This means a subscription can switch to the next billing cycle anytime during the day, depending on its billing cycle anchor.

{
  "id": "sub_1NeiUbLvyihio9p576GGh6Y9",
  "object": "subscription",
  "billing_cycle_anchor": 1693594862,     // 2023-09-01T19:01:02.000Z
  "current_period_start": 1693594862,     // 2023-09-01T19:01:02.000Z
  "current_period_end": 1696186862,       // 2023-10-01T19:01:02.000Z
  ...
}

Since Stripe only allows usage reporting for the current active billing cycle, continuous usage reporting becomes mandatory with metered billing. For example, we can report usage hourly or daily to Stripe. This also means some usage will drift into the next billing cycle. This can happen, for example, when you report usage daily at midnight, but the customer's billing cycle ends during the day. In this case, the most recent day's usage will be reported on the new billing cycle and billed only in the next cycle. This can be unclear to your customers when they analyze their bills.

To reduce usage slipping into the next billing cycle, some companies choose to report usage frequently, like hourly. Although reporting usage more frequently reduces usage drift, it doesn't fully eliminate some usage slipping in the next billing cycle. Also you can only report usage as frequently as Stripe's rate limit allows you to do, which can become more and more challenging with a growing customer base.

3. Deliver Usage Exactly Once

As discussed above, most companies meter usage separately and report only the aggregated value to Stripe. This is necessary to avoid rate limit challenges and to have flexibility around the source of truth of usage data. It's easy to see how under or over-reporting usage to Stripe from our metering service would lead to inaccurate customer charges. We also discussed how we need to report usage to Stripe periodically to report usage on the current active billing cycle and reduce usage drift between billing cycles.

Combining the goal of reporting usage frequently while doing it exactly once can be challenging. For example, if you decide to write usage daily, you need to ensure that the daily report for each customer succeeds and is retried in the case of an error or hitting the rate limit but not reported twice on the same day. A robust usage reporting with exactly-once delivery guarantees requires retry logic with a state machine.

4. Reporting Usage on Subscription Items

Stripe is required to report usage on its Subscription Item entity. You can think about SubscriptionItem as a product-price pair in the subscription. As a single Stripe customer can have multiple subscriptions and a single subscription can have multiple Subscription Items, it's important to ensure we report usage on the right entity. Not every SubscriptionItem is metered, and different items usually belong to different metes; it requires some mapping in your application at usage reporting.

A common solution is to map meters to SubscriptionItem(s) based on the corresponding product. So you can retrieve active subscriptions for the given customer from Stripe and find SubscriptionItems in the Subscription referencing metered prices.

Robust Usage Reporting

In the previous sections, we discussed that robust usage reporting:

  • Delivery usage once and exactly once to Stripe
  • Reports frequently to minimize usage drift between billing cycles
  • Retries in the case of errors or rate limits
  • Maps meters to SubscriptionItems

Let's discuss how we can implement robust usage reporting that fulfills these requirements. First, we must ensure we retry reporting for errors and rate limits. A great way to do this is to send the usage data to a message queue first. Whenever we fail to report the usage to Stripe, we return the message to the queue for further processing. Some distributed messaging queues like AWS SQS only guarantee at least one delivery, so we still need to ensure we don't double-report usage. Luckily, when we report usage to Stripe, we can report usage on the same timestamp with action=set, which overrides the already reported usage and avoids double reporting.

await stripe.subscriptionItems.createUsageRecord(
  subscriptionItemID,
  {
    quantity: usageQuantity, // usage to report
    timestamp: timestamp, // has to be within the current billing period
    action: 'set', // set or override
  },
  {
    idempotencyKey,
  },
);

As with Stripe you need to report usage on the SubscriptionItem entity, you need to map meters to subscription items. You can do this by mapping meters to price IDs and finding the corresponding subscription items in the customer's subscriptions.

Finally, we need to fill the queue with usage periodically according to our reporting frequency; we can do this with a CRON job. For example, if we want to report usage every hour, we can collect usage per customer for the last hour when a job runs and send it to the queue. This job can also fail, especially if it has dependencies to retrieve customers and their usage, so we must ensure that this periodic job always succeeds. One way to ensure it is to use a workflow management software such as Temporal and retry failed jobs.

Implementing Stripe usage reporting may seem challenging, particularly for engineering teams who wish to focus on their core product. OpenMeter Cloud can alleviate this burden by metering usage and reporting aggregated values to Stripe with exactly-once delivery guarantees.

Implementing usage-based billing?