ds@mcpanalytics.aiEmail your data and your question and get an answer about your own data, with the method shown.Email your data and question. Get an answer.

Stripe MRR Tutorial: Calculate Current MRR, Churn and Plan Mix From Your Data

By MCP Analytics Team | Published , updated

Master Monthly Recurring Revenue analysis to understand subscription growth, customer retention, and revenue trends in your Stripe account.

Introduction to MRR Analysis

If you're running a subscription-based business with Stripe, one question keeps you up at night: "What is my MRR?" Monthly Recurring Revenue (MRR) is the lifeblood of subscription businesses, providing a clear picture of predictable income and business health.

To see how many of your own customers churn and what separates the ones who leave, try the free churn analysis tool.

Unlike one-time sales, MRR represents the normalized monthly value of all your active subscriptions. It's the single most important metric for understanding whether your business is growing, stagnating, or declining. This tutorial will walk you through analyzing your MRR in Stripe, answering critical questions like:

By the end of this guide, you will know how to calculate MRR, active subscriptions, customer churn and plan mix from Stripe data, and what each of those numbers leaves out.

Steps at a Glance

Start with the prerequisites (Stripe access, API keys, how much history you need), then work through the steps:

  1. Step 1: What is my current MRR?
  2. Step 2: How many active subscriptions do I have?
  3. Step 3: What is my churn rate?
  4. Step 4: Which plans are most popular?

The small worked example below runs on its own, with no Stripe account, and shows the arithmetic behind Steps 1 and 3.

Worked Example: MRR and Churn for 20 Customers

Question: What is monthly recurring revenue from fixed monthly prices at month end, and what share of the starting customers was lost?

Inputs: Simulated data, seed 42. Each customer has one fixed monthly price: $20, $40 or $60. The script marks each customer as active or not at the start and end of the month. The outputs in Steps 1 to 4 below come from a different, larger synthetic set, not from this script.

Method: Add the prices of customers active at month end. Divide the customers lost from the starting group by the size of that group. If the starting group is empty, churn is undefined.

import random

SEED = 42
random.seed(SEED)

# Each row is one simulated customer with a fixed monthly price.
rows = []
for customer_id in range(1, 21):
    monthly_price = random.choice((20, 40, 60))
    active_at_start = random.random() >= 0.20
    canceled_in_month = active_at_start and random.random() < 0.15
    active_at_end = active_at_start and not canceled_in_month
    rows.append((customer_id, monthly_price,
                 active_at_start, active_at_end))

opening = [row for row in rows if row[2]]
lost = [row for row in opening if not row[3]]
closing = [row for row in rows if row[3]]
mrr = sum(row[1] for row in closing)
churn = (len(lost) / len(opening) * 100) if opening else None

print(f"Simulated data, seed {SEED}")
print("Inputs: 20 customers; each has one fixed monthly price "
      "chosen from $20, $40, or $60.")
print("Method: sum closing monthly prices; churn is lost "
      "opening customers divided by opening customers.")
print(f"Customers at start: {len(opening)}")
print(f"Customers lost: {len(lost)}")
print(f"Customers at end: {len(closing)}")
print(f"Gross fixed-price MRR at end: ${mrr:.2f}")
if churn is not None:
    print(f"Monthly customer churn: {churn:.2f}%")
else:
    print("Monthly customer churn: undefined "
          "(empty opening group)")

# Built in: 15% of starting customers cancel.
# How far can a 20-customer month land from that?
rates = []
for s in range(1000):
    random.seed(s)
    start_n = lost_n = 0
    for _ in range(20):
        random.choice((20, 40, 60))
        a = random.random() >= 0.20
        c = a and random.random() < 0.15
        start_n += a
        lost_n += c
    if start_n:
        rates.append(lost_n / start_n * 100)
rates.sort()
print("Built in: 15% of starting customers cancel. Across 1000 "
      "reruns of this 20-customer month, the printed rate ran "
      f"from {rates[50]:.1f}% to {rates[949]:.1f}% "
      "(middle 90%)")

Output of this script:

Simulated data, seed 42
Inputs: 20 customers; each has one fixed monthly price chosen from $20, $40, or $60.
Method: sum closing monthly prices; churn is lost opening customers divided by opening customers.
Customers at start: 14
Customers lost: 1
Customers at end: 13
Gross fixed-price MRR at end: $440.00
Monthly customer churn: 7.14%
Built in: 15% of starting customers cancel. Across 1000 reruns of this 20-customer month, the printed rate ran from 0.0% to 31.6% (middle 90%)

Result: The run prints gross fixed-price MRR of $440.00 (the sum of the 13 closing prices) and a 7.14% monthly loss rate, which is 1 of 14 starting customers. The script gives each starting customer a 15% chance to cancel, and across 1,000 reruns of this 20-customer month the printed rate ran from 0.0% to 31.6% (middle 90%), so one month of this size gives an imprecise estimate of churn.

Limit: This small made-up set has no price cuts, taxes, or prices billed on other plans. It does not show how a real Stripe business handles them.

Sources

Prerequisites and Data Requirements

Before diving into MRR analysis, ensure you have the following in place:

Required Access

Recommended Timeline

Technical Knowledge

What You'll Accomplish

This tutorial guides you through a complete MRR analysis workflow. You'll learn to:

  1. Calculate your current MRR from Stripe subscription data
  2. Track the number of active subscriptions over time
  3. Compute your monthly churn rate
  4. Identify which pricing plans contribute most to revenue
  5. Visualize trends and make strategic decisions

Each step includes code and the output it prints. The outputs in Steps 1 to 4 come from a synthetic Stripe subscription set generated for this tutorial, with annual, quarterly, multi-item, multi-seat, non-USD, metered, trialing and canceled subscriptions in it. It is not a real business, and your own numbers will differ.

Step 1: What Is My Current MRR?

MRR is the sum of all recurring revenue normalized to a monthly amount. This means annual subscriptions are divided by 12, quarterly by 3, and monthly subscriptions are counted as-is.

Using the Stripe Dashboard

Stripe's menu names change, so check Stripe's current help for where your dashboard shows subscriptions and any MRR figure, and note how it defines MRR before comparing it with the code below. Sources above link where Stripe documents subscriptions.

Calculating MRR Manually

For a deeper understanding or custom analysis, you can calculate MRR using the Stripe API:

import stripe
from datetime import datetime, timezone

stripe.api_key = 'your_secret_key'

DAYS_PER_MONTH = 365 / 12

def monthly_cents(item):
    """Monthly value of one subscription item in minor units, or None if it cannot be computed from unit_amount."""
    price = item['price']
    rec = price['recurring']
    # metered prices bill on usage, so quantity is not a fixed count
    if rec['usage_type'] == 'metered':
        return None
    if price['unit_amount'] is None:          # tiered price: no fixed amount
        return None
    months = {'month': 1, 'year': 12, 'week': 7 / DAYS_PER_MONTH, 'day': 1 / DAYS_PER_MONTH}[rec['interval']] * rec['interval_count']
    return price['unit_amount'] * item['quantity'] / months

# status='active' returns active subscriptions only; auto_paging_iter() walks every page
active = list(stripe.Subscription.list(status='active', limit=100).auto_paging_iter())

total_cents, skipped = 0, 0
for sub in active:
    items = sub['items']['data']
    if sub['items']['has_more']:
        # the embedded list is only the first page: fetch every item
        items = list(stripe.SubscriptionItem.list(
            subscription=sub['id'], limit=100).auto_paging_iter())
    for item in items:
        if item['price']['currency'] != 'usd' or monthly_cents(item) is None:
            skipped += 1                      # other currency, metered or tiered: handle separately
            continue
        total_cents += monthly_cents(item)

total_mrr = total_cents / 100
print(f"Current MRR: ${total_mrr:,.2f}")
print(f"Items skipped (not USD, or no fixed unit amount): {skipped}")

Expected Output

Current MRR: $9,675.00
Items skipped (not USD, or no fixed unit amount): 3

Understanding the Result

The result is gross fixed-price MRR before price cuts: monthly subscription revenue at the listed prices. In this synthetic example it is $9,675.00, which would repeat each month if nothing changes. The code counts every item on a subscription, multiplies by quantity, and divides by the number of months in the billing period (so a price billed every 3 months is divided by 3). It counts only subscriptions with status active: trialing and past_due subscriptions are left out, and so are the 3 items priced in another currency or without a fixed unit amount (recurring prices with usage type metered are excluded explicitly, and tiered prices have no unit amount). The example does not add a metered row, so its printed output is unchanged. The script reads the items embedded in each subscription; when an item list has has_more set to true, page through that subscription's items, as the script's check reminds you. Decide how to treat those before you quote a single MRR figure. Stripe's list call returns one page at a time and, by default, leaves out canceled subscriptions, which is why the code pages through the results with auto_paging_iter().

Step 2: How Many Active Subscriptions Do I Have?

Understanding your subscription count helps you assess customer base growth and calculate important metrics like average revenue per user (ARPU).

Via Stripe Dashboard

Stripe's menu names change, so check Stripe's current help for how to list subscriptions and filter them by status. Count subscriptions in "trialing" status separately, as they represent future MRR.

Programmatic Count

import stripe

stripe.api_key = 'your_secret_key'

# A list response has no total_count: count by walking the pages
active_count = sum(1 for _ in stripe.Subscription.list(status='active', limit=100).auto_paging_iter())
trial_count = sum(1 for _ in stripe.Subscription.list(status='trialing', limit=100).auto_paging_iter())

print(f"Active Subscriptions: {active_count}")
print(f"Trialing Subscriptions: {trial_count}")
print(f"Total: {active_count + trial_count}")

Expected Output

Active Subscriptions: 175
Trialing Subscriptions: 9
Total: 184

Key Insights

Calculate your Average Revenue Per User (ARPU): MRR ÷ Active Subscriptions. In our example: $9,675.00 ÷ 175 = $55.29. This is approximate: the 175 includes 3 subscriptions whose value is not in the MRR, and a customer with several subscriptions is counted more than once. Divide by distinct customers if you want a per-customer figure.

Step 3: What Is My Churn Rate?

Churn rate measures the percentage of customers who cancel their subscriptions during a given period. It's one of the most critical metrics for subscription businesses because high churn directly undermines growth.

Understanding Churn Types

Calculating Monthly Customer Churn

The formula: (Customers Lost During Month ÷ Customers at Start of Month) × 100. The opening group must be the customers who were subscribed at the start of the month, including those who canceled later; listing only the subscriptions that are active today would drop exactly the customers you are trying to count.

import stripe
from datetime import datetime, timezone

stripe.api_key = 'your_secret_key'

# UTC month boundaries: the month is [month_start, next_month_start)
month_start = int(datetime(2026, 9, 1, tzinfo=timezone.utc).timestamp())
next_month_start = int(datetime(2026, 10, 1, tzinfo=timezone.utc).timestamp())

# status='all' includes canceled subscriptions, which the default list leaves out
all_subs = list(stripe.Subscription.list(status='all', limit=100).auto_paging_iter())

def live_at(sub, t):
    """Was this subscription in force at time t? Created before t and not canceled before t."""
    # Assumption: canceled_at is used here as the end of service. In
    # Stripe it can record when the cancellation was requested, which
    # may be earlier than the service end. Use your actual end
    # timestamps (for example ended_at) where your data has them.
    if sub['status'] in ('incomplete', 'incomplete_expired') or sub['created'] >= t:
        return False
    return sub['canceled_at'] is None or sub['canceled_at'] >= t

# Customers with at least one subscription in force at the start of the month
opening = {s['customer'] for s in all_subs if live_at(s, month_start)}
# Of those, customers with no subscription in force at the end of the month
still_there = {s['customer'] for s in all_subs if live_at(s, next_month_start)}
lost = opening - still_there

churn_rate = len(lost) / len(opening) * 100 if opening else None

print(f"Customers at Start: {len(opening)}")
print(f"Customers Lost: {len(lost)}")
if churn_rate is not None:
    print(f"Monthly Customer Churn Rate: {churn_rate:.2f}%")
else:
    print("Monthly Customer Churn Rate: undefined (no opening customers)")

Expected Output

Customers at Start: 189
Customers Lost: 10
Monthly Customer Churn Rate: 5.29%

Assumption about canceled_at: the code above treats canceled_at as the moment service ended. Stripe can record the time a cancellation was requested in that field, which may be earlier than the end of service (a customer who schedules a cancellation can keep service afterwards). If your data has the real end timestamps, use those instead, otherwise a customer who still had service at the start of the month can fall out of the opening group.

Interpreting Churn Rate

The bands below are illustrative thresholds, not a sourced benchmark. What counts as acceptable depends on your segment, price point and contract length.

Churn Rate Assessment Action Required
< 2% Excellent Maintain current retention strategies
2-5% Good Monitor trends, optimize onboarding
5-7% Concerning Investigate cancellation reasons, improve product value
> 7% Critical Immediate action needed, customer interviews, product-market fit review

In this synthetic example, 10 of 189 customers is 5.29%, which falls in the "Concerning" band of the illustrative table. One month on 189 customers is a small sample: one or two customers more or fewer moves the rate by about half a point to a full point, so look at several months before acting. Exit surveys can help you learn why customers leave.

Understanding statistical significance in your churn analysis is crucial. Learn more about statistical significance in A/B testing to ensure your retention experiments yield reliable results.

Step 4: Which Plans Are Most Popular?

Analyzing plan distribution helps you understand which offerings resonate with customers and drive revenue. This insight informs pricing strategy and product development.

Via Stripe Dashboard

  1. Go to Products > Pricing
  2. Each price shows the number of active subscriptions
  3. Sort by "Subscriptions" to see most popular plans

Detailed Plan Analysis

import stripe
from collections import defaultdict

stripe.api_key = 'your_secret_key'

plan_stats = defaultdict(lambda: {'count': 0, 'mrr': 0.0})

for sub in active:                      # the active subscriptions fetched in Step 1
    for item in sub['items']['data']:
        cents = monthly_cents(item)     # helper from Step 1
        if item['price']['currency'] != 'usd' or cents is None:
            continue
        name = item['price']['nickname'] or item['price']['id']
        plan_stats[name]['count'] += 1
        plan_stats[name]['mrr'] += cents / 100

sorted_plans = sorted(plan_stats.items(), key=lambda x: x[1]['mrr'], reverse=True)

print(f"{'Plan Name':<30} {'Subscriptions':<15} {'MRR':<15}")
print("-" * 60)
for plan, stats in sorted_plans:
    print(f"{plan:<30} {stats['count']:<15} ${stats['mrr']:>12,.2f}")

Expected Output

Plan Name                      Subscriptions   MRR
------------------------------------------------------------
Professional Plan              41              $    4,575.00
Starter Plan                   73              $    2,280.00
Enterprise Plan                5               $    1,200.00
Basic Plan                     40              $      600.00
Professional Plan (annual)     10              $      600.00
Team Plan (quarterly)          6               $      420.00

Strategic Insights

Each row counts subscription items, so a subscription with two items appears in two rows, and the plan MRR values add up to the $9,675.00 from Step 1.

From this analysis, you can identify:

Interpreting Your Results

Now that you've gathered your MRR data, active subscription counts, churn rate, and plan distribution, it's time to synthesize these metrics into actionable insights.

Calculating Key Performance Indicators

1. MRR Growth Rate

Formula: ((Current Month MRR - Previous Month MRR) ÷ Previous Month MRR) × 100

Hypothetical example:
Current MRR: $9,675.00
Previous MRR: $9,100.00
Growth Rate: ((9,675.00 - 9,100.00) / 9,100.00) × 100 = 6.32%

2. Net MRR Churn

This accounts for both lost revenue (downgrades/cancellations) and gained revenue (upgrades/expansion):

Net MRR Churn = ((Churned MRR - Expansion MRR) ÷ Starting MRR) × 100

Hypothetical example:
Starting MRR: $9,100.00
Churned MRR: $520.00
Expansion MRR: $700.00
Net MRR Churn: ((520 - 700) / 9,100) × 100 = -1.98% (negative means expansion outweighed losses)

3. Customer Lifetime Value (LTV)

A rough estimate of the revenue a customer generates over their lifetime. It uses revenue, not margin, and assumes churn stays constant:

LTV = ARPU ÷ Monthly Churn Rate

Example from the figures above:
ARPU: $55.29
Churn Rate: 5.29% (0.0529)
LTV: $55.29 ÷ 0.0529 = about $1,045

Health Check Framework

Use this framework as a starting checklist. The ranges are illustrative assumptions, not sourced benchmarks, and healthy values differ by business stage, price point and segment:

Metric Healthy Range Warning Signs
MRR Growth Rate > 10% monthly < 5% for 3+ months
Customer Churn < 5% monthly > 7% or increasing trend
Net MRR Churn < 0% (negative) > 2% positive
LTV:CAC Ratio > 3:1 < 2:1

Trend Analysis

Single-point measurements only tell part of the story. Track these metrics over time:

Cohort tables and survival curves can show patterns in your subscription data that a single month's metrics hide.

Run It on Your Own Stripe Export

Export your subscriptions from Stripe as a CSV and upload it at MCP Analytics.

Try it on your data

Common Issues and Solutions

Issue 1: MRR Doesn't Match Revenue

Problem: Your calculated MRR differs from actual revenue received.

Cause: MRR represents normalized recurring revenue, not actual cash flow. One-time charges, failed payments, and billing timing create discrepancies.

Solution:

Issue 2: Churn Rate Seems Too High

Problem: Your churn rate exceeds 7-10% monthly.

Cause: Poor product-market fit, inadequate onboarding, pricing issues, or competitive pressure.

Solution:

Issue 3: Subscription Count Increasing, But MRR Flat

Problem: You're acquiring customers but revenue isn't growing proportionally.

Cause: New customers choosing lower-tier plans while high-value customers churn (negative revenue mix).

Solution:

Issue 4: API Rate Limits When Fetching Subscriptions

Problem: Stripe API returns rate limit errors when processing many subscriptions.

Cause: Exceeding Stripe's API rate limits, which Stripe documents separately for test and live mode.

Solution:

import stripe

stripe.api_key = 'your_secret_key'

# The library retries connection errors and conflict or lock-timeout responses; a plain rate-limit 429 is raised as RateLimitError, so back off yourself
stripe.max_network_retries = 3

def fetch_all_subscriptions():
    # status='all' includes canceled; auto_paging_iter() follows has_more for you
    return list(stripe.Subscription.list(status='all', limit=100).auto_paging_iter())

try:
    subscriptions = fetch_all_subscriptions()
except stripe.RateLimitError:
    # not retried by the library: sleep with exponential backoff and jitter, or fetch in smaller date ranges with the created filter
    raise

Issue 5: Difficulty Tracking MRR Changes Over Time

Problem: Understanding what drove MRR changes requires manual investigation.

Cause: Stripe doesn't provide built-in MRR movement categorization.

Solution: Implement MRR movement tracking:

Use Stripe webhooks to track subscription lifecycle events and categorize MRR changes as each event arrives.

Next Steps with Stripe Analytics

You have now seen the fundamentals of MRR analysis in Stripe. Here are advanced topics to explore next:

1. Cohort Retention Analysis

Track how customer cohorts (grouped by signup month) retain over time. This reveals whether your product is improving at retaining customers and helps predict long-term value.

2. Revenue Forecasting

Use historical MRR growth, churn rates, and seasonality to project future revenue. This is critical for fundraising, hiring plans, and strategic planning.

3. Advanced Segmentation

Segment your MRR analysis by:

4. Churn Prediction

Build machine learning models to identify at-risk customers before they cancel. Techniques like Accelerated Failure Time (AFT) models can help predict subscription lifetime based on customer behavior.

5. Pricing Optimization

Experiment with pricing changes using controlled tests. Understand how to apply ensemble learning methods like AdaBoost to optimize pricing based on customer characteristics and behavior patterns.

6. Payment Recovery

Analyze failed payment patterns and implement dunning strategies to recover revenue from involuntary churn (expired cards, insufficient funds).

Recommended Resources

Conclusion

Understanding your MRR is fundamental to operating a successful subscription business. By systematically analyzing your current MRR, active subscriptions, churn rate, and plan distribution, you gain the insights needed to make informed decisions about growth, retention, and product strategy.

The metrics you've learned to calculate (MRR growth rate, customer churn, ARPU, and LTV) form the foundation of subscription business analytics. Track these consistently, identify trends early, and take action when metrics signal opportunity or risk.

Remember that MRR analysis is not a one-time exercise but an ongoing discipline. Set up weekly or monthly reviews, automate data collection where possible, and share insights across your organization. The businesses that win in subscription markets are those that deeply understand their metrics and act on them quickly.

To run these calculations on your own subscriptions without writing code, see the "Run It on Your Own Stripe Export" section above.

Explore more: Stripe Analytics: all tools, tutorials, and guides →

Not sure which plan? Compare plans →

Your turn

Bring your own data and the question you actually need answered.

MCP AnalyticsData science by email Send us your data and your question, and we will reply. ds@mcpanalytics.ai