
Biz Cac Ltv
- 30 installs
- 223 repo stars
- Updated June 6, 2026
- asgard-ai-platform/skills
biz-cac-ltv is a Claude skill that calculates Customer Acquisition Cost and Customer Lifetime Value and analyzes the LTV:CAC ratio to evaluate unit economics and marketing efficiency.
About
This skill calculates Customer Acquisition Cost and Customer Lifetime Value and analyzes the LTV:CAC ratio to evaluate unit economics and marketing efficiency. A developer uses it to judge whether customer acquisition is profitable and where to allocate marketing budget. It outputs a unit-economics summary with CAC by channel, an LTV calculation, and per-segment optimization recommendations.
- Calculates Customer Acquisition Cost and Customer Lifetime Value and the LTV:CAC ratio
- Enforces the 3:1 LTV:CAC health threshold and inclusion of all acquisition costs (salaries, tools, content)
- Outputs unit-economics summary, CAC-by-channel table, LTV calculation, and optimization recommendations
Biz Cac Ltv by the numbers
- 30 all-time installs (skills.sh)
- Ranked #604 of 853 Sales & Marketing skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
biz-cac-ltv capabilities & compatibility
- Capabilities
- financial ratios · pricing strategy · cohort analysis
- Use cases
- data analysis · marketing
What biz-cac-ltv says it does
LTV:CAC ratio must be at least 3:1 for sustainable businesses.
CAC = Total marketing & sales spend / Number of new customers acquired
npx skills add https://github.com/asgard-ai-platform/skills --skill biz-cac-ltvAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 30 |
|---|---|
| repo stars | ★ 223 |
| Last updated | June 6, 2026 |
| Repository | asgard-ai-platform/skills ↗ |
What it does
Compute CAC, LTV, and the LTV:CAC ratio to assess whether customer acquisition is profitable and sustainable.
Who is it for?
Assessing whether customer acquisition is profitable and where to allocate acquisition budget
Skip if: Product pricing decisions, customer segmentation, or comprehensive financial analysis (the docs route those to Pricing Strategy, STP/RFM, or financial ratios)
When should I use this skill?
A user asks whether they are spending too much on ads, what a customer is worth, or whether their growth is sustainable
What you get
A unit-economics summary with CAC by channel, an LTV calculation, and the LTV:CAC ratio against the 3:1 benchmark plus optimization actions.
- unit-economics summary
- CAC-by-channel table
- LTV calculation
By the numbers
- healthy LTV:CAC benchmark of >3:1
- target payback period <12 months
- 5-step process from CAC to optimization
Files
CAC and LTV Analysis
Overview
CAC (Customer Acquisition Cost) and LTV (Customer Lifetime Value) are the two fundamental unit economics metrics. Together they answer: "Does each customer generate more revenue than it costs to acquire them?" The LTV:CAC ratio is the single most important indicator of marketing efficiency and business model viability.
When to Use
Trigger conditions:
- User evaluating marketing spend efficiency
- User asks "what's each customer worth?" or "are we spending too much on marketing?"
- User assessing business model viability or fundraising metrics
- User needs to allocate budget across acquisition channels
When NOT to use:
- For product pricing decisions → use Pricing Strategy
- For customer segmentation → use STP or RFM
- For comprehensive financial analysis → use financial ratios
Framework
IRON LAW: LTV:CAC > 3 for Healthy Business
LTV:CAC ratio must be at least 3:1 for sustainable businesses.
- < 1:1 = You're LOSING money on every customer
- 1-3:1 = Unsustainable unless you can reduce CAC or increase LTV
- 3-5:1 = Healthy
- > 5:1 = Potentially underinvesting in growth (leaving market share on the table)
This ratio applies to the BLENDED average. Individual channels can be
below 3:1 if the overall blend exceeds it.IRON LAW: CAC Must Include ALL Acquisition Costs
CAC = Total marketing & sales spend / Number of new customers acquired
"Total spend" includes: ad spend, marketing team salaries, sales team
salaries, tools, content production, events — EVERYTHING spent to acquire
customers in that period. Excluding salaries or tools understates true CAC.Step 1: Calculate CAC
Basic formula:
CAC = Total acquisition spend in period / New customers acquired in periodBy channel:
CAC (Channel X) = Spend on Channel X / Customers from Channel XInclude in total acquisition spend:
- Advertising (digital + offline)
- Marketing team compensation
- Sales team compensation (for B2B)
- Marketing tools and software
- Content production costs
- Events and sponsorships
- Agency fees
Step 2: Calculate LTV
Simple formula:
LTV = ARPU × Gross Margin % × Average Customer LifespanWhere:
- ARPU = Average Revenue Per User per period (monthly or annual)
- Gross Margin % = (Revenue - COGS) / Revenue
- Average Customer Lifespan = 1 / Churn Rate
Cohort-based (more accurate): Track actual revenue per customer cohort over time. Sum cumulative revenue per customer, apply gross margin.
Step 3: Calculate Key Ratios
| Metric | Formula | Healthy Benchmark |
|---|---|---|
| LTV:CAC | LTV / CAC | > 3:1 |
| Payback Period | CAC / (ARPU × Gross Margin) | < 12 months |
| CAC % of LTV | CAC / LTV × 100 | < 33% |
Step 4: Segment Analysis
Calculate CAC and LTV by:
- Channel: Which acquisition channels are most efficient?
- Customer segment: Which segments have highest LTV:CAC?
- Cohort: Is LTV improving or degrading over time?
Step 5: Optimization Strategies
To reduce CAC:
- Shift budget to lower-CAC channels
- Improve conversion rates (better landing pages, sales process)
- Increase organic/referral acquisition (content, word-of-mouth)
To increase LTV:
- Reduce churn (improve product, customer success)
- Increase ARPU (upsell, cross-sell, price increases)
- Extend customer lifespan (loyalty programs, switching costs)
Output Format
# CAC-LTV Analysis: {Company/Product}
## Unit Economics Summary
| Metric | Value | Benchmark | Status |
|--------|-------|-----------|--------|
| CAC (blended) | ${X} | — | — |
| LTV | ${X} | — | — |
| LTV:CAC | {X}:1 | > 3:1 | ✓/✗ |
| Payback Period | {X} months | < 12 months | ✓/✗ |
## CAC by Channel
| Channel | Spend | Customers | CAC | % of Total |
|---------|-------|-----------|-----|-----------|
| {channel} | ${X} | {N} | ${X} | {X%} |
## LTV Calculation
- ARPU: ${X}/month
- Gross Margin: {X%}
- Avg Lifespan: {X} months (churn rate: {X%}/month)
- LTV = ${X}
## LTV:CAC by Segment
| Segment | CAC | LTV | Ratio | Action |
|---------|-----|-----|-------|--------|
| {seg A} | ${X} | ${X} | {X}:1 | Invest / Maintain / Cut |
## Optimization Recommendations
1. ...
2. ...Examples
Correct Application
Scenario: CAC-LTV for a Taiwanese B2C subscription box (monthly NT$599)
CAC calculation:
| Item | Monthly Spend |
|---|---|
| Facebook/Instagram ads | NT$200,000 |
| Google Ads | NT$80,000 |
| KOL partnerships | NT$50,000 |
| Marketing team (2 people) | NT$120,000 |
| Total | NT$450,000 |
New customers in month: 300 CAC = NT$450,000 / 300 = NT$1,500
LTV calculation:
- ARPU: NT$599/month
- Gross Margin: 55%
- Monthly churn: 8% → Avg lifespan: 1/0.08 = 12.5 months
- LTV = NT$599 × 0.55 × 12.5 = NT$4,118
LTV:CAC = 4,118 / 1,500 = 2.75:1 — Below the 3:1 threshold. Need to either reduce CAC or improve retention.
Incorrect Application
What went wrong:
- CAC calculated as "ad spend / new customers" only, excluding NT$120K/month marketing team salary → True CAC is NT$1,500, not NT$1,100. Violates Iron Law: include ALL acquisition costs.
- LTV:CAC of 1.8:1 reported as "good because we're growing" → Growth at LTV:CAC < 3:1 means you're growing into larger losses. Violates Iron Law: ratio must be > 3:1.
Gotchas
- Attribution is messy: A customer who saw an Instagram ad, Googled your brand, then signed up via a referral link — which channel gets credit? Be consistent in attribution methodology (first-touch, last-touch, or multi-touch).
- Blended vs marginal CAC: Blended CAC includes all channels. Marginal CAC is the cost of acquiring ONE MORE customer. As you scale, marginal CAC typically rises (best channels saturate first).
- LTV is always an estimate: Future churn and spending behavior are uncertain. Use conservative assumptions and update with real cohort data as it accumulates.
- Payback period matters for cash flow: Even with LTV:CAC of 5:1, if payback takes 24 months, you need significant upfront capital. Fast-growing companies can die from long payback periods despite great unit economics.
- Negative churn is a superpower: If expansion revenue (upsells) exceeds lost revenue (churn), Net Revenue Retention > 100%. This means LTV grows over time — the best possible scenario.
Scripts
| Script | Description | Usage |
|---|---|---|
scripts/cac_ltv.py | Compute CAC, LTV, LTV/CAC ratio, and payback period | python scripts/cac_ltv.py --help |
Run python scripts/cac_ltv.py --verify to execute built-in sanity tests.
References
- For cohort-based LTV calculation methods, see
references/cohort-ltv.md - For channel attribution models, see
references/attribution-models.md
{
"_comment": "台灣 B2C 訂閱箱服務單月數據:三個付費渠道 + 行銷團隊固定成本,月均流失率 8%,測試 LTV:CAC 落在 3:1 門檻附近的臨界情境",
"channels": [
{
"name": "Facebook/Instagram Ads",
"spend": 200000,
"customers": 148
},
{
"name": "Google Ads",
"spend": 80000,
"customers": 61
},
{
"name": "KOL Partnerships",
"spend": 50000,
"customers": 38
},
{
"name": "Referral / Organic",
"spend": 0,
"customers": 53
}
],
"additional_costs": [
{
"name": "Marketing Team (2 FTE)",
"amount": 120000
},
{
"name": "Marketing Tools & Software",
"amount": 18000
},
{
"name": "Content Production",
"amount": 22000
}
],
"arpu": 599,
"gross_margin": 0.55,
"monthly_churn_rate": 0.08,
"currency": "TWD"
}
Attribution Models for CAC Calculation
Attribution determines which marketing channel "gets credit" for a customer acquisition — and therefore which channel's CAC you're calculating. Wrong attribution = wrong CAC = wrong budget decisions.
The Core Problem
A typical B2C customer journey before converting:
Day 1: Sees Instagram ad → no click
Day 3: Clicks Facebook retargeting ad → bounces
Day 7: Googles brand name → clicks organic result → reads blog
Day 9: Clicks Google Search ad → starts free trial
Day 12: Receives email nurture → converts to paidFive touchpoints. One customer. Each model assigns credit differently.
---
The Five Standard Models
1. Last-Touch Attribution
100% of credit goes to the last touchpoint before conversion.
Touchpoint Credit
Instagram 0%
Facebook 0%
Organic 0%
Google Ad 0%
Email 100% ← last touchCAC implication: Email CAC looks very low. Google Ads and paid social look expensive.
Use when: You have a very short sales cycle (< 1 day) with minimal multi-touch journeys.
Systematic bias: Overvalues bottom-funnel channels (email, retargeting, brand search). Undervalues awareness channels. Will cause you to cut top-of-funnel spend and wonder why pipeline dries up 60 days later.
---
2. First-Touch Attribution
100% of credit goes to the first touchpoint.
Touchpoint Credit
Instagram 100% ← first touch
Facebook 0%
Organic 0%
Google Ad 0%
Email 0%Use when: You care about which channel introduced you to customers. Useful for brand awareness measurement.
Systematic bias: Overvalues awareness channels. Makes email and retargeting look useless (they get no credit even when they close deals).
---
3. Linear Attribution
Credit split equally across all touchpoints.
Touchpoint Credit
Instagram 20%
Facebook 20%
Organic 20%
Google Ad 20%
Email 20%Formula:
Credit per touchpoint = 1 / Total touchpoints in journeyCAC implication: If you spent NT$10,000 on Google Ads and NT$10,000 on email in a month, and the average journey has 5 touches:
- Each channel gets 20% of acquisition credit
- CAC per channel = Channel Spend / (Customers × 0.20)
Use when: You genuinely don't know which touchpoints matter more. It's unbiased, but also uninformative.
Systematic bias: Treats a fleeting ad impression the same as a 20-minute product demo. Equal weight ≠ equal influence.
---
4. Time-Decay Attribution
Recent touchpoints get more credit. Credit decays exponentially backward in time.
Standard half-life: 7 days. A touchpoint 7 days before conversion gets half the credit of one the day before conversion.
Decay formula:
Weight(t) = 2^(−d/half_life)
Where d = days before conversionWorked example (7-day half-life):
| Touchpoint | Days Before Conversion | Raw Weight | Normalized Credit |
|---|---|---|---|
| 11 | 2^(−11/7) = 0.336 | 11.8% | |
| 9 | 2^(−9/7) = 0.406 | 14.3% | |
| Organic | 5 | 2^(−5/7) = 0.609 | 21.4% |
| Google Ad | 3 | 2^(−3/7) = 0.743 | 26.1% |
| 0 | 2^(0/7) = 1.000 | 35.2% | |
| Total | 3.094 | 108.8% → normalize |
Normalization: divide each by sum (3.094):
Instagram: 0.336/3.094 = 10.9%
Facebook: 0.406/3.094 = 13.1%
Organic: 0.609/3.094 = 19.7%
Google Ad: 0.743/3.094 = 24.0%
Email: 1.000/3.094 = 32.3%Use when: Your sales cycle is moderate (1-4 weeks) and closing activities genuinely matter more. Standard for B2C e-commerce.
---
5. Position-Based (U-Shaped) Attribution
First and last touchpoints each get 40%. Middle touchpoints split the remaining 20%.
Touchpoint Credit
Instagram 40% ← first
Facebook 6.7% ← middle (20% / 3)
Organic 6.7% ← middle
Google Ad 6.7% ← middle
Email 40% ← lastUse when: You believe both discovery and closing are high-value moments, but middle assists are less decisive. Common in B2B SaaS where the first demo and the final proposal meeting are the key events.
---
Model Comparison: Same Data, Different CAC
Setup: A company acquires 100 customers in a month with the journey above (all five touchpoints, one customer type).
Monthly spend:
- Instagram: NT$50,000
- Facebook: NT$80,000
- Google Ads: NT$120,000
- Email tools + labor: NT$30,000
- Total: NT$280,000
- Blended CAC: NT$2,800
CAC by channel under each model (NT$):
| Channel | Spend | Last-Touch | First-Touch | Linear | Time-Decay | U-Shaped |
|---|---|---|---|---|---|---|
| 50K | ∞* | 1,250 | 5,600 | 5,138 | 3,182 | |
| 80K | ∞* | ∞* | 11,200 | 8,122 | — | |
| Google Ads | 120K | ∞* | ∞* | 16,800 | 6,667 | — |
| 30K | 750 | ∞* | 4,200 | 2,480 | 1,818 |
*∞ = channel gets 0 conversions attributed, so CAC is undefined
Key observation: Under last-touch, Email CAC appears to be NT$750 — a "great" channel. Under time-decay, it's NT$2,480. The underlying economics haven't changed; your measurement model has.
---
Decision Framework: Which Model to Use
Q1: Is your typical customer journey < 2 touchpoints or < 24 hours?
YES → Last-touch is fine. Multi-touch models add noise, not signal.
NO → Go to Q2.
Q2: Do you have reliable data on all touchpoints across channels?
NO → Use linear. Admitting ignorance beats false precision.
YES → Go to Q3.
Q3: Does your sales cycle exceed 2 weeks?
YES → Time-decay (B2C) or U-shaped (B2B)
NO → Go to Q4.
Q4: Are you primarily trying to measure brand awareness ROI?
YES → First-touch (plus last-touch as a secondary view)
NO → Time-decayDefault recommendation for most Taiwanese e-commerce and SaaS companies: Time-decay with 7-day half-life. It's defensible, accounts for recency without completely ignoring assists, and most analytics platforms implement it natively.
---
Data Requirements by Model
| Model | Minimum Data Needed | What Breaks It |
|---|---|---|
| Last-touch | Final conversion event + source tag | UTM stripping, Safari ITP |
| First-touch | First session source | Direct traffic that should be attributed elsewhere |
| Linear | Full session history with sources | Sessions with (direct)/(none) source |
| Time-decay | Full session history + timestamps | Clock skew between events |
| U-shaped | Full session history + touchpoint order | Journeys with only 1-2 touches |
The data gap problem: Cross-device journeys (mobile → desktop → tablet) break all session-based models. Logged-in users with a persistent ID are partially solvable; anonymous users are not. If > 30% of your traffic is cross-device anonymous, your attribution numbers have a structural error floor regardless of which model you pick.
---
Implementing in Practice
Using GA4 (Google Analytics 4)
GA4 offers: Last click, First click, Linear, Time decay, Position-based, and Data-driven (ML-based).
Navigation: Admin → Attribution settings → Reporting attribution model
⚠ GA4 only attributes to Google-trackable touchpoints. Offline events, dark social (WhatsApp shares, email forwards), and app-to-web journeys require manual stitching.
Using a Spreadsheet (Manual Multi-Touch)
When you can export session-level data with UTM parameters:
# Pseudocode for time-decay attribution
HALF_LIFE_DAYS = 7
for each conversion_event:
sessions = get_sessions_before_conversion(user_id, lookback_days=30)
weights = []
for session in sessions:
d = (conversion_date - session.date).days
w = 2 ** (-d / HALF_LIFE_DAYS)
weights.append((session.channel, w))
total_weight = sum(w for _, w in weights)
for channel, w in weights:
channel_credits[channel] += w / total_weightCAC Calculation with Fractional Attribution
Once you have fractional credits per channel:
CAC (channel X) = Spend on channel X
──────────────────────────────────────────
Σ attribution_credit(channel X, customer i)
for all customers i acquired in periodBlended CAC check: Sum of all channel-attributed credits should equal total customers acquired. If not, you have an attribution gap (usually untracked direct traffic).
---
Common Mistakes
1. Comparing CAC across models without disclosing the model
"Our Facebook CAC improved from NT$800 to NT$600" — meaningless unless the attribution model is the same in both periods. Platform migrations (UA → GA4) often change the default model and create phantom improvements.
2. Using channel-level CAC for budget decisions without checking blended CAC
Individual channel CAC can look great even when blended CAC is terrible. Always anchor to blended CAC first:
If Σ(channel CAC × channel share) ≠ Blended CAC:
→ You have an attribution gap; do not trust channel-level numbers for budget decisions3. Letting the platform choose the model for you
Meta Ads Manager defaults to last-click, 7-day click + 1-day view. Google Ads defaults to data-driven (when available) or last-click. These are different models. Comparing them directly inflates total attributed customers beyond actual acquisitions (double-counting assists).
4. Treating UTM-based attribution as ground truth
UTMs only survive if:
- The user clicks a link (impressions untracked)
- The browser preserves the parameter (Safari strips some cross-site params)
- The landing page passes the UTM through (redirects can strip them)
Use UTM-based numbers as directional, not absolute.
5. Ignoring view-through attribution entirely
A customer who saw your YouTube ad three times and then converted via "direct" was influenced by YouTube — but zero-touch last-click misses this entirely. View-through attribution (typically 1-day window for display, 7-day for video) is imprecise but not zero.
---
The Incrementality Alternative
Attribution models answer "which channel got credit?" Incrementality answers "what would have happened without this channel?"
Holdout test design: 1. Randomly split customers into treatment (see ads) and holdout (ads suppressed) 2. Measure conversion rate difference 3. Incremental CAC = Spend / (Treatment conversions − Holdout conversions)
This is the only attribution method that is causally defensible. It requires:
- Minimum ~500 users per cell for statistical power
- A platform that supports holdouts (Meta, Google both do)
- 2-4 week test window
When blended LTV:CAC is close to the 3:1 threshold, incrementality testing is worth the setup cost. If your attributed ratio is 3.2:1 but actual incrementality shows 40% of conversions would have happened anyway, your true ratio is closer to 1.9:1 — a fundamentally different business situation.
Cohort-Based LTV Calculation
Simple LTV = ARPU × Gross Margin × (1 / Churn Rate) assumes churn is constant and behavior is uniform. Real cohorts behave differently: early cohorts churn faster, pricing changes mid-stream, expansion revenue kicks in at month 6. Cohort-based LTV measures what actually happened to a group of customers acquired in the same period.
Core Concept
A cohort is a group of customers acquired in the same time window (typically a calendar month). You track each cohort's cumulative revenue through time, then calculate average LTV per customer as the cohort ages.
Cohort LTV (month N) = Cumulative revenue from cohort through month N
─────────────────────────────────────────────
Original cohort size at month 0This gives you a per-customer LTV at each age. As N → ∞ (or as churn approaches 100%), the series converges to the true LTV for that cohort.
Step-by-Step Procedure
Step 1: Build the Revenue Retention Matrix
Rows = cohorts (acquisition month). Columns = age in months (0, 1, 2, ...). Each cell = total revenue from that cohort at that age.
| Cohort | M0 | M1 | M2 | M3 | M4 |
|---|---|---|---|---|---|
| Jan | 59,900 | 48,518 | 40,271 | 34,232 | 29,097 |
| Feb | 77,870 | 63,274 | 52,724 | 44,816 | — |
| Mar | 53,910 | 44,239 | 37,603 | — | — |
| Apr | 65,880 | 53,920 | — | — | — |
Revenue in NT$. Jan cohort had 100 customers at NT$599/month.
Step 2: Convert to Cumulative Revenue per Customer
Divide each row by original cohort size, then take running sum.
| Cohort | M0 | Cum M1 | Cum M2 | Cum M3 | Cum M4 |
|---|---|---|---|---|---|
| Jan (n=100) | 599 | 1,083 | 1,485 | 1,827 | 2,118 |
| Feb (n=130) | 599 | 1,085 | 1,490 | 1,834 | — |
| Mar (n=90) | 599 | 1,091 | 1,509 | — | — |
| Apr (n=110) | 599 | 1,089 | — | — | — |
Cum M1 for Jan = (59,900 + 48,518) / 100 = 1,083
Step 3: Apply Gross Margin
The above numbers are revenue. LTV tracks contribution, not revenue.
Cohort LTV (month N) = Cumulative Revenue per Customer × Gross Margin %With Gross Margin = 55%:
| Age | Gross Revenue/Customer | LTV (55% GM) |
|---|---|---|
| M0 | 599 | 329 |
| M1 | 1,083 | 596 |
| M2 | 1,485 | 817 |
| M3 | 1,827 | 1,005 |
| M4 | 2,118 | 1,165 |
Step 4: Project to Full LTV
You can only observe cohort LTV up to how long you've been in business. For Jan cohort at month 4, LTV is NT$1,165 — but the customer hasn't fully churned yet. You need to project forward.
Option A: Extrapolate via observed churn curve
Fit an exponential decay to observed retention:
Retention(t) = e^(−λt)
λ = − ln(retention rate at M1)For Jan cohort: M1 retention = 48,518 / 59,900 = 0.810 λ = −ln(0.810) = 0.211
Projected additional LTV from month N onward:
= (Monthly Revenue × GM) × Retention(N) / λFrom month 4 onward:
Retention(4) = e^(−0.211 × 4) = 0.431
Additional LTV = (599 × 0.55) × 0.431 / 0.211 = NT$672
Total projected LTV = 1,165 + 672 = NT$1,837Compare this to the simple-formula estimate:
Simple LTV = 599 × 0.55 × (1 / 0.19) = NT$1,734(Using observed average monthly churn of ~19% from M0→M1 for Jan cohort)
The two estimates are close here. When they diverge significantly, prefer the cohort projection because it reflects actual behavior.
Option B: Use the oldest cohort as a benchmark
If your Jan cohort is 18 months old and LTV is NT$3,200, that is your most reliable estimate. Apply a small growth multiplier if newer cohorts show better retention.
Reading the Cohort Curve Shape
The shape of cumulative LTV per customer over time reveals business health.
Cumulative LTV
per customer
| ___________ ← Flat: churn has stopped, remaining customers
| __/ are long-term loyalists
| _/
| _/ ← Steep early: strong first-purchase revenue
| /
|/___________________________
0 3 6 9 12 months| Shape | Interpretation | Action |
|---|---|---|
| Steep M0, then flattens quickly | High first-payment, fast early churn | Fix onboarding / M1 retention |
| Roughly linear | Steady churners, no expansion | Add upsell/cross-sell to steepen curve |
| Accelerating slope | Expansion revenue kicking in | Protect and amplify expansion motion |
| Curve bends downward | Refunds, chargebacks, or negative expansion | Investigate immediately |
Accounting for Expansion Revenue
When customers upgrade plans or buy add-ons, their monthly revenue increases over time. This breaks the simple-formula assumption that ARPU is constant.
Track revenue per active customer per month:
| Month | Active Customers | Total Revenue | Revenue per Active Customer |
|---|---|---|---|
| M0 | 100 | 59,900 | 599 |
| M1 | 81 | 52,164 | 644 |
| M2 | 67 | 46,098 | 688 |
| M3 | 57 | 41,610 | 730 |
Revenue per active customer is rising — surviving customers are expanding. This is negative churn in revenue terms (even though headcount churn is ~19%/month).
Net Revenue Retention (NRR):
NRR = Revenue from existing cohort at month N
─────────────────────────────────────────
Revenue from that cohort at month 0Month 3 NRR for Jan cohort = 41,610 / 59,900 = 69.5%
NRR > 100% means absolute revenue from the cohort is growing despite some customers leaving. When NRR > 100%, the simple LTV formula understates true LTV; cohort tracking is essential.
Worked Python Snippet
import numpy as np
# Revenue matrix: rows = cohorts, cols = age in months
# -1 = not yet observable
revenue = [
[59900, 48518, 40271, 34232, 29097], # Jan, n=100
[77870, 63274, 52724, 44816, -1], # Feb, n=130
[53910, 44239, 37603, -1, -1], # Mar, n=90
[65880, 53920, -1, -1, -1], # Apr, n=110
]
sizes = [100, 130, 90, 110]
gm = 0.55
def cumulative_ltv(revenue_row, cohort_size, gm):
"""Return cumulative LTV per customer at each observable age."""
ltv = []
cumrev = 0
for r in revenue_row:
if r < 0:
break
cumrev += r
ltv.append(round(cumrev / cohort_size * gm, 1))
return ltv
for i, row in enumerate(revenue):
print(f"Cohort {i}: {cumulative_ltv(row, sizes[i], gm)}")
# Output:
# Cohort 0: [329.5, 595.4, 816.6, 1004.5, 1164.9]
# Cohort 1: [329.5, 595.4, 817.1, 1034.3]
# Cohort 2: [329.5, 595.3, 821.8]
# Cohort 3: [329.5, 625.5]Common Errors
Mixing new and existing customers in the revenue count
When you sum revenue for the Jan cohort at M3, use ONLY revenue traceable to the original Jan customers. If your data warehouse sums all-customer revenue by calendar month, you'll overcount.
Using calendar months instead of cohort age
Jan cohort at "month 3" means 3 months after acquisition (April). Not the calendar month of March. Keep cohort age (0, 1, 2, ...) separate from calendar dates.
Treating early cohorts as representative
Your first cohorts may have been acquired via personal networks or early-adopter channels. They often churn less and spend more. Use them as a ceiling, not a target.
Projecting before you have at least 3 data points
Fitting an exponential curve to two data points gives garbage projections. Wait until month 3 before extrapolating.
Ignoring cohort size when averaging
Weighted average LTV across cohorts must weight by cohort size, not by number of cohorts:
Blended LTV = Σ(LTV_i × size_i) / Σ(size_i)Connecting Back to LTV:CAC
Cohort LTV at month N is not the same as the LTV used in LTV:CAC. For LTV:CAC, use the projected full LTV (extrapolated to full churn-out), not the observed-to-date number.
Typical practice:
- Use the 24-month cohort LTV as a proxy for full LTV if your average lifespan is shorter than 24 months
- Use the oldest observable cohort as the estimate if you have less than 24 months of history
- Document which definition you're using — investors and operators often mean different things
Payback period uses the same LTV numerically but measures a different question: how many months until cumulative contribution per customer equals CAC. Read directly off the cohort curve:
Find the month N where:
Cumulative LTV per customer (month N) ≥ CACFor Jan cohort with CAC = NT$1,500:
- M2 LTV = NT$817 → not recovered
- M3 LTV = NT$1,005 → not recovered
- Extrapolate: recovery occurs around month 5–6
This is more accurate than the formula-based CAC / (ARPU × GM) because it reflects actual retention behavior.
#!/usr/bin/env python3
"""
CAC / LTV / Unit Economics calculator.
Deterministic calculator for:
- Customer Acquisition Cost (CAC)
- Customer Lifetime Value (LTV)
- LTV/CAC ratio (health metric)
- CAC payback period (months)
Usage:
python cac_ltv.py --marketing-cost 100000 --new-customers 500 \
--arpu 50 --gross-margin 0.70 --monthly-churn 0.05
python cac_ltv.py --input data.json
python cac_ltv.py --verify
All monetary values in the same currency. ARPU and churn must be on the
same time basis (e.g., both monthly).
"""
import argparse
import json
import sys
def compute(marketing_cost, new_customers, arpu, gross_margin, monthly_churn):
"""Compute CAC, LTV, LTV/CAC ratio, and payback period.
Args:
marketing_cost: Total marketing + sales spend over the period.
new_customers: Number of NEW customers acquired in that period.
arpu: Average revenue per user per month.
gross_margin: Gross margin as decimal (e.g., 0.70 for 70%).
monthly_churn: Monthly churn rate as decimal (e.g., 0.05 for 5%).
Returns:
Dict with CAC, LTV, ratio, payback_months, health assessment.
"""
if new_customers <= 0:
raise ValueError("new_customers must be > 0")
if not (0 < gross_margin <= 1):
raise ValueError("gross_margin must be in (0, 1]")
if not (0 < monthly_churn < 1):
raise ValueError("monthly_churn must be in (0, 1)")
cac = marketing_cost / new_customers
# LTV = ARPU * gross_margin * (1 / monthly_churn)
# This is the simple LTV formula assuming constant churn.
monthly_contribution = arpu * gross_margin
avg_customer_lifetime_months = 1 / monthly_churn
ltv = monthly_contribution * avg_customer_lifetime_months
ratio = ltv / cac if cac > 0 else float("inf")
payback_months = cac / monthly_contribution if monthly_contribution > 0 else float("inf")
if ratio >= 3:
health = "healthy"
elif ratio >= 1:
health = "marginal"
else:
health = "unsustainable"
return {
"cac": round(cac, 2),
"ltv": round(ltv, 2),
"ltv_cac_ratio": round(ratio, 2),
"payback_months": round(payback_months, 2),
"avg_customer_lifetime_months": round(avg_customer_lifetime_months, 2),
"monthly_contribution": round(monthly_contribution, 2),
"health": health,
"inputs": {
"marketing_cost": marketing_cost,
"new_customers": new_customers,
"arpu": arpu,
"gross_margin": gross_margin,
"monthly_churn": monthly_churn,
},
}
def verify():
"""Self-test with known values."""
# Case 1: Healthy SaaS
# CAC = 100_000 / 500 = 200
# monthly_contribution = 50 * 0.70 = 35
# lifetime = 1 / 0.05 = 20 months
# LTV = 35 * 20 = 700
# ratio = 700 / 200 = 3.5 (healthy)
# payback = 200 / 35 = 5.71 months
r = compute(100_000, 500, 50, 0.70, 0.05)
assert r["cac"] == 200.0, f"CAC: expected 200, got {r['cac']}"
assert r["ltv"] == 700.0, f"LTV: expected 700, got {r['ltv']}"
assert r["ltv_cac_ratio"] == 3.5, f"Ratio: expected 3.5, got {r['ltv_cac_ratio']}"
assert r["health"] == "healthy", f"Health: expected healthy, got {r['health']}"
assert abs(r["payback_months"] - 5.71) < 0.01, f"Payback: expected ~5.71, got {r['payback_months']}"
# Case 2: Unsustainable
r2 = compute(100_000, 500, 20, 0.50, 0.10)
# CAC = 200, monthly_contrib = 10, lifetime = 10 → LTV = 100, ratio = 0.5
assert r2["health"] == "unsustainable", f"Health: expected unsustainable, got {r2['health']}"
print("[OK] All verification tests passed")
return True
def main():
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--marketing-cost", type=float, help="Total marketing + sales spend")
parser.add_argument("--new-customers", type=int, help="New customers acquired")
parser.add_argument("--arpu", type=float, help="Average revenue per user (monthly)")
parser.add_argument("--gross-margin", type=float, help="Gross margin as decimal (0.70)")
parser.add_argument("--monthly-churn", type=float, help="Monthly churn rate as decimal (0.05)")
parser.add_argument("--input", help="Read inputs from JSON file")
parser.add_argument("--verify", action="store_true", help="Run self-tests")
args = parser.parse_args()
if args.verify:
verify()
return
if args.input:
with open(args.input) as f:
data = json.load(f)
result = compute(**data)
else:
required = [args.marketing_cost, args.new_customers, args.arpu, args.gross_margin, args.monthly_churn]
if any(x is None for x in required):
parser.error("Provide all 5 args or use --input")
result = compute(args.marketing_cost, args.new_customers, args.arpu, args.gross_margin, args.monthly_churn)
print(json.dumps(result, indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()
Related skills
FAQ
What LTV:CAC ratio is considered healthy?
At least 3:1; below 1:1 loses money on every customer, 1-3:1 is unsustainable, 3-5:1 is healthy, and above 5:1 may mean underinvesting in growth.
What costs go into CAC?
All acquisition costs: ad spend, marketing and sales salaries, tools, content production, events, and agency fees, not just ad spend.