Back to Blog

SaaS Metrics and Benchmarks in 2026: ARR, NRR, CAC/LTV, and Rule of 40

Master SaaS metrics: ARR/MRR calculation, NRR targets by stage, CAC/LTV ratios, Rule of 40, churn benchmarks, and the SQL queries to track them accurately.

Viprasol Tech Team
August 4, 2026
12 min read

SaaS Metrics and Benchmarks in 2026: ARR, NRR, CAC/LTV, and Rule of 40

SaaS metrics are a language. When a VC asks "what's your NRR?" they're asking whether your existing customers are growing faster than they're churning. When they ask about Rule of 40, they're asking whether your growth rate compensates for your burn. Getting these numbers wrong in a pitch destroys credibility; getting them right signals you understand your business.

This post covers the core SaaS metrics: how they're defined, how to calculate them accurately in SQL, what good looks like by stage, and the common mistakes that make boards distrust your numbers.


The SaaS Metrics Hierarchy

Revenue Metrics          Health Metrics         Efficiency Metrics
โ”œโ”€โ”€ ARR / MRR            โ”œโ”€โ”€ NRR / NDR          โ”œโ”€โ”€ CAC
โ”œโ”€โ”€ New ARR              โ”œโ”€โ”€ Gross Churn         โ”œโ”€โ”€ LTV
โ”œโ”€โ”€ Expansion ARR        โ”œโ”€โ”€ Net Churn           โ”œโ”€โ”€ CAC:LTV Ratio
โ”œโ”€โ”€ Churned ARR          โ”œโ”€โ”€ Logo Churn          โ”œโ”€โ”€ CAC Payback Period
โ””โ”€โ”€ Contracted ARR       โ””โ”€โ”€ Revenue Churn       โ””โ”€โ”€ Rule of 40

ARR and MRR: Getting the Definitions Right

MRR (Monthly Recurring Revenue) = sum of all recurring subscription revenue normalized to monthly.

ARR (Annual Recurring Revenue) = MRR ร— 12.

-- MRR as of a specific date
-- This query handles prorated amounts, trial periods, and cancellations
WITH active_subscriptions AS (
  SELECT
    s.id,
    s.customer_id,
    s.mrr_amount,          -- Already normalized to monthly
    s.started_at,
    s.cancelled_at,
    s.status
  FROM subscriptions s
  WHERE s.status = 'active'
    OR (s.status = 'cancelled' AND s.cancelled_at > :as_of_date)
)
SELECT
  sum(mrr_amount) AS mrr,
  sum(mrr_amount) * 12 AS arr,
  count(*) AS active_subscription_count,
  count(DISTINCT customer_id) AS paying_customer_count
FROM active_subscriptions
WHERE started_at <= :as_of_date;

-- Monthly MRR movement table (for waterfall chart)
SELECT
  date_trunc('month', event_date) AS month,
  sum(CASE WHEN event_type = 'new'       THEN mrr_delta ELSE 0 END) AS new_mrr,
  sum(CASE WHEN event_type = 'expansion' THEN mrr_delta ELSE 0 END) AS expansion_mrr,
  sum(CASE WHEN event_type = 'contraction' THEN mrr_delta ELSE 0 END) AS contraction_mrr,
  sum(CASE WHEN event_type = 'churn'     THEN mrr_delta ELSE 0 END) AS churned_mrr,
  sum(mrr_delta) AS net_new_mrr
FROM subscription_events
WHERE event_date >= now() - interval '12 months'
GROUP BY 1
ORDER BY 1;

MRR Event Types

-- Track every MRR-affecting event
CREATE TABLE subscription_events (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    customer_id UUID NOT NULL,
    sub_id      UUID NOT NULL REFERENCES subscriptions(id),
    event_type  TEXT NOT NULL CHECK (event_type IN (
                  'new',         -- First subscription
                  'expansion',   -- Upgrade or seat addition
                  'contraction', -- Downgrade or seat reduction
                  'churn',       -- Cancellation
                  'reactivation' -- Churned customer returns
                )),
    mrr_before  NUMERIC(12,2) NOT NULL,
    mrr_after   NUMERIC(12,2) NOT NULL,
    mrr_delta   NUMERIC(12,2) GENERATED ALWAYS AS (mrr_after - mrr_before) STORED,
    event_date  DATE NOT NULL DEFAULT CURRENT_DATE,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

๐Ÿš€ SaaS MVP in 8 Weeks โ€” Seriously

We have launched 50+ SaaS platforms. Multi-tenant architecture, Stripe billing, auth, role-based access, and cloud deployment โ€” all handled by one senior team.

  • Week 1โ€“2: Architecture design + wireframes
  • Week 3โ€“6: Core features built + tested
  • Week 7โ€“8: Launch-ready on AWS/Vercel with CI/CD
  • Post-launch: Maintenance plans from month 3

Net Revenue Retention (NRR)

NRR measures whether your existing customer base is growing. It's the single most important indicator of product-market fit and long-term business health.

Formula: NRR = (Starting MRR + Expansion - Contraction - Churn) / Starting MRR ร— 100

-- NRR for trailing 12 months
WITH cohort AS (
  -- Customers active at start of period
  SELECT customer_id, sum(mrr_amount) AS starting_mrr
  FROM subscriptions
  WHERE status = 'active'
    AND started_at <= now() - interval '12 months'
  GROUP BY customer_id
),
current AS (
  -- What those same customers pay today
  SELECT s.customer_id, sum(s.mrr_amount) AS current_mrr
  FROM subscriptions s
  JOIN cohort c ON c.customer_id = s.customer_id
  WHERE s.status = 'active'
  GROUP BY s.customer_id
)
SELECT
  sum(cohort.starting_mrr)                              AS starting_mrr,
  sum(COALESCE(current.current_mrr, 0))                 AS current_mrr,
  round(
    sum(COALESCE(current.current_mrr, 0)) /
    NULLIF(sum(cohort.starting_mrr), 0) * 100, 1
  )                                                     AS nrr_pct
FROM cohort
LEFT JOIN current USING (customer_id);

NRR Benchmarks by Stage

StageWeakGoodBest-in-Class
Seed / Pre-PMF<80%80โ€“95%>100%
Series A / PMF<90%100โ€“110%>120%
Series B+<100%110โ€“120%>130%
Public SMB SaaS<100%100โ€“110%>120%
Public Enterprise SaaS<110%120โ€“130%>140%

NRR >100% = growth even if you stop acquiring new customers ("negative churn"). This is what makes SaaS exponential.


Customer Acquisition Cost (CAC)

-- Blended CAC: total sales + marketing spend / new customers acquired
-- Run per quarter
WITH quarterly_spend AS (
  SELECT
    date_trunc('quarter', period_date) AS quarter,
    sum(amount) AS total_spend
  FROM marketing_spend
  WHERE category IN ('ads', 'content', 'events', 'sales_salaries', 'tools')
  GROUP BY 1
),
quarterly_new_customers AS (
  SELECT
    date_trunc('quarter', first_paid_at) AS quarter,
    count(*) AS new_customers
  FROM customers
  WHERE first_paid_at IS NOT NULL
  GROUP BY 1
)
SELECT
  s.quarter,
  s.total_spend,
  c.new_customers,
  round(s.total_spend / NULLIF(c.new_customers, 0), 2) AS blended_cac
FROM quarterly_spend s
JOIN quarterly_new_customers c USING (quarter)
ORDER BY 1 DESC;

-- Separate CAC by channel (if UTM tracking is available)
SELECT
  attribution_channel,
  count(*) AS customers,
  sum(first_month_acv) AS total_acv,
  round(sum(acquisition_cost) / NULLIF(count(*), 0), 2) AS channel_cac
FROM customer_attribution
WHERE first_paid_at >= now() - interval '12 months'
GROUP BY 1
ORDER BY channel_cac;

CAC Benchmarks by Segment

SegmentAverage CACRange
SMB (self-serve)$100โ€“$500$50โ€“$2,000
Mid-market$3,000โ€“$10,000$1,000โ€“$25,000
Enterprise$20,000โ€“$80,000$10,000โ€“$200,000+

๐Ÿ’ก The Difference Between a SaaS Demo and a SaaS Business

Anyone can build a demo. We build SaaS products that handle real load, real users, and real payments โ€” with architecture that does not need to be rewritten at 1,000 users.

  • Multi-tenant PostgreSQL with row-level security
  • Stripe subscriptions, usage billing, annual plans
  • SOC2-ready infrastructure from day one
  • We own zero equity โ€” you own everything

LTV and CAC:LTV Ratio

LTV (Customer Lifetime Value) = ARPU / Gross Churn Rate

-- LTV calculation by customer segment
WITH segment_metrics AS (
  SELECT
    CASE
      WHEN mrr_amount >= 1000 THEN 'enterprise'
      WHEN mrr_amount >= 200  THEN 'mid_market'
      ELSE                        'smb'
    END AS segment,
    count(*) AS customers,
    avg(mrr_amount) AS avg_mrr,
    -- Gross logo churn rate (monthly)
    count(*) FILTER (WHERE cancelled_at >= now() - interval '30 days') /
    NULLIF(count(*), 0)::float AS monthly_logo_churn
  FROM subscriptions
  WHERE started_at <= now() - interval '1 month'
  GROUP BY 1
)
SELECT
  segment,
  customers,
  round(avg_mrr, 2) AS avg_mrr,
  round(monthly_logo_churn * 100, 2) AS monthly_churn_pct,
  -- LTV = ARPU / monthly churn rate ร— gross margin (assume 75%)
  round(avg_mrr * 0.75 / NULLIF(monthly_logo_churn, 0), 0) AS ltv_usd,
  -- CAC:LTV (needs CAC from other source)
  NULL AS cac_to_ltv_ratio  -- Fill in from CAC table
FROM segment_metrics;

CAC:LTV Targets

RatioInterpretation
<1:1Losing money on every customer โ€” unsustainable
1:1 โ€“ 3:1Marginal; need to either cut CAC or grow LTV
3:1Healthy minimum for VC-backed SaaS
5:1 โ€“ 8:1Excellent; may be underinvesting in growth
>10:1Great product economics; aggressive growth makes sense

Rule of 40

Rule of 40 = ARR Growth Rate (%) + EBITDA Margin (%)

  • Score โ‰ฅ 40 = healthy for VC/growth stage
  • Score โ‰ฅ 60 = excellent; "Rule of 60" for elite SaaS
  • Score <40 for multiple quarters = requires attention
# Calculate Rule of 40
def rule_of_40(
    current_arr: float,
    prior_year_arr: float,
    ebitda: float,
    revenue: float,
) -> float:
    arr_growth_pct = (current_arr - prior_year_arr) / prior_year_arr * 100
    ebitda_margin_pct = ebitda / revenue * 100
    return arr_growth_pct + ebitda_margin_pct

# Examples:
# High-growth startup: 150% growth + (-80%) margin = 70 โœ…
# Profitable slow-growth: 20% growth + 25% margin = 45 โœ…
# Struggling: 15% growth + (-30%) margin = -15 โŒ

Rule of 40 Benchmarks (Public SaaS, 2026)

Company StageMedian Rule of 40Top Quartile
$1Mโ€“$10M ARR20โ€“40>60
$10Mโ€“$50M ARR30โ€“50>70
$50Mโ€“$200M ARR35โ€“55>75
$200M+ ARR40โ€“60>80

Churn Rate Calculations

-- Monthly gross logo churn rate
SELECT
  date_trunc('month', cancelled_at) AS month,
  count(*) AS churned_customers,
  -- Denominator: customers active at start of month
  (SELECT count(*)
   FROM subscriptions s2
   WHERE s2.status = 'active'
     AND s2.started_at < date_trunc('month', s.cancelled_at)
     AND (s2.cancelled_at IS NULL OR s2.cancelled_at >= date_trunc('month', s.cancelled_at))
  ) AS starting_customers,
  round(
    count(*)::numeric /
    NULLIF((SELECT count(*)
            FROM subscriptions s2
            WHERE s2.started_at < date_trunc('month', s.cancelled_at)
            AND (s2.cancelled_at IS NULL OR s2.cancelled_at >= date_trunc('month', s.cancelled_at))
           ), 0) * 100, 2
  ) AS logo_churn_rate_pct
FROM subscriptions s
WHERE cancelled_at >= now() - interval '12 months'
GROUP BY 1
ORDER BY 1;

Churn Rate Benchmarks

SegmentMonthly ChurnAnnual ChurnNotes
SMB self-serve3โ€“7%36โ€“84%High; offset by high-volume acquisition
Mid-market1โ€“2.5%12โ€“30%Important to reduce via CS
Enterprise0.25โ€“1%3โ€“12%Long contracts help; hard to recover from
Best-in-class (any)<0.5%<6%NPS promoters, strong CS

CAC Payback Period

How many months to recoup the cost of acquiring a customer:

CAC Payback = CAC / (ARPU ร— Gross Margin)
StageAcceptable PaybackGoodBest-in-Class
Self-serve<12 months<6 months<3 months
Mid-market<18 months<12 months<9 months
Enterprise<24 months<18 months<12 months

Metrics Dashboard Implementation

// src/analytics/saas-metrics.ts
export async function getSaaSMetrics(asOfDate: Date): Promise<SaaSMetrics> {
  const [mrr, nrr, mrrMovement, churnRate] = await Promise.all([
    db.query<{ mrr: number; arr: number; customer_count: number }>(MRR_QUERY, [asOfDate]),
    db.query<{ nrr_pct: number }>(NRR_QUERY),
    db.query<MRRMovement[]>(MRR_MOVEMENT_QUERY),
    db.query<{ logo_churn_rate_pct: number }>(CHURN_RATE_QUERY),
  ]);

  const mrrRow = mrr.rows[0];
  const nrrRow = nrr.rows[0];

  return {
    mrr: mrrRow.mrr,
    arr: mrrRow.arr,
    activeCustomers: mrrRow.customer_count,
    nrr: nrrRow.nrr_pct,
    monthlyChurnRate: churnRate.rows[0]?.logo_churn_rate_pct ?? 0,
    mrrMovement: mrrMovement.rows,
    computedAt: new Date(),
  };
}

Working With Viprasol

We build SaaS metrics infrastructure for growth-stage companies โ€” from clean data pipelines through investor-ready dashboards and automated reporting.

What we deliver:

  • MRR/ARR tracking with proper event sourcing (new, expansion, contraction, churn)
  • NRR calculation with cohort analysis
  • CAC and LTV attribution by channel and segment
  • Rule of 40 and unit economics dashboard
  • Automated weekly metrics email to founders and investors

โ†’ Discuss your metrics infrastructure โ†’ AI and analytics services


Share this article:

About the Author

V

Viprasol Tech Team

Custom Software Development Specialists

The Viprasol Tech team specialises in algorithmic trading software, AI agent systems, and SaaS development. With 100+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement. Based in India, serving clients globally.

MT4/MT5 EA DevelopmentAI Agent SystemsSaaS DevelopmentAlgorithmic Trading

Building a SaaS Product?

We've helped launch 50+ SaaS platforms. Let's build yours โ€” fast.

Free consultation โ€ข No commitment โ€ข Response within 24 hours

Viprasol ยท AI Agent Systems

Add AI automation to your SaaS product?

Viprasol builds custom AI agent crews that plug into any SaaS workflow โ€” automating repetitive tasks, qualifying leads, and responding across every channel your customers use.