Metric

What is expansion revenue rate?

Definition

Expansion revenue rate is the new recurring revenue generated inside the existing customer base — upsell, cross-sell, and seat growth — divided by the MRR that base started the period with. It is the growth component inside net revenue retention, isolated. Report expansion and contraction side by side, never netted: a base where heavy expansion masks equally heavy downgrades is a very different business from a quiet one, and a single net number cannot tell them apart.

Formula: Expansion revenue rate = expansion MRR (upsell + cross-sell + seat growth) / starting MRR × 100, per period

What data do you need?

  • A subscription-change history with one row per MRR delta, typed as upsell, cross-sell, seat change, downgrade, or churn — current MRR alone cannot produce this metric
  • A starting-MRR snapshot per period, written by a scheduled job
  • Change effective dates, so a delta lands in the period the revenue actually moves
  • Subscription start dates, so expansion can be cohorted by customer age (time to first expansion)
  • A stable account key that survives plan changes and entity renames

SQL pattern

Monthly expansion rate with contraction split (PostgreSQL)PostgreSQL
-- subscription_changes has one row per MRR delta with a change_type.
-- Expansion and contraction are reported side by side, never netted.
WITH months AS (
  SELECT generate_series(
    date_trunc('month', CURRENT_DATE - INTERVAL '12 months'),
    date_trunc('month', CURRENT_DATE - INTERVAL '1 month'),
    INTERVAL '1 month'
  )::date AS month
), starting AS (
  -- Snapshot written on the first of each month; the denominator
  -- must not drift as the month plays out.
  SELECT snapshot_month AS month, SUM(mrr_usd) AS starting_mrr
  FROM mrr_snapshots
  GROUP BY 1
), changes AS (
  SELECT
    date_trunc('month', effective_at)::date AS month,
    SUM(mrr_delta_usd) FILTER (
      WHERE change_type IN ('upsell', 'cross_sell', 'seat_expansion')
    ) AS expansion_mrr,
    SUM(-mrr_delta_usd) FILTER (
      WHERE change_type IN ('downgrade', 'seat_reduction')
    ) AS contraction_mrr,
    COUNT(DISTINCT account_id) FILTER (
      WHERE change_type IN ('upsell', 'cross_sell', 'seat_expansion')
    ) AS expanding_accounts
  FROM subscription_changes
  GROUP BY 1
)
SELECT
  m.month,
  ROUND(st.starting_mrr, 2) AS starting_mrr,
  ROUND(COALESCE(c.expansion_mrr, 0), 2) AS expansion_mrr,
  ROUND(COALESCE(c.contraction_mrr, 0), 2) AS contraction_mrr,
  ROUND(
    100.0 * COALESCE(c.expansion_mrr, 0)
    / NULLIF(st.starting_mrr, 0), 2
  ) AS expansion_rate_pct,
  ROUND(
    100.0 * COALESCE(c.contraction_mrr, 0)
    / NULLIF(st.starting_mrr, 0), 2
  ) AS contraction_rate_pct,
  COALESCE(c.expanding_accounts, 0) AS expanding_accounts
FROM months m
JOIN starting st USING (month)
LEFT JOIN changes c USING (month)
ORDER BY m.month;

Common pitfalls

Netting contraction against expansion and reporting the remainder.→ Show both rates on the same card. +8% expansion with −6% contraction and +2% expansion with 0% contraction net to similar numbers and demand opposite responses.
Deriving expansion by diffing this month's MRR against last month's per account.→ A snapshot diff collapses an upsell, a seat reduction, and a plan swap in the same month into one net delta. You need a change-event model — one typed row per movement — which is why this metric requires subscription history, not just current MRR.
Leaving it undefined whether contractual price increases count as expansion.→ Decide once and write it down. A CPI uplift applied to the whole base is not the same signal as a customer choosing to buy more seats, and mixing them makes the rate move on invoice mechanics.
Reporting the rate without saying how many accounts produced it.→ Track expanding accounts alongside expansion MRR. Two whales can carry the rate for quarters while the rest of the base sits still — concentration is the risk the average hides.

Where does this metric apply?

This metric commonly uses data from Stripe, Chargebee, Zuora, Maxio, Gainsight, plus any warehouse models built on account, subscription, and product-usage tables at the same grain.

Dashboards

Integrations

Metrics

Analytics

FAQ

How does expansion revenue rate relate to NRR?
NRR is the composite: starting revenue minus churn and contraction plus expansion. Expansion revenue rate isolates the last term, so you can see whether a strong NRR is genuinely low churn or heavy expansion papering over a leak. If NRR is the headline, this is the decomposition that makes it actionable.
Which accounts should you expect to expand?
Cohort it two ways. By customer age: median time to first expansion tells you when the motion realistically starts, so CS is not pushed to upsell accounts still onboarding. And by account health: pair the rate with your health score, because expansion concentrated in low-health accounts is usually discount-driven and tends to come back as contraction.