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.
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`);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.
subscriptions.data.forEach(subscription => {
subscription.items.data.forEach(item => {
const price = item.price;
console.log(`Amount: $${price.unit_amount / 100}, Interval: ${price.recurring.interval}`);
});
});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.
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.
let monthlyAmount = amount * item.quantity;
if (subscription.discount && subscription.discount.coupon) {
const discountAmount = subscription.discount.coupon.amount_off / 100;
monthlyAmount -= discountAmount;
}
mrr += monthlyAmount;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.
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.
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.