6 min read

How to Track MRR in Stripe

Your subscription revenue is scattered across Stripe's dashboard—active subscriptions, pending invoices, and trial periods. MRR (Monthly Recurring Revenue) rolls all of that into one number that actually matters: how much predictable revenue hits your account each month. Stripe gives you the raw data; you just need to query it the right way.

Fetch Active Subscriptions from Stripe

MRR is the sum of all active subscription amounts normalized to a monthly rate. Start by pulling your subscription data from the Subscriptions API.

Query all active subscriptions

Use stripe.subscriptions.list() with status: 'active' to get every active subscription. The API returns paginated results, so handle pagination if you have more than 100 subscriptions.

javascript
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

const subscriptions = await stripe.subscriptions.list({
  status: 'active',
  limit: 100,
});

console.log(`Found ${subscriptions.data.length} active subscriptions`);
status: 'active' excludes canceled, past_due, and unpaid subscriptions.

Access subscription items and pricing

Each subscription contains items.data[]—an array of line items. Most SaaS businesses have one item per subscription. The price object contains the amount and billing interval.

javascript
subscriptions.data.forEach(subscription => {
  subscription.items.data.forEach(item => {
    const price = item.price;
    console.log(`Amount: $${price.unit_amount / 100}, Interval: ${price.recurring.interval}`);
  });
});
Watch out: subscriptions with auto_advance: false are still 'active' but don't renew automatically. Exclude them if they're not real revenue.

Calculate MRR by Normalizing Billing Intervals

Not all subscriptions bill monthly. Some are annual, others weekly. Convert everything to a monthly amount to get true MRR.

Normalize intervals to monthly equivalent

Divide annual plans by 12, weekly by 4.33, daily by 30.42. Handle interval: 'month' directly. Skip subscriptions without an active price.

javascript
let mrr = 0;

subscriptions.data.forEach(subscription => {
  const item = subscription.items.data[0];
  if (!item || !item.price.recurring) return;

  const amount = item.price.unit_amount / 100;
  const interval = item.price.recurring.interval;

  if (interval === 'month') {
    mrr += amount;
  } else if (interval === 'year') {
    mrr += amount / 12;
  } else if (interval === 'week') {
    mrr += amount * 4.33;
  } else if (interval === 'day') {
    mrr += amount * 30.42;
  }
});

console.log(`Total MRR: $${mrr.toFixed(2)}`);

Account for quantity and subscription-level discounts

Multiply unit_amount by item.quantity for multi-seat plans. For subscription discounts, check subscription.discount and subtract its amount.

javascript
let monthlyAmount = amount * item.quantity;

if (subscription.discount && subscription.discount.coupon) {
  const discountAmount = subscription.discount.coupon.amount_off / 100;
  monthlyAmount -= discountAmount;
}

mrr += monthlyAmount;
Tip: Trial subscriptions have trial_end set to a future date. Exclude them entirely—they generate no revenue until the trial ends.

Keep MRR Fresh with Webhooks

Query MRR once and it's stale within hours. Use Stripe webhooks to update MRR every time a subscription changes.

Listen for subscription change events

Set up a webhook endpoint that listens for customer.subscription.created, customer.subscription.updated, and customer.subscription.deleted. Stripe POSTs to your endpoint whenever these events occur.

javascript
const express = require('express');
const app = express();

app.post('/webhooks/stripe', express.raw({type: 'application/json'}), (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET
    );
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  if (['customer.subscription.created', 'customer.subscription.updated', 'customer.subscription.deleted'].includes(event.type)) {
    updateMRR(); // Recalculate MRR
  }

  res.json({received: true});
});

Register your webhook in Stripe Dashboard

Go to Developers > Webhooks > Add endpoint. Paste your HTTPS endpoint URL and select the three subscription events. Save the signing secret as STRIPE_WEBHOOK_SECRET.

Watch out: webhooks may not arrive in order. A cancellation could come before an update. Always timestamp your MRR calculations and skip old events.

Common Pitfalls

  • Forgetting to normalize billing intervals. A $1,200 annual plan should add $100 to MRR, not $1,200.
  • Including trial subscriptions. They generate no revenue until the trial ends. Filter them out completely.
  • Ignoring discounts and coupons. A $100/month plan with a $10 coupon is $90 MRR, not $100.
  • Skipping pagination. Stripe returns max 100 subscriptions per request. If you have 200, you'll miss half your revenue.

Wrapping Up

You now have a real-time MRR number by querying Stripe's subscriptions, normalizing intervals, and keeping it fresh with webhooks. This is the foundation of SaaS metrics—knowing your baseline recurring revenue. If you want to track this automatically across tools, Product Analyst can help.

Track these metrics automatically

Product Analyst connects to your stack and surfaces the insights that matter.

Try Product Analyst — Free