6 min read

What Is LTV in Stripe

Your recurring revenue business lives or dies by customer LTV. Stripe has all the data you need to calculate it, but it doesn't hand you an LTV number directly—you have to assemble it from invoices and subscription history. Here's how.

What LTV Actually Means in Stripe

LTV is the total revenue a customer generates over the entire relationship. In Stripe, this means summing all successful payments—subscriptions, one-time charges, whatever.

Understand the Stripe customer data model

A Customer in Stripe is the container for all transactions. Every invoice, charge, and subscription attaches to a customer ID. To calculate LTV, you fetch a customer and sum their amount_paid across all paid invoices.

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

// Fetch a customer and their paid invoices
const customer = await stripe.customers.retrieve('cus_...');
const invoices = await stripe.invoices.list({
  customer: customer.id,
  status: 'paid',
  limit: 100
});

const ltv = invoices.data.reduce((sum, inv) => sum + inv.amount_paid, 0) / 100;
Amounts are in cents, so divide by 100 to convert to dollars.

Remember LTV includes all revenue, not just subscriptions

Don't limit yourself to subscription invoices. Include one-time charges, manual invoices, and anything else the customer paid. The invoices.list() call with status: 'paid' captures all of it.

javascript
// Get paid invoices across all revenue sources
const allPaidInvoices = await stripe.invoices.list({
  customer: 'cus_...',
  status: 'paid',
  limit: 100
});

const totalLTV = allPaidInvoices.data.reduce((sum, inv) => {
  return sum + inv.amount_paid;
}, 0) / 100;

console.log(`Total customer LTV: $${totalLTV.toFixed(2)}`);
Watch out: amount_paid on invoices accounts for refunds automatically—it's net revenue. If you want gross revenue before refunds, use the Charges API and sum amount instead.

Calculate LTV from payment history

In practice, you'll query Stripe's API to pull all a customer's invoices, then sum them. Here's the working pattern.

List all paid invoices for a customer

Use the Invoices API with status: 'paid' to get only money that cleared. If a customer has more than 100 invoices, paginate using starting_after.

javascript
async function getCustomerLTV(customerId) {
  let allInvoices = [];
  let hasMore = true;
  let startingAfter = null;

  while (hasMore) {
    const response = await stripe.invoices.list({
      customer: customerId,
      status: 'paid',
      limit: 100,
      starting_after: startingAfter
    });

    allInvoices = allInvoices.concat(response.data);
    hasMore = response.has_more;
    startingAfter = response.data[response.data.length - 1]?.id;
  }

  const ltv = allInvoices.reduce((sum, inv) => sum + inv.amount_paid, 0) / 100;
  return ltv;
}

const ltv = await getCustomerLTV('cus_...');
Handles pagination automatically for customers with dozens of invoices.

Sum up the paid amounts

Add up amount_paid from each invoice to get net revenue (after refunds). Divide by 100 to convert from cents.

javascript
const ltv = invoices.reduce((sum, invoice) => {
  return sum + invoice.amount_paid;
}, 0) / 100;

console.log(`Total LTV: $${ltv.toFixed(2)}`);

Use the Charges API for gross revenue

If you need gross revenue before refunds, query Charges and filter by status: 'succeeded'. Subtract refunds separately if needed.

javascript
const charges = await stripe.charges.list({
  customer: 'cus_...',
  limit: 100
});

const grossRevenue = charges.data
  .filter(charge => charge.status === 'succeeded')
  .reduce((sum, charge) => sum + charge.amount, 0) / 100;

console.log(`Gross revenue: $${grossRevenue.toFixed(2)}`);
Tip: Stripe's Dashboard shows customer revenue in the customer detail page, but querying the API directly gives you the raw data to audit and extend for your own analysis.

Rank customers by LTV

Once you can calculate LTV, segment your customers by value. Batch-fetch and rank them to identify your top spenders.

Fetch all customers and calculate LTV for each

List all customers, then calculate LTV for each one. Sort by value to identify your high-value accounts.

javascript
async function getAllCustomerLTVs() {
  let customers = [];
  let startingAfter = null;

  while (true) {
    const response = await stripe.customers.list({
      limit: 100,
      starting_after: startingAfter
    });

    for (const customer of response.data) {
      const invoices = await stripe.invoices.list({
        customer: customer.id,
        status: 'paid'
      });
      const ltv = invoices.data.reduce((s, i) => s + i.amount_paid, 0) / 100;
      customers.push({ id: customer.id, email: customer.email, ltv });
    }

    if (!response.has_more) break;
    startingAfter = response.data[response.data.length - 1].id;
  }

  return customers.sort((a, b) => b.ltv - a.ltv);
}

const ranked = await getAllCustomerLTVs();
console.log('Top 10 customers:', ranked.slice(0, 10));
Watch out: At scale (1000+ customers), this operation gets expensive—one API call per customer. Export to a data warehouse and calculate there instead.

Common Pitfalls

  • Forgetting to paginate—customers with 100+ invoices return incomplete results without starting_after loops.
  • Using total instead of amount_paidtotal includes amounts due and refunded amounts; amount_paid is actual net revenue.
  • Mixing invoices and charges—invoices handle subscriptions and multi-line items; charges are one-offs. Include both for true LTV.
  • Calculating LTV once and treating it as static—LTV changes every time a customer pays, so recalculate regularly for current analysis.

Wrapping Up

LTV in Stripe is just the sum of what a customer paid you, calculated from the Invoices API with status: 'paid'. Start with a single customer to test, then batch-fetch for all customers to rank by value. If you want to track LTV automatically across Stripe, Mixpanel, Amplitude, and other 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