5 min read

How to Track Net Revenue Retention in Stripe

You're tracking revenue, but you're missing the real story—how much existing customers are actually expanding versus churning. Net Revenue Retention tells you if your existing customer base is getting healthier or smaller. In Stripe, this means tying subscription events to cohorts and doing math on the data you already have.

Capture subscription events with webhooks

To calculate NRR, you need to know when customers upgrade, downgrade, or cancel. Stripe webhooks deliver these events in real time.

Set up a webhook endpoint for subscription events

Create an endpoint that listens to Stripe's customer.subscription.updated and customer.subscription.deleted events. In your Node.js server, use the Stripe SDK to verify the webhook signature and process the event.

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

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

  if (event.type === 'customer.subscription.updated') {
    const subscription = event.data.object;
    console.log(`Subscription ${subscription.id} updated for customer ${subscription.customer}`);
    // Store this event in your database
  }

  if (event.type === 'customer.subscription.deleted') {
    const subscription = event.data.object;
    console.log(`Customer ${subscription.customer} churned`);
  }

  res.json({received: true});
});
Webhook handler that catches subscription changes. Always verify the signature to prevent spoofing.

Store metadata on subscriptions to identify cohorts

When creating subscriptions, add metadata to tag the customer's cohort (sign-up month, plan type, company segment). This lets you later group customers by when they started.

javascript
const subscription = await stripe.subscriptions.create({
  customer: 'cus_123',
  items: [{price: 'price_monthly_pro'}],
  metadata: {
    cohort_month: '2024-01',
    plan_type: 'pro',
    company_segment: 'mid-market'
  }
});
Metadata attached at subscription creation lets you slice NRR by cohort later.
Watch out: Stripe webhooks can be delayed or duplicated. Always check if you've already processed an event before updating your database.

Query Stripe to calculate NRR by cohort

Once you have subscription events logged, query Stripe's API or your database to calculate NRR for each cohort.

Pull subscription history for a cohort using the API

Use the Stripe API to list all subscriptions with the cohort metadata, then filter by start date. The starting_after parameter helps you paginate through large result sets.

javascript
const subscriptions = await stripe.subscriptions.list({
  limit: 100,
  created: {
    gte: Math.floor(new Date('2024-01-01').getTime() / 1000),
    lt: Math.floor(new Date('2024-02-01').getTime() / 1000)
  }
});

const cohortRevenue = subscriptions.data.reduce((sum, sub) => {
  if (sub.status === 'active' || sub.status === 'past_due') {
    return sum + (sub.items.data[0].price.unit_amount / 100);
  }
  return sum;
}, 0);
Query subscriptions created in January 2024 and calculate their current MRR.

Calculate NRR by comparing MRR across time periods

NRR formula: (Ending MRR - Starting MRR + Churn) / Starting MRR × 100. Get the MRR at the start of a period, then again at the end. Separately track downgrades and cancellations as churn.

javascript
// Example: Calculate NRR for Jan 2024 cohort, measured in March
const startingMRR = 50000;  // Jan 1
const endingMRR = 55000;    // Mar 31
const churnValue = 5000;    // Lost to downgrades + cancellations

const nrr = ((endingMRR - startingMRR + churnValue) / startingMRR) * 100;
console.log(`NRR: ${nrr.toFixed(1)}%`); // Output: NRR: 120.0%
NRR > 100% means you gained more from expansion than you lost from churn. NRR < 100% means churn is outpacing growth.
Tip: Export Stripe data to a data warehouse if you need to run these calculations regularly. The API works fine for ad-hoc analysis, but batch queries get slow at scale.

Monitor NRR over time

Calculate NRR monthly and track it in a dashboard so you see trends.

Automate NRR calculation in a scheduled job

Run a cron job each month-end that queries Stripe subscriptions and writes NRR metrics to your database. Use the Stripe API with date filters to get only active subscriptions at the start and end of each period.

javascript
const schedule = require('node-schedule');
const stripe = require('stripe')('sk_live_...');

schedule.scheduleJob('0 2 1 * *', async () => {
  const lastMonthStart = new Date();
  lastMonthStart.setMonth(lastMonthStart.getMonth() - 1);
  lastMonthStart.setDate(1);
  lastMonthStart.setHours(0, 0, 0, 0);

  const subscriptions = await stripe.subscriptions.list({
    limit: 100,
    expand: ['data.items.data.price'],
    created: {gte: Math.floor(lastMonthStart.getTime() / 1000)}
  });

  // Calculate NRR and save to your database
  console.log(`Processed ${subscriptions.data.length} subscriptions`);
});
Schedule NRR calculation to run automatically each month.
Watch out: Don't include new customer acquisition in NRR—only measure existing customers. Filter subscriptions by creation date to define your cohort boundary.

Common Pitfalls

  • Mixing new customer MRR with existing customer MRR. NRR only measures retention and expansion of the base you started with—new sales are separate.
  • Forgetting to account for plan changes in the same subscription. A customer who upgrades doesn't churn—Stripe keeps the subscription ID the same, just updates the amount.
  • Not handling test mode subscriptions. Stripe separates test and live data. Make sure your queries filter to livemode: true if you're pulling production metrics.
  • Assuming Stripe's Reports feature calculates NRR for you. It doesn't. You have to export the data and do the math yourself, or build it in your dashboard.

Wrapping Up

You now have a way to measure if your existing customers are actually getting more valuable or leaving. NRR above 100% means you're winning at expansion, while below 100% means churn is outpacing growth. 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