6 min read

How to Calculate Expansion Revenue in Stripe

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.

javascript
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}`);
  });
});
List customers with their subscriptions to see the full billing picture

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.

javascript
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`);
Tip: Use 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.

javascript
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);
Sum invoice amounts by month to see revenue per customer over time

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.

javascript
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)}`);
Watch out: Don't count refunds as negative expansion—they're churn recovery or billing corrections, not downgrades. Filter for refunds separately using 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.

javascript
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.

javascript
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)}%`);
Tip: Set a baseline date—January 1st or the start of your fiscal year. All comparisons should be against the same anchor so month-over-month changes are meaningful.

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: active subscriptions.
  • 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.lines to 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.

Track these metrics automatically

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

Try Product Analyst — Free