6 min read

What Is Net Revenue Retention in Stripe

Net Revenue Retention (NRR) tells you whether your SaaS business is growing faster than it's shrinking. In Stripe, you're working with subscription data scattered across the API — beginning MRR, expansion from upgrades, and contraction from downgrades and cancellations. You need a way to pull all this together and understand your actual customer growth.

Understanding Net Revenue Retention

NRR measures revenue growth from your existing customer base by accounting for upgrades, downgrades, and cancellations.

Learn the NRR formula and why it matters

NRR is calculated as: (Beginning MRR + Expansion MRR - Churn MRR) / Beginning MRR × 100. In Stripe terms: Beginning MRR is your total recurring revenue at the start of a period. Expansion MRR is revenue from upgrades and plan increases. Churn MRR is revenue lost from downgrades and cancellations. A healthy SaaS typically targets 120%+ NRR.

javascript
const stripe = require('stripe')('sk_live_...');

// Fetch a subscription to see the MRR structure
const sub = await stripe.subscriptions.retrieve('sub_123456');
const mrr = (sub.items.data[0].price.recurring.amount) / 100;

console.log(`Monthly recurring revenue: $${mrr}`);

// Apply NRR formula with example values
const beginningMRR = 50000;  // MRR at start of month
const expansionMRR = 5000;   // Revenue from upgrades
const churnMRR = 2000;       // Revenue lost to cancellations

const nrr = ((beginningMRR + expansionMRR - churnMRR) / beginningMRR) * 100;
console.log(`NRR: ${nrr.toFixed(1)}%`); // Output: 106.0%
NRR above 100% means your existing customers generate more revenue than before

Identify the three revenue components

Every dollar of NRR comes from subscriptions that: (1) stayed at the same price (baseline), (2) upgraded or increased (expansion), or (3) downgraded, paused, or canceled (churn). You'll pull these from the subscriptions API by filtering on status and examining the items array for price changes.

Calculating NRR From Stripe Data

To calculate NRR, query Stripe's Subscription API for active and canceled subscriptions in your period, then segment by MRR movement.

Fetch all subscriptions in your measurement period

Use the Stripe Node SDK to list subscriptions, filtering by creation date and including both active and canceled subscriptions to capture churn. Set limit: 100 and paginate through results if you have more than 100 subscriptions.

javascript
const stripe = require('stripe')('sk_live_...');

const subscriptions = await stripe.subscriptions.list({
  limit: 100,
  expand: ['data.customer'],
  created: {
    gte: Math.floor(new Date('2024-02-01').getTime() / 1000),
    lte: Math.floor(new Date('2024-02-29').getTime() / 1000)
  },
  status: 'all' // Include active and canceled
});

console.log(`Found ${subscriptions.data.length} subscriptions`);
subscriptions.data.forEach(sub => {
  console.log(`${sub.id}: Status ${sub.status}, MRR $${(sub.items.data[0]?.price?.recurring?.amount || 0) / 100}`);
});
Use `status: 'all'` to capture both active and canceled subscriptions created during your period

Segment subscriptions by MRR movement

For each subscription, determine whether it represents baseline revenue, expansion, or churn. Baseline = subscriptions that remained at the same price. Expansion = subscriptions that upgraded or increased. Churn = subscriptions canceled or downgraded. Use the status field and the items array to see plan details.

Calculate total MRR for each component and apply the formula

Sum the MRR for baseline, expansion, and churn by iterating through your segmented subscriptions. The key is to use consistent measurement dates — always snapshot on the same calendar day each month so billing cycles don't distort your numbers.

javascript
const calculateNRR = (subscriptions) => {
  let beginningMRR = 0;
  let expansionMRR = 0;
  let churnMRR = 0;
  
  subscriptions.forEach(sub => {
    const mrr = (sub.items.data[0]?.price?.recurring?.amount || 0) / 100;
    
    if (sub.status === 'canceled') {
      churnMRR += mrr;
    } else if (sub.metadata?.plan_change === 'upgrade') {
      expansionMRR += mrr;
    } else if (sub.status === 'active') {
      beginningMRR += mrr;
    }
  });
  
  const nrr = ((beginningMRR + expansionMRR - churnMRR) / beginningMRR) * 100;
  
  return {
    beginningMRR: beginningMRR.toFixed(2),
    expansionMRR: expansionMRR.toFixed(2),
    churnMRR: churnMRR.toFixed(2),
    nrr: nrr.toFixed(1) + '%'
  };
};

const metrics = calculateNRR(subscriptions.data);
console.log('Monthly NRR Metrics:', metrics);
Store plan changes in `metadata` or compare subscription snapshots to identify expansions vs. baseline
Watch out: Stripe's current_period_start changes with each billing cycle. To measure NRR accurately month-to-month, snapshot your data on the same calendar day each month (e.g., the 1st), not relative to billing cycle dates.

Common Mistakes to Avoid

A few things trip people up when calculating NRR from Stripe.

Don't include new customer subscriptions in expansion

NRR only counts revenue movement from existing customers. If you signed 10 new customers this month, that's new ARR, not expansion. In your calculation, only count subscriptions created before your measurement period started. Use the created_at field to filter.

javascript
// Separate new from existing subscriptions
const measurementStart = Math.floor(new Date('2024-02-01').getTime() / 1000);

const existingSubscriptions = subscriptions.data.filter(sub => sub.created < measurementStart);
const newSubscriptions = subscriptions.data.filter(sub => sub.created >= measurementStart);

console.log(`Existing customers (for NRR): ${existingSubscriptions.length}`);
console.log(`New customers (for ARR): ${newSubscriptions.length}`);

// NRR calculation uses only existingSubscriptions
const nrrMetrics = calculateNRR(existingSubscriptions);
NRR ignores new business entirely — it only measures how much revenue you're retaining and expanding

Account for billing cycle misalignment

A subscription with a March 15 billing date will have partial March and partial April in its current period. For monthly NRR, use a consistent snapshot date (e.g., always measure on the 1st at midnight UTC) and calculate MRR as of that single point in time.

Common Pitfalls

  • Confusing NRR (existing customer growth) with ARR growth (which includes new customers). NRR only measures how existing revenue changes.
  • Not accounting for billing cycle misalignment. Stripe's current_period dates don't align with calendar months, so snapshot on the same calendar date each month.
  • Including downgrades as negative expansion instead of churn. Any plan reduction is churn MRR, not negative expansion.
  • Forgetting to exclude new subscriptions created in the current month. NRR is about existing customer retention and growth, not new business.

Wrapping Up

NRR in Stripe is built from three components: the MRR you start with, the revenue you gain from upgrades, and the revenue you lose from downgrades and cancellations. Query the subscriptions API, segment by movement type, and apply the formula on a consistent calendar date each month. 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