You need to know if your existing customers are generating more revenue over time—that's expansion revenue. In Stripe, expansion happens when customers upgrade plans, add more seats, or increase usage-based billing. We'll show you how to extract this data from your subscriptions and calculate the real lift from your installed base.
Extract subscription data by cohort
The foundation of expansion revenue is seeing what each customer paid in period A versus period B. You'll list subscriptions and filter by customer creation date.
List all customers and their subscription start dates
Use Stripe Dashboard > Customers or query the API to get a full picture of your customer cohorts. Include expand: ['data.subscriptions'] to get subscription details inline.
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const customers = await stripe.customers.list({
limit: 100,
expand: ['data.subscriptions']
});
customers.data.forEach(customer => {
console.log(`Customer: ${customer.id}, Created: ${new Date(customer.created * 1000)}`);
customer.subscriptions.data.forEach(sub => {
console.log(` Subscription: ${sub.id}, Amount: ${sub.items.data[0].price.unit_amount / 100}`);
});
});Filter for active subscriptions in a time period
You need subscriptions that were active during both your comparison periods. Check the subscription's created timestamp and current status to exclude churned customers.
const activeSubscriptions = await stripe.subscriptions.list({
status: 'active',
created: {
gte: Math.floor(new Date('2025-01-01').getTime() / 1000),
lte: Math.floor(new Date('2025-12-31').getTime() / 1000)
},
limit: 100,
expand: ['data.customer', 'data.items.data.price']
});
const paidSubscriptions = activeSubscriptions.data.filter(
sub => sub.customer.created * 1000 < new Date('2025-01-01').getTime()
);
console.log(`Found ${paidSubscriptions.length} eligible subscriptions`);expand: ['data.items.data.price'] to fetch price details without extra API calls. This saves quota on large customer bases.Calculate MRR change per customer
Expansion revenue is the delta between what a customer paid last month versus this month. You need invoices to see the actual revenue flow.
Get invoices for a customer to see payment history
Pull invoices grouped by month to compare what each customer paid in consecutive periods. Use created filters and status: paid to only count revenue that actually landed.
const invoicesByMonth = {};
const invoices = await stripe.invoices.list({
customer: 'cus_xxxxx',
status: 'paid',
limit: 100
});
invoices.data.forEach(invoice => {
const month = new Date(invoice.created * 1000).toISOString().substring(0, 7);
if (!invoicesByMonth[month]) invoicesByMonth[month] = 0;
invoicesByMonth[month] += invoice.total / 100;
});
console.log('MRR by month:', invoicesByMonth);Compare MRR between periods to identify expansion
Calculate the difference: if a customer paid $100 in January and $150 in February, that's $50 of expansion revenue. Only count increases—don't count downgrades as negative expansion.
const months = Object.keys(invoicesByMonth).sort();
let totalExpansion = 0;
for (let i = 1; i < months.length; i++) {
const prev = months[i - 1];
const current = months[i];
const delta = invoicesByMonth[current] - invoicesByMonth[prev];
if (delta > 0) {
totalExpansion += delta;
console.log(`${current}: +$${delta.toFixed(2)} expansion`);
}
}
console.log(`Total expansion for customer: $${totalExpansion.toFixed(2)}`);type: refund.Aggregate expansion revenue across your customer base
Once you've calculated expansion per customer, roll it up to see total expansion revenue as a business metric.
Sum expansion revenue from all customers
Loop through all customers from your cohort, calculate their individual expansion, and add them together. This gives you a single number: total expansion revenue.
let totalExpansion = 0;
let expansionCustomerCount = 0;
const customers = await stripe.customers.list({ limit: 100 });
for (const customer of customers.data) {
const invoices = await stripe.invoices.list({
customer: customer.id,
status: 'paid',
limit: 100
});
const invoicesByMonth = {};
invoices.data.forEach(invoice => {
const month = new Date(invoice.created * 1000).toISOString().substring(0, 7);
invoicesByMonth[month] = (invoicesByMonth[month] || 0) + (invoice.total / 100);
});
const months = Object.keys(invoicesByMonth).sort();
for (let i = 1; i < months.length; i++) {
const delta = invoicesByMonth[months[i]] - invoicesByMonth[months[i - 1]];
if (delta > 0) {
totalExpansion += delta;
expansionCustomerCount++;
}
}
}
console.log(`Total expansion: $${totalExpansion.toFixed(2)}`);
console.log(`Customers with expansion: ${expansionCustomerCount}`);Calculate expansion as a percentage of total revenue
Divide expansion revenue by your total MRR to see expansion as a percentage of total revenue. This benchmarks your growth efficiency—companies typically target 10–30% expansion as a percentage of total revenue.
const allInvoices = await stripe.invoices.list({
status: 'paid',
created: {
gte: Math.floor(new Date('2025-02-01').getTime() / 1000),
lte: Math.floor(new Date('2025-02-28').getTime() / 1000)
},
limit: 100
});
const totalRevenue = allInvoices.data.reduce((sum, inv) => sum + (inv.total / 100), 0);
const expansionPercentage = (totalExpansion / totalRevenue) * 100;
console.log(`Total MRR: $${totalRevenue.toFixed(2)}`);
console.log(`Expansion revenue: $${totalExpansion.toFixed(2)}`);
console.log(`Expansion as % of revenue: ${expansionPercentage.toFixed(1)}%`);Common Pitfalls
- Including new customers in expansion revenue. New logos are separate from expansion. Filter for customers created before your measurement period starts.
- Counting subscription cancellations as expansion. A canceled subscription with a higher amount is churn, not expansion. Only count
status: activesubscriptions. - Ignoring usage-based charges and add-ons. If you bill by API calls or seats, expansion appears in line items beyond the base subscription price. Use
invoice.linesto get the full picture. - Not accounting for proration and billing cycle timing. Stripe prorates partial months. Compare full billing cycles or normalize for cycle length to avoid noise.
Wrapping Up
Expansion revenue shows you're getting more value from your existing customer base. The core method is comparing what customers paid in one period versus the next—Stripe's invoices and subscriptions give you everything you need. If you want to track this automatically across tools and build dashboards, Product Analyst can help.