Subscription Renewal Rate
Also known as: Renewal Rate, Subscription Retention Rate, Subscription Continuation Rate, Term Renewal Rate
Subscription Renewal Rate is the percentage of paid subscription terms due for renewal in a period that were successfully charged for the next billing term, including renewals recovered within the payment grace (dunning) window. It is the leading retention signal for subscription commerce (replenishment, subscribe-and-save and paid membership programmes): a decline isolates voluntary cancellations or failed payments before they surface as lost recurring revenue.
Business Use Case
1️⃣ The Retention Marketing team reviews the monthly rate by plan and billing frequency and, when a cohort drops more than 3 points month over month, launches pre-renewal reminder and save-offer campaigns for the terms due in the next 30 days. 2️⃣ The Finance team applies the trailing 3-month rate to the terms due in the next quarter to forecast renewal revenue and recurring revenue in the budget. 3️⃣ The Payments / Revenue Operations team watches the involuntary non-renewal share and, when it rises, tunes dunning retry schedules and card-updater settings. 4️⃣ The Merchandising team compares the rate across subscription products and pack sizes and changes delivery cadence or default quantities for products with below-average renewal.
Business Criticality
High
Journey / Navigation Area
Monetization
Owning Function
Marketing
Scope
User
Formula
Calculation Notes
1️⃣ Denominator = every paid subscription term whose end (renewal due) date falls in the reporting period, including subscriptions the customer cancelled before the due date; those count as non-renewals and are never removed from the base. 2️⃣ Numerator = due terms with a paid renewal invoice (billing_reason = subscription_cycle or subscription_update) covering the next term (invoice period start = due date), paid from 1 day before the due date up to the end of the 7-day payment grace (dunning) window. 3️⃣ Report a period only after its last due date plus the grace window has passed; otherwise late recoveries are missing and the rate is understated. 4️⃣ Exclude free-trial terms (trial-to-paid conversion is a separate metric), test subscriptions and merchant-initiated terminations (fraud, policy); keep paused subscriptions out of the base until they resume. 5️⃣ Plan upgrades, downgrades and billing-interval switches at renewal count as renewed; count each subscription term once, even when a customer holds several subscriptions. The metric is term-weighted; revenue-weighted renewal (renewed recurring revenue / recurring revenue due) is reported separately and never blended into this figure. 6️⃣ Break out voluntary non-renewals (customer cancellation) and involuntary ones (payment failure after dunning) because they need different fixes; do not compare billing frequencies in one blended number, since a monthly and an annual rate describe very different exposure windows. 7️⃣ Value action by industry: E-commerce / Retail = paid replenishment or subscribe-and-save order invoice for the next cycle; Membership retail = paid annual or monthly membership fee; SaaS = paid invoice for the next contract or billing term; Media = successful recurring charge for the next access period.
Technical Details
Events
SQL Queries
Google Analytics 4 (BigQuery)
-- ==========================================
-- 1. GOOGLE BIGQUERY SQL
-- ==========================================
-- Subscription Renewal Rate by renewal month from native billing tables.
-- Base: every paid term due in the month (cancelled-before-due terms stay in as non-renewals).
-- Table and column names (subscription_periods, subscriptions, invoices; is_trial, is_paused, is_test,
-- termination_reason, billing_reason, period_start, paid_at) are a normalized billing schema: map them to your source (e.g. Stripe, Recharge).
WITH due_terms AS (
SELECT
p.subscription_id,
p.period_end,
DATE_TRUNC(DATE(p.period_end), MONTH) AS renewal_month
FROM `your-project.billing.subscription_periods` AS p
JOIN `your-project.billing.subscriptions` AS s
ON s.subscription_id = p.subscription_id
WHERE p.is_trial = FALSE
AND COALESCE(p.is_paused, FALSE) = FALSE -- paused at the due date: out of the base until it resumes
AND s.is_test = FALSE
AND COALESCE(s.termination_reason, '') NOT IN ('fraud', 'merchant_terminated')
AND p.period_end >= TIMESTAMP(DATE_TRUNC(DATE_SUB(CURRENT_DATE(), INTERVAL 12 MONTH), MONTH))
-- whole renewal months only, reported once the last due date plus the 7-day grace window has passed
AND DATE(p.period_end) < DATE_TRUNC(DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY), MONTH)
),
renewed_terms AS (
SELECT DISTINCT d.subscription_id, d.period_end
FROM due_terms AS d
JOIN `your-project.billing.invoices` AS i
ON i.subscription_id = d.subscription_id
AND i.billing_reason IN ('subscription_cycle', 'subscription_update') -- plan / interval changes at renewal count as renewed
AND i.period_start = d.period_end -- invoice covers the next term
AND i.status = 'paid'
AND i.paid_at >= TIMESTAMP_SUB(d.period_end, INTERVAL 1 DAY)
AND i.paid_at < TIMESTAMP_ADD(d.period_end, INTERVAL 7 DAY)
)
SELECT
d.renewal_month,
COUNT(*) AS terms_due_for_renewal,
COUNT(r.subscription_id) AS terms_renewed,
ROUND(100 * SAFE_DIVIDE(COUNT(r.subscription_id), COUNT(*)), 2) AS subscription_renewal_rate_pct
FROM due_terms AS d
LEFT JOIN renewed_terms AS r
ON r.subscription_id = d.subscription_id
AND r.period_end = d.period_end
GROUP BY d.renewal_month
ORDER BY d.renewal_month;Adobe Experience Platform (AEP Query Service)
-- ==========================================
-- 2. ADOBE AEP QUERY SERVICE SQL
-- ==========================================
-- Subscription Renewal Rate by renewal month from billing datasets ingested into AEP.
-- Base: every paid term due in the month (cancelled-before-due terms stay in as non-renewals).
-- Table and column names (billing_subscription_periods, billing_subscriptions, billing_invoices; is_trial, is_paused, is_test,
-- termination_reason, billing_reason, period_start, paid_at) are a normalized billing schema: map them to your source (e.g. Stripe, Recharge).
WITH due_terms AS (
SELECT
p.subscription_id,
p.period_end,
DATE_TRUNC('month', p.period_end) AS renewal_month
FROM billing_subscription_periods AS p
JOIN billing_subscriptions AS s
ON s.subscription_id = p.subscription_id
WHERE p.is_trial = FALSE
AND COALESCE(p.is_paused, FALSE) = FALSE -- paused at the due date: out of the base until it resumes
AND s.is_test = FALSE
AND COALESCE(s.termination_reason, '') NOT IN ('fraud', 'merchant_terminated')
AND p.period_end >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '12' MONTH)
-- whole renewal months only, reported once the last due date plus the 7-day grace window has passed
AND p.period_end < DATE_TRUNC('month', CURRENT_DATE - INTERVAL '7' DAY)
),
renewed_terms AS (
SELECT DISTINCT d.subscription_id, d.period_end
FROM due_terms AS d
JOIN billing_invoices AS i
ON i.subscription_id = d.subscription_id
AND i.billing_reason IN ('subscription_cycle', 'subscription_update') -- plan / interval changes at renewal count as renewed
AND i.period_start = d.period_end -- invoice covers the next term
AND i.status = 'paid'
AND i.paid_at >= d.period_end - INTERVAL '1' DAY
AND i.paid_at < d.period_end + INTERVAL '7' DAY
)
SELECT
d.renewal_month,
COUNT(*) AS terms_due_for_renewal,
COUNT(r.subscription_id) AS terms_renewed,
ROUND(100.0 * COUNT(r.subscription_id) / NULLIF(COUNT(*), 0), 2) AS subscription_renewal_rate_pct
FROM due_terms AS d
LEFT JOIN renewed_terms AS r
ON r.subscription_id = d.subscription_id
AND r.period_end = d.period_end
GROUP BY d.renewal_month
ORDER BY d.renewal_month;Source Data
Backend Billing (normalized subscription periods and invoices model, e.g. modelled from Stripe / Recharge billing data)
Dependencies
Prerequisite: List the dependencies required for this Metric
Events:
Dimensions:
Report Attributes
Subscription Plan Billing Frequency Renewal Cycle Number Acquisition Cohort Payment Method Country
Recommended Reporting Views
Segment Eligibility
true
Governance
Derived KPIs
Governance
GitHub
catalog/metrics/subscription-renewal-rate.yml