ds@mcpanalytics.aiEmail your data and your question and get an answer about your own data, with the method shown.Email your data and question. Get an answer.

SaaS Customer Lifetime Value

By MCP Analytics Team | Published , updated

The CAC:LTV ratio is a common unit economics metric in SaaS. Yet most founders calculate LTV as "average revenue per customer times average lifespan" -- a formula that treats every customer as identical. Some customers pay for years. Others already placed their last payment and you do not know it yet. This analysis uses probabilistic modeling to predict what each individual customer is worth, which ones are still active, and where to focus your retention spend.

Worked Example (Simulated)

Made-up data, seed 17. A script builds the customers and payments below. They are not real SaaS data.

Question: How well can a simple model guess payment count for the next 90 days from the past 180 days?

Inputs: 120 customers, 180 days of past payments, and 90 days held back for a test. Each customer's payment rate is drawn from a gamma distribution, so rates are uneven by design. Payment amounts are drawn uniformly from $20 to $120, so the mean of $69.55 is near the built-in 70. The past span has 1082 payments.

Method: For each customer, the model guesses a payment count for the test span. It adds 1 to past payments, multiplies by 90, and divides by 180. The added 1 is a simple way to smooth the rate. The script groups customers by past payment count and compares the guess with the test count. For a rough value, it multiplies the mean payment by the mean guessed count.

import numpy as np

SEED = 17
rng = np.random.default_rng(SEED)
customers = 120
calibration_days = 180
holdout_days = 90

# Simulated repeat-payment data. Each customer has a different payment rate.
rate_per_day = rng.gamma(shape=2.0, scale=0.025, size=customers)
calibration_counts = rng.poisson(rate_per_day * calibration_days)
holdout_counts = rng.poisson(rate_per_day * holdout_days)
width = max(calibration_counts.max(), 1)
values = rng.uniform(20.0, 120.0, size=(customers, width))
revenue = sum(values[i, :calibration_counts[i]].sum() for i in range(customers))
transactions = int(calibration_counts.sum())
mean_order_value = revenue / transactions

# Simple smoothed rate: add one prior payment in each 180-day calibration span.
predicted = (calibration_counts + 1) * (holdout_days / calibration_days)

print(f"Simulated data, seed {SEED}")
print(f"Inputs: {customers} customers; {calibration_days}-day calibration; "
      f"{holdout_days}-day holdout")
print(f"Calibration: {transactions} payments; "
      f"mean order value ${mean_order_value:.2f}")
print("Method: predicted holdout payments = "
      "(calibration payments + 1) x holdout days / calibration days")
print("Holdout check by calibration payment count:")
for label, mask in [
    ("0", calibration_counts == 0),
    ("1-3", (calibration_counts >= 1) & (calibration_counts <= 3)),
    ("4+", calibration_counts >= 4),
]:
    n = int(mask.sum())
    pred = float(predicted[mask].sum())
    actual = int(holdout_counts[mask].sum())
    print(f"  {label} payments: {n} customers; "
          f"predicted {pred:.1f}; actual {actual}")
print(f"All customers: predicted {predicted.sum():.1f} holdout payments; "
      f"actual {holdout_counts.sum()}")
mae = np.abs(predicted - holdout_counts).mean()
print(f"Mean absolute error per customer: {mae:.2f} payments")
print(f"Illustrative mean future value per customer: "
      f"${predicted.mean() * mean_order_value:.2f}")

Printed output:

Simulated data, seed 17
Inputs: 120 customers; 180-day calibration; 90-day holdout
Calibration: 1082 payments; mean order value $69.55
Method: predicted holdout payments = (calibration payments + 1) x holdout days / calibration days
Holdout check by calibration payment count:
  0 payments: 4 customers; predicted 2.0; actual 4
  1-3 payments: 23 customers; predicted 36.0; actual 52
  4+ payments: 93 customers; predicted 563.0; actual 506
All customers: predicted 601.0 holdout payments; actual 562
Mean absolute error per customer: 2.16 payments
Illustrative mean future value per customer: $348.32

Result: The model guesses 601.0 payments for all customers. The test has 562. The added 1 raises every customer's guess by half a payment, 60 payments over 120 customers, which is more than the 39 payment gap between 601.0 and 562. The mean absolute error, the average size of the miss, is 2.16 payments per customer. The rule over-predicts the group with 4 or more past payments (563.0 guessed, 506 in the test) and under-predicts the group with 0 past payments (2.0 guessed, 4 in the test) and the group with 1 to 3 (36.0 guessed, 52 in the test). The rough mean future value is $348.32 per customer.

Limit: This is a small test with made-up data. It checks a simple rate rule, not the BG/NBD model. The value guess assumes future payments have the same mean value as past ones. It does not show profit, churn, or fit for a real SaaS firm.

Sources

Why the Simple LTV Formula Fails

The back-of-napkin formula -- ARPU divided by churn rate -- gives you a single number for your entire business. If your ARPU is $80/month and monthly churn is 4%, you get LTV = $80 / 0.04 = $2,000. That number is fine for a pitch deck. It is useless for deciding which customers to invest in.

To forecast what your own customers are worth from here, straight from a transaction file, try the free customer lifetime value tool.

The problem is variation. Your $2,000 average hides a customer who pays $199/month and has been active for 2 years (actual value: $4,776 and counting) and a customer who paid $29 once and vanished (actual value: $29). Treating them the same leads to blanket retention campaigns that overspend on low-value accounts and underspend on high-value ones.

A single LTV figure that is an average hides customer-level variation. Per-customer predictions can show whether your best accounts justify more acquisition spend and whether your lowest-value accounts are worth retaining.

How Per-Customer CLV Prediction Works

The worked example above uses a simple rate rule, not BG/NBD (Beta-Geometric / Negative Binomial Distribution). BG/NBD was published by Fader, Hardie and Lee in 2005. The idea behind this kind of model is simple even if the math is not: it looks at each customer's purchase history and estimates two things.

First, is the customer still "alive"? Not in the literal sense, but in the buying sense -- are they still in their purchasing cycle, or have they silently churned? A customer who bought 5 times last month is almost certainly alive. A customer who bought 5 times over 3 years and has not returned in 6 months is probably gone. The model quantifies this as a P(alive) score between 0 and 1.

Second, how many future transactions should you expect? The model weighs recency (when did they last pay?) against frequency (how often do they pay?) to predict future behavior. A second step estimates the average value of each customer's transactions. Multiply expected future transactions by expected order value, and you get a predicted CLV for every person in your database.

The result is not one LTV number for the business. It is a ranked list of every customer with their individual predicted value, their probability of still being active, and their expected future transactions over the next 12 months.

What the Report Shows You

P(alive) probability matrix -- arguably the most actionable output. A heatmap showing which frequency-recency combinations correspond to active customers and which correspond to likely churners. Scores in the middle of the range mark customers who may be worth a check-in; test it before you set a rule.

Top customers by predicted CLV -- a ranked list of your highest-value accounts by predicted future revenue. Each entry shows the customer identifier, predicted CLV, P(alive) score, and historical purchase stats. This is the list your retention team should work from. Losing any of these customers has an outsized impact on future revenue.

CLV distribution -- shows how predicted value is distributed across your customer base. In most businesses, the distribution is heavily right-skewed. The chart shows how concentrated your value is and can help you decide how many customers are worth individual attention versus segment-level campaigns.

Customer segment summary -- groups customers into actionable categories based on model scores: Champions (high frequency, high recency, high CLV), At Risk (historically valuable but declining P(alive)), Hibernating (low recent activity), and New (recent first purchase, not enough data yet). Each segment shows size, average CLV, average P(alive), and total predicted revenue. Design a different retention action for each segment.

Expected transactions matrix -- a heatmap showing expected future transactions by frequency and recency. Customers in the upper-right (high frequency, recent purchase) are expected to keep buying. Customers in the lower-left are expected to buy rarely or never.

Holdout check -- the data is split into a calibration period and a holdout period. The holdout table shows predicted vs. actual transactions for each frequency group. When they track closely, the forward-looking estimates have more support.

When to Use CLV Modeling

What Data Do You Need?

A CSV of your transaction or billing history with three required columns:

Required columns

Optional columns

For SaaS, each monthly subscription payment is a transaction row. If your billing system only records subscription start/end dates (not individual payments), generate a row per payment month before uploading.

Minimum: 50 unique customers with some repeat transactions and at least 90 days of history. Best results with 200+ customers and 6+ months. The model needs repeat purchase behavior -- datasets with mostly one-time buyers produce estimates with high uncertainty.

Common Mistakes

When to Use Something Else

Your turn

Bring your own data and the question you actually need answered.

MCP AnalyticsData science by email Send us your data and your question, and we will reply. ds@mcpanalytics.ai