If you're running a subscription business on Stripe, you need to know which customers are actually profitable. Lifetime value (LTV) tells you how much revenue you'll make from a customer over their lifetime — it's the metric that drives retention decisions. Stripe gives you the raw data; we'll show you how to calculate it.
Pull Customer and Subscription Data from Stripe
LTV starts with gathering your customer revenue history. Stripe stores everything — subscriptions, invoices, charges — but you need to fetch it in the right order.
List all customers and their subscriptions
Start by retrieving your customers from Stripe. Use the Customers API endpoint to list all customers, then fetch their active and canceled subscriptions. This gives you a complete view of who paid you and when.
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
// Fetch all customers with pagination
const customers = await stripe.customers.list({
limit: 100
});
// For each customer, get their subscriptions
for (const customer of customers.data) {
const subscriptions = await stripe.subscriptions.list({
customer: customer.id,
status: 'all', // Include active and canceled subscriptions
limit: 100
});
console.log(`Customer ${customer.id} has ${subscriptions.data.length} subscriptions`);
}Retrieve invoices and charges for revenue calculation
Subscriptions tell you about recurring revenue, but you also need invoices and charges. Invoices capture what each customer owed; charges show what actually settled. Use the Invoices API to get the complete payment history per customer.
// Get all paid invoices for a customer
const invoices = await stripe.invoices.list({
customer: customer.id,
status: 'paid',
limit: 100
});
let totalRevenue = 0;
invoices.data.forEach(invoice => {
// Stripe returns amounts in cents
totalRevenue += invoice.amount_paid;
});
const totalRevenueInDollars = totalRevenue / 100;
console.log(`Total revenue from customer: $${totalRevenueInDollars.toFixed(2)}`);Account for one-time charges outside of subscriptions
Some customers might have one-time charges or add-ons. Query the Charges API separately to capture revenue that isn't tied to a subscription. Filter for succeeded charges only.
// Get one-time charges for this customer
const charges = await stripe.charges.list({
customer: customer.id,
status: 'succeeded',
limit: 100
});
let oneTimeRevenue = 0;
charges.data.forEach(charge => {
// Skip charges that were already counted in invoices
if (!charge.invoice) {
oneTimeRevenue += charge.amount;
}
});
const finalLTV = totalRevenueInDollars + (oneTimeRevenue / 100);
console.log(`Final LTV for customer: $${finalLTV.toFixed(2)}`);charge.invoice == null for standalone charges only.Calculate Churn and Adjust LTV Over Time
Raw revenue isn't LTV — you need to factor in how long customers stick around. A customer who paid $100 and canceled next month is worth less than one who paid $100 and stayed for a year.
Track subscription cancellation dates
For past subscriptions, look at the canceled_at timestamp. Subscriptions without canceled_at are still active. Use this to calculate how long each customer relationship lasted.
let totalMonthsActive = 0;
subscriptions.data.forEach(sub => {
const startDate = new Date(sub.created * 1000);
const endDate = sub.canceled_at
? new Date(sub.canceled_at * 1000)
: new Date();
const monthsActive = (endDate - startDate) / (1000 * 60 * 60 * 24 * 30);
totalMonthsActive += monthsActive;
console.log(`Subscription lasted ${monthsActive.toFixed(1)} months`);
});
const averageMonthsActive = totalMonthsActive / subscriptions.data.length;Calculate monthly recurring revenue and project forward
Divide total revenue by months active to get a monthly value. Then project forward — if a customer lasted 12 months at $100/month, their actual LTV was $1,200. For active customers, estimate how long they'll stay using your churn rate.
const monthlyRecurringRevenue = totalRevenueInDollars / averageMonthsActive;
// Project LTV assuming customer stays for 24 months
const projectedLTV24mo = monthlyRecurringRevenue * 24;
// Or use a churn-based estimate: assume 20% annual churn
const annualChurnRate = 0.20;
const retentionRate = 1 - annualChurnRate;
const conservativeLTV = monthlyRecurringRevenue * (12 / Math.log(retentionRate));
console.log(`Projected LTV (24 months): $${projectedLTV24mo.toFixed(2)}`);
console.log(`Conservative LTV (20% churn): $${conservativeLTV.toFixed(2)}`);Store LTV in your database and update via webhooks
Stripe API calls are slow and rate-limited. Calculate LTV once, store it in your database, then update nightly or when webhooks fire. Use customer.subscription.updated and customer.subscription.deleted events to keep LTV fresh without hammering the API.
// Store LTV in your database
await db.customers.update({
stripe_customer_id: customer.id,
ltv: projectedLTV24mo,
lifetime_months: averageMonthsActive,
mrr: monthlyRecurringRevenue,
updated_at: new Date()
});
// Listen for subscription changes
const event = req.body;
if (event.type === 'customer.subscription.deleted') {
const subscription = event.data.object;
// Recalculate LTV for this customer asynchronously
await queue.add('recalculate-ltv', { customerId: subscription.customer });
}Common Pitfalls
- Forgetting to divide by 100 when Stripe returns amounts in cents. $100 USD is 10000 in the API.
- Double-counting revenue by including charges that are already tied to invoices. Always check
charge.invoicebefore adding. - Using only active subscriptions and ignoring canceled ones. This inflates your LTV because you're missing churn data.
- Calling Stripe API in a loop for thousands of customers. Use list pagination and webhooks to update your database instead.
Wrapping Up
LTV in Stripe is built from three pieces: total revenue (invoices + charges), months active (subscription timestamps), and churn rate (cancellation patterns). Calculate it once, cache it in your database, and refresh when subscriptions change. If you want to track this automatically across tools, Product Analyst can help.