6 min read

How to Calculate LTV in Stripe

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.

javascript
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`);
}
Fetch customers and list all their subscriptions (active + canceled)

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.

javascript
// 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.

javascript
// 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)}`);
Watch out: Stripe amounts are in cents for USD. Always divide by 100 when displaying dollars. Also, charges linked to invoices will be double-counted if you're not careful — filter by 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.

javascript
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.

javascript
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.

javascript
// 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 });
}
Tip: Don't call Stripe's API every time you need LTV. Cache calculations in your database and refresh only when webhooks fire. Stripe rate-limits to 100 requests/second — looping through thousands of customers during peak hours will hit limits fast.

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.invoice before 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.

Track these metrics automatically

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

Try Product Analyst — Free