Metric

What is gross revenue retention (GRR)?

Definition

Gross revenue retention is the share of recurring revenue from an existing cohort of customers that survives a period, counting churn and downgrades but never expansion. Because the numerator can only shrink, GRR is capped at 100% — it measures how leaky the bucket is, with none of the credit for pouring more water in.

Formula: GRR = (starting ARR − churned ARR − downgrade ARR) / starting ARR × 100

What data do you need?

  • Subscription records with ARR or MRR and effective date ranges
  • A cohort definition: customers active at the start of the period
  • Churn and downgrade events dated to when they take revenue effect
  • Expansion amounts identified separately so they can be excluded
  • A stable account key that survives renewals, upgrades, and entity renames

SQL pattern

Quarterly GRR from subscription ARR (PostgreSQL)PostgreSQL
-- Expansion is deliberately excluded: GRR is capped at 100% by design.
WITH periods AS (
  SELECT
    d::date AS period_start,
    (d + INTERVAL '3 months')::date AS period_end
  FROM generate_series(
    date_trunc('quarter', CURRENT_DATE) - INTERVAL '2 years',
    date_trunc('quarter', CURRENT_DATE) - INTERVAL '3 months',
    INTERVAL '3 months'
  ) AS d
), starting AS (
  SELECT
    p.period_start,
    s.account_id,
    SUM(s.arr_usd) AS start_arr
  FROM periods p
  JOIN subscriptions s
    ON s.started_at < p.period_start
   AND (s.ended_at IS NULL OR s.ended_at >= p.period_start)
  GROUP BY 1, 2
), ending AS (
  SELECT
    p.period_start,
    s.account_id,
    SUM(s.arr_usd) AS end_arr
  FROM periods p
  JOIN subscriptions s
    ON s.started_at < p.period_end
   AND (s.ended_at IS NULL OR s.ended_at >= p.period_end)
  GROUP BY 1, 2
)
SELECT
  st.period_start,
  ROUND(SUM(st.start_arr), 2) AS starting_arr,
  ROUND(SUM(LEAST(COALESCE(en.end_arr, 0), st.start_arr)), 2)
    AS retained_arr,
  ROUND(
    100.0 * SUM(LEAST(COALESCE(en.end_arr, 0), st.start_arr))
    / NULLIF(SUM(st.start_arr), 0), 1
  ) AS grr_pct
FROM starting st
LEFT JOIN ending en
  ON en.period_start = st.period_start
 AND en.account_id = st.account_id
GROUP BY st.period_start
ORDER BY st.period_start;

Common pitfalls

Letting expansion leak into GRR and reporting a number above 100%.→ Cap each account's contribution at its starting ARR — the LEAST() above. A GRR over 100% means expansion got in, and expansion belongs in net revenue retention instead.
Including new customers acquired during the period.→ GRR is a fixed cohort: only accounts that were paying on day one count in both the numerator and the denominator.
Dating churn to the cancellation click.→ Use the date the revenue actually stops — usually the end of the paid term. A notice given in January for a March expiry is Q1 churn, not January churn.
Computing GRR from CS platform fields.→ CS tools mirror CRM values that people edit by hand. Compute retention from billing or subscription tables and reconcile the CS view against them.

Where does this metric apply?

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

Dashboards

Integrations

Metrics

Analytics

FAQ

How is GRR different from NRR?
GRR excludes expansion and is capped at 100%; NRR includes upsell, cross-sell, and price increases and can exceed 100%. Report both: strong NRR can hide bad GRR when a handful of expanding accounts mask a churning long tail.
What GRR is considered healthy?
It depends on segment far more than on any industry benchmark — enterprise contracts with annual terms retain very differently from self-serve monthly plans. Segment the metric by plan and contract length before comparing yourself to anyone.