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.
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)
}
});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.
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;
});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.
const beforePeriod = await stripe.subscriptions.list({
limit: 100,
created: {
lt: Math.floor(startOfMonth.getTime() / 1000)
}
});
const subscriptionsAtStart = beforePeriod.data.length;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.
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%Handle pagination to get complete data
If you have more than 100 subscriptions, iterate through paginated results. Use starting_after to fetch subsequent pages.
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;
}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.
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;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.