5 min read

How to Calculate Churn Rate in Stripe

Subscription churn matters because losing customers quietly is worse than losing them loudly. Stripe tracks all subscription cancellations, but the data doesn't automatically tell you your churn rate—you need to query it yourself.

Pull subscription data from the Stripe API

Your churn rate calculation depends on three numbers: subscriptions created in your period, subscriptions canceled during that period, and the total that existed at period start.

Query subscriptions created in your period

Use the Stripe API to list subscriptions created during your measurement window. This gives you a cohort to analyze.

javascript
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

const startOfMonth = new Date(2026, 2, 1);
const endOfMonth = new Date(2026, 2, 31);

const subscriptionsInPeriod = await stripe.subscriptions.list({
  limit: 100,
  created: {
    gte: Math.floor(startOfMonth.getTime() / 1000),
    lte: Math.floor(endOfMonth.getTime() / 1000)
  }
});
Use Unix timestamps to query subscriptions by creation date

Get all canceled subscriptions in your period

List subscriptions with status: 'canceled' and filter by canceled_at to isolate cancellations that happened during your measurement window.

javascript
const allCanceled = await stripe.subscriptions.list({
  status: 'canceled',
  limit: 100
});

const canceledThisMonth = allCanceled.data.filter(sub => {
  const canceledAt = new Date(sub.canceled_at * 1000);
  return canceledAt >= startOfMonth && canceledAt <= endOfMonth;
});
Filter canceled subscriptions by the canceled_at timestamp

Count subscriptions that existed at period start

Get your denominator by counting subscriptions that existed before your period started. This includes both active and already-canceled subscriptions.

javascript
const beforePeriod = await stripe.subscriptions.list({
  limit: 100,
  created: {
    lt: Math.floor(startOfMonth.getTime() / 1000)
  }
});

const subscriptionsAtStart = beforePeriod.data.length;
Query subscriptions created before the period to get your starting count
Watch out: Pagination. The Stripe API returns 100 items max. Use has_more and loop through all pages when calculating real churn.

Calculate your churn rate

Once you have your numbers, the math is straightforward. Then filter for production subscriptions to get your real business churn.

Apply the churn formula

Divide the number of canceled subscriptions by the subscriptions that existed at the start of your period, then multiply by 100 for a percentage.

javascript
const churnRate = (canceledThisMonth.length / subscriptionsAtStart) * 100;

console.log(`Churn rate for March: ${churnRate.toFixed(2)}%`);

// Example: If you had 200 subscriptions at start and 10 canceled, churn = 5%
Churn rate = (canceled / starting) × 100

Handle pagination to get complete data

If you have more than 100 subscriptions, iterate through paginated results. Use starting_after to fetch subsequent pages.

javascript
async function getAllCanceledSubscriptions(startDate, endDate) {
  let allCanceled = [];
  let hasMore = true;
  let lastId = null;

  while (hasMore) {
    const params = {
      status: 'canceled',
      limit: 100
    };

    if (lastId) params.starting_after = lastId;

    const response = await stripe.subscriptions.list(params);
    const thisMonth = response.data.filter(sub => {
      const canceledAt = new Date(sub.canceled_at * 1000);
      return canceledAt >= startDate && canceledAt <= endDate;
    });
    
    allCanceled = allCanceled.concat(thisMonth);
    hasMore = response.has_more;
    lastId = response.data[response.data.length - 1]?.id;
  }

  return allCanceled;
}
Paginate through all canceled subscriptions to avoid undercounting

Filter for production subscriptions only

Exclude test mode subscriptions with livemode: false. Also exclude trial cancellations—customers who canceled before their trial ended didn't generate revenue.

javascript
const paidChurn = canceledThisMonth.filter(sub => {
  // Exclude test mode
  if (sub.livemode === false) return false;

  // Exclude cancellations during free trial
  if (sub.trial_end && sub.canceled_at < sub.trial_end) {
    return false;
  }

  // Exclude zero-amount subscriptions
  const price = sub.items.data[0]?.price;
  if (price && price.unit_amount === 0) {
    return false;
  }

  return true;
});

const paidChurnRate = (paidChurn.length / subscriptionsAtStart) * 100;
Filter for paid, production subscriptions
Tip: Automate this monthly. Write a scheduled job that calculates churn and stores results so you can spot trends over time.

Common Pitfalls

  • Forgetting to paginate—if you stop at 100 results, you'll undercount cancellations
  • Including free trial cancellations—customers who never paid shouldn't count as revenue churn
  • Mixing test and production data—test subscriptions skew your actual metrics
  • Using the wrong denominator—measure churn against customers who *could* have churned, not total signups

Wrapping Up

You now have the framework to calculate subscription churn from Stripe. The key: churn is a leading indicator. High churn means growth has to run harder. If you want to track this automatically across all your tools and correlate it with product changes, Product Analyst can help.

Track these metrics automatically

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

Try Product Analyst — Free