
If you model CAC payback at the cohort level, you capture the real dynamics of churn, contraction, and expansion—so your “months to pay back” is grounded in how revenue actually unfolds over time. In this guide, you’ll build a reusable spreadsheet template (Google Sheets or Excel), fill it with a sample dataset, and ask AI to generate the formulas and optional Python code to automate the workflow.
- Time: 45–90 minutes (data prep 15–30, template build 15–25, validation 10–20, charts 5–15)
- Difficulty: Intermediate (comfortable with spreadsheets; optional Python)
- What you’ll get: A working template, a filled sample sheet you can copy, AI prompts, validation checks, and troubleshooting tips
Why bother with cohort-level payback? Because efficiency standards keep rising. Benchmarks popularized by Bessemer Venture Partners, summarized in the 2023 report, frame “good/better/best” CAC payback around roughly 12–18, 6–12, and 0–6 months respectively, with segment nuance (SMB often <12, enterprise <24) according to the BVP State of the Cloud 2023. Use these only as directional context, not rules. Also, favor gross-margin-adjusted payback; it’s closer to underlying unit economics, a point echoed in efficient growth discussions in BVP’s 2023 work and in OpenView’s SaaS Metrics 2.0 playbook (2023).
What you’ll build and what you’ll need
You’ll create a workbook with four tabs:
- Inputs: one row per acquisition cohort (e.g., 2025-01) with New_Customers, S&M_Spend, and Gross_Margin_%.
- Cohort_Monthly: monthly revenue per cohort (M0, M1, …) where M0 is the first full month after acquisition, or your chosen policy.
- Calcs: cumulative gross profit arrays and the month where each cohort crosses CAC per customer.
- Charts: visuals (cumulative vs CAC line, payback distribution).
Prerequisites:
- Source data from billing/CRM: new customers by cohort, monthly cohort revenue/MRR, churn/expansion if you have it, and gross margin.
- Google Sheets or Excel 365. If you’re on older Excel, I include a fallback.
Quick refresher: CAC payback (unit vs cohort)
- CAC payback measures how long it takes cumulative gross profit to repay CAC. Paying back in gross profit, not just revenue, better reflects efficiency (see BVP 2023 and OpenView 2023).
- We’ll group customers into cohorts by acquisition month and track revenue over time—this retains churn/expansion effects that simple unit math hides. Tools like Mixpanel describe this cohort approach in their Revenue Analytics and Cohorts docs.
- Definitions you’ll use: CAC, churn/expansion, NRR, and negative churn. For concise primers, see the ChartMogul SaaS metrics library and their notes on negative churn.
Formula we’ll implement (per cohort):
- CAC_per_Customer = S&M_Spend / New_Customers
- Cumulative_Gross_Profit_t = cumulative sum of (Monthly_Revenue_t × Gross_Margin_% or GM%_t)
- Payback_Month = the first t where Cumulative_Gross_Profit_t ≥ CAC_per_Customer
If you don’t have gross margin yet, you can compute a revenue-only payback. Just label it clearly; it’s typically optimistic vs gross-profit payback.
Prepare your data (schema + cleaning rules)
Decision points to make now:
- Cohort granularity: Use monthly cohorts for most SaaS models.
- First month definition: Either prorate partial acquisition months or start from the next full month. Pick a policy and document it.
- Revenue vs gross-profit: Prefer GM-adjusted; if not available, proceed with revenue-only and mark it as an approximation.
Minimum schema you’ll paste into the template shortly:
- Inputs tab columns: Cohort_ID (YYYY-MM), Acquisition_Date (first day of month), New_Customers, S&M_Spend, Gross_Margin_%
- Cohort_Monthly tab columns: Cohort_ID, M0_Revenue, M1_Revenue, M2_Revenue, …
Data hygiene checks before you compute:
- New_Customers > 0; S&M_Spend ≥ 0; 0% ≤ Gross_Margin_% ≤ 100%
- Monthly revenue cells are non-negative; fill missing months with 0 or leave blank if your formulas handle blanks
Build the template (Sheets or Excel)
Paste these CSV blocks into two tabs. In Google Sheets: File → Import → Upload → Insert new sheet. In Excel: Data → From Text/CSV.
- Inputs (name this tab “Inputs”):
Cohort_ID,Acquisition_Date,New_Customers,S&M_Spend,Gross_Margin_%
2025-01,2025-01-01,100,25000,75%
2025-02,2025-02-01,80,22000,75%
2025-03,2025-03-01,120,30000,75%
- Cohort_Monthly (name this tab “Cohort_Monthly”):
Cohort_ID,M0_Revenue,M1_Revenue,M2_Revenue,M3_Revenue,M4_Revenue,M5_Revenue,M6_Revenue
2025-01,8000,7600,7300,7100,6800,6600,6400
2025-02,6000,5900,5800,5700,5600,5500,5400
2025-03,9000,8700,8400,8200,7900,7700,7500
Notes:
- These are dummy numbers to illustrate churn/decay and a bit of stability. Replace with your exports when ready.
- If you have per-month GM% or COGS, add columns M0_GM%, M1_GM%, … and we’ll adapt the formulas.
Ask AI to generate your spreadsheet formulas
Pro tip: Give the AI your exact tab and column names. Paste a prompt like this and let it return formulas you can copy directly.
Prompt for Google Sheets or Excel 365:
You are helping me compute cohort CAC payback in my workbook.
- Tab Inputs has columns: Cohort_ID, Acquisition_Date, New_Customers, S&M_Spend, Gross_Margin_%.
- Tab Cohort_Monthly has columns: Cohort_ID, M0_Revenue, M1_Revenue, M2_Revenue, M3_Revenue, M4_Revenue, M5_Revenue, M6_Revenue.
Tasks:
1) On Inputs, create a column CAC_per_Customer = S&M_Spend / New_Customers.
2) On a Calcs tab, create for each cohort a row with revenue pulled from Cohort_Monthly and an array of cumulative gross profit across M0..M6 using the cohort’s Gross_Margin_%.
3) Return the first month index t where cumulative gross profit ≥ CAC_per_Customer. If never reached, return "No payback yet".
4) Also compute a blended payback weighted by New_Customers across cohorts with valid payback.
Please output formulas for Google Sheets and Excel 365 (SCAN/BYROW/LET/XMATCH versions), and a fallback for older Excel.
Below are proven patterns you can paste right now.
Google Sheets formulas (modern functions)
Docs if you want to learn the functions: Google Sheets SCAN (Docs, 2023), BYROW (Docs, 2023), and MATCH (Docs, 2024).
- On Inputs, in F1 type header CAC_per_Customer. In F2:
=IFERROR(D2/C2,)
- On Calcs!A1, set headers Cohort_ID, CAC_per_Customer, Gross_Margin_%, then month headers M0…M6, Payback_Index, Payback_Label.
- On Calcs!A2, pull the cohort list:
=FILTER(Inputs!A2:A, Inputs!A2:A<>")
- On Calcs!B2 (CAC per customer array):
=INDEX(Inputs!F2:F, MATCH(A2, Inputs!A2:A, 0))
- On Calcs!C2 (GM% as decimal):
=VALUE(SUBSTITUTE(INDEX(Inputs!E2:E, MATCH(A2, Inputs!A2:A, 0)), "%", ""))/100
- On Calcs!D2:J2 (bring revenue M0..M6): select range D2:J2, then array-enter:
=INDEX(Cohort_Monthly!B2:H, MATCH(A2, Cohort_Monthly!A2:A, 0), )
- On Calcs!K2 (cumulative gross profit across D2:J2):
=SCAN(0, D2:J2*C2, LAMBDA(acc, val, acc+val))
This returns an array spilling across columns K2:Q2. Label K1:Q1 as Cum_GP_M0 … Cum_GP_M6.
- On Calcs!R2 (first month index where cumulative ≥ CAC):
=IFERROR(MATCH(TRUE, K2:Q2>=B2, 0)-1, "No payback yet")
We subtract 1 to convert from 1-based index to zero-based month labels (M0..M6). If you prefer month names, wrap with INDEX over headers.
- On Calcs!S2 (human-friendly label):
=IF(ISNUMBER(R2), "M"&R2, R2)
-
Fill A2:S2 down for as many cohorts as you have.
-
Blended payback (e.g., on Calcs!U2) weighted by New_Customers across cohorts with numeric payback:
=LET(
ids, A2:A,
pb, R2:R,
w, MAP(ids, LAMBDA(id, IF(id="",, INDEX(Inputs!C2:C, MATCH(id, Inputs!A2:A, 0))))),
num_mask, ISNUMBER(pb),
SUM(FILTER(pb*w, num_mask)) / SUM(FILTER(w, num_mask))
)
If you don’t have LET/MAP in your version of Sheets, you can compute the weighted average in a helper table instead.
Microsoft Excel 365 formulas (dynamic arrays)
Function docs: SCAN (Microsoft, 2024), BYROW (Microsoft, 2024), XMATCH (Microsoft, 2024), LET (Microsoft, 2024).
- Inputs!F2 (CAC per customer):
=IFERROR(D2/C2,"")
- Calcs!A2 (cohort list):
=FILTER(Inputs!A2:A, Inputs!A2:A<>")
- Calcs!B2 (CAC per customer lookup):
=INDEX(Inputs!F:F, XMATCH(A2, Inputs!A:A, 0))
- Calcs!C2 (GM% as decimal):
=SUBSTITUTE(INDEX(Inputs!E:E, XMATCH(A2, Inputs!A:A, 0)),"%","")/100
- Calcs!D2:J2 (revenue M0..M6): select D2:J2 and enter:
=INDEX(Cohort_Monthly!B:H, XMATCH(A2, Cohort_Monthly!A:A, 0), )
- Calcs!K2:Q2 (cumulative GP):
=SCAN(0, D2:J2*C2, LAMBDA(a,v, a+v))
- Calcs!R2 (first month index):
=LET(cum, K2:Q2, thr, B2, res, XMATCH(TRUE, cum>=thr, 0), IFERROR(res-1, "No payback yet"))
- Calcs!S2 (label):
=IF(ISNUMBER(R2), "M"&R2, R2)
- Blended payback (Calcs!U2):
=LET(
ids, A2:A1000,
pb, R2:R1000,
w, MAP(ids, LAMBDA(id, IF(id="",, INDEX(Inputs!C:C, XMATCH(id, Inputs!A:A, 0))))),
mask, ISNUMBER(pb),
SUM(FILTER(pb*w, mask))/SUM(FILTER(w, mask))
)
Older Excel fallback (no dynamic arrays)
- Compute CAC_per_Customer with =IFERROR(D2/C2,"") and copy down.
- Use helper columns for cumulative GP: in K2 enter =D2*$C2, in L2 enter =K2+E2*$C2, in M2 enter =L2+F2*$C2, etc.
- First month index: use =MATCH(TRUE, K2:Q2>=$B2, 0)-1 as a CSE (Ctrl+Shift+Enter) array formula; if error, show "No payback yet" with IFERROR.
- Weighted average: do it in a summary table with SUMPRODUCT over numeric paybacks and weights.
Compute cohort payback and blended payback
- For each cohort row on Calcs, read the Payback_Index S2. If it reads “M5”, that means the cumulative gross profit crosses CAC per customer in month 5 (sixth observed month). If it says “No payback yet,” keep monitoring or extend your window.
- The blended payback expresses your recent efficiency across cohorts, weighted by cohort size. This is useful for board updates, but don’t let it mask outliers; the distribution often tells the real story.
Interpretation notes with benchmarks: As a directional frame, many operators consider 12–18 months “good,” 6–12 “better,” 0–6 “best,” with segment variation—see BVP State of the Cloud 2023. Pair payback with efficiency metrics like net new ARR-to-burn as suggested by OpenView’s 2023 playbook.
Validate and troubleshoot (read this before sharing numbers)
Data sanity checks:
- New_Customers > 0; S&M_Spend ≥ 0; 0% ≤ GM% ≤ 100%
- Monthly revenue is non-negative; detect missing months; be careful with the current, partial month
Logic checks:
- Cumulative gross profit must be non-decreasing. If it drops, you may have refunds or negative adjustments—investigate.
- Heavy expansion can create faster payback. That’s fine; document the driver so stakeholders understand the shift. For context on churn/expansion dynamics, see ChartMogul’s notes on negative churn.
- Some cohorts will not pay back within the observed window. Leave them as “No payback yet” rather than forcing an estimate.
Reconciliation:
- Sum the M0 revenue across cohorts and compare to “new-logo MRR” for the same period in your billing/reporting system. Small timing differences are normal; big gaps need review. Mixpanel’s revenue analytics overview is a good reference for how different systems define cohort revenue.
Common pitfalls and fixes:
- GM% missing: Temporarily set GM% to a reasonable estimate and flag the tab with a warning; recompute with true GM% once finance closes the month.
- Zero customers in a cohort: Exclude from weighted averages or mark invalid; avoid divide-by-zero.
- Usage-based plans: Normalize revenue to monthly; document how you handle seasonality or delayed realization.
- Mixed first-month policy: Pick one rule (prorate vs next full month) and apply consistently; otherwise you’ll skew M0 vs later months.
Visualize and interpret results
- Per cohort, plot a line chart of Cumulative Gross Profit (K:Q) and a flat CAC per customer threshold. The first crossing is your payback point. Keep a consistent y-axis across cohorts for quick scans.
- Build a histogram of Payback_Index across cohorts to see your distribution. If many sit at >M18, look for acquisition channel issues, pricing, or onboarding frictions.
- Track blended payback month over time as a KPI. Annotate major pricing or channel changes.
Automate with Python (optional)
If you prefer a scriptable pipeline, use pandas to compute payback per cohort and a blended average. Reference docs: pandas groupby and Series.cumsum.
Example script you can adapt (save as payback.py):
import pandas as pd
# inputs.csv columns: Cohort_ID,New_Customers,S&M_Spend,Gross_Margin_%
# cohort_monthly.csv columns: Cohort_ID,month_index,revenue
def load_inputs(path):
df = pd.read_csv(path)
# normalize GM% like "75%" to 0.75
if 'Gross_Margin_%' in df.columns:
df['GM_dec'] = (
df['Gross_Margin_%']
.astype(str)
.str.replace('%','', regex=False)
.astype(float) / 100.0
)
else:
df['GM_dec'] = 1.0 # revenue-only fallback
df['CAC_per_Customer'] = df['S&M_Spend'] / df['New_Customers']
return df
def compute_payback(df_inputs, df_monthly):
df = df_monthly.merge(
df_inputs[['Cohort_ID','CAC_per_Customer','GM_dec','New_Customers']],
on='Cohort_ID', how='left'
)
df = df.sort_values(['Cohort_ID','month_index'])
df['gross_profit'] = df['revenue'] * df['GM_dec']
df['cum_profit'] = df.groupby('Cohort_ID')['gross_profit'].cumsum()
df['met'] = df['cum_profit'] >= df['CAC_per_Customer']
first_met = (
df[df['met']]
.groupby('Cohort_ID')['month_index']
.min()
.rename('payback_month')
.reset_index()
)
out = df_inputs.merge(first_met, on='Cohort_ID', how='left')
# blended payback weighted by New_Customers, excluding NaN paybacks
mask = out['payback_month'].notna() & (out['New_Customers'] > 0)
blended = (out.loc[mask, 'payback_month'] * out.loc[mask, 'New_Customers']).sum() / out.loc[mask, 'New_Customers'].sum()
return out, blended
if __name__ == '__main__':
inputs = load_inputs('inputs.csv')
monthly = pd.read_csv('cohort_monthly.csv')
results, blended = compute_payback(inputs, monthly)
print(results[['Cohort_ID','CAC_per_Customer','payback_month']])
print('\nBlended payback (months):', round(blended, 2))
AI prompt for code generation:
I have two CSVs: inputs.csv (Cohort_ID, New_Customers, S&M_Spend, Gross_Margin_%) and cohort_monthly.csv (Cohort_ID, month_index, revenue). Generate a pandas script that computes CAC_per_Customer, cumulative gross profit per cohort, the first month where cum_profit >= CAC_per_Customer (payback_month), and a blended payback weighted by New_Customers. Handle 0 customers, missing months, and GM% strings like "75%". Output a clean table and the blended average.
Scheduling tips:
- Run monthly after your books close; write to a shared drive or BI warehouse.
- Keep a log of any data policy changes (e.g., first-month proration).
Tools/Stack (neutral toolbox)
- Google Sheets or Microsoft Excel — Flexible modeling with shareable templates; modern functions (SCAN/BYROW/MATCH/XMATCH) simplify cohort arrays. See Google’s SCAN function docs and Microsoft’s SCAN docs.
- Python (pandas) — Reproducible pipelines for monthly refreshes; pairs well with CSV or warehouse exports. See pandas groupby.
- Mixpanel or Amplitude — Build behavioral/revenue cohorts and export the monthly tables you need. See Mixpanel’s Revenue Analytics guide.
- ChartMogul or Baremetrics — Subscription analytics and exports to power your Inputs and Cohort_Monthly tabs. Start with the ChartMogul SaaS metrics library.
- WarpDriven — Disclosure: WarpDriven is our product. Its AI-first ERP can unify order, finance, and product data, which you can export as revenue and cost inputs for cohort payback analysis.
We list multiple peer tools for parity; pick whichever aligns with your stack and data governance.
Download links: template and sample sheet
We included copyable CSV blocks above. If you want a jump-start:
- Create a blank Google Sheet or Excel workbook.
- Import the two CSV blocks into tabs named Inputs and Cohort_Monthly.
- Add a Calcs tab and paste the formulas from the “Ask AI to generate your spreadsheet formulas” section.
- You now have both a template and a filled sample sheet. Replace sample data with your exports to go live.
Next steps
- Add more months (M7, M8, …) as you collect data. The cumulative formulas will extend naturally.
- If you track expansion and churn separately, build diagnostic charts to explain shifts in payback.
- Compare revenue-only vs gross-profit-adjusted payback to quantify optimism bias; drive conversations about margin improvements.
- Incorporate payback distribution tracking into your monthly operating review.
Further reading and references:
- Directional benchmarks and efficient growth framing: BVP State of the Cloud 2023
- PLG-aware efficiency metrics: OpenView SaaS Metrics 2.0 (2023)
- Definitions and churn dynamics: ChartMogul SaaS metrics library and negative churn explainer
- Cohorts and revenue analytics concepts: Mixpanel Revenue Analytics
- Function references: Google Sheets SCAN, Excel SCAN, and pandas groupby