Stripe Trial-to-Paid Conversion: Tracking Guide

Category: Stripe Analytics | Updated: December 2024

Introduction to Trial-to-Paid Conversion Analysis

Trial-to-paid conversion rate is one of the most critical metrics for subscription-based businesses using Stripe. Understanding not just what percentage of your trial users convert to paying customers, but why they convert, can dramatically impact your revenue and growth trajectory.

In this comprehensive tutorial, you'll learn how to perform trial-to-paid conversion analysis using Stripe data. We'll cover everything from data extraction to advanced segmentation techniques that reveal the hidden patterns driving successful conversions. Whether you're seeing a 10% conversion rate or 50%, this guide will help you understand the levers you can pull to improve performance.

By the end of this tutorial, you'll be able to:

Prerequisites and Data Requirements

What You'll Need

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

  1. Stripe Account Access: You need admin or developer access to your Stripe account with permissions to view subscription data and use the API.
  2. Active Trial Subscriptions: At least 100 completed trials (either converted or expired) for statistically meaningful analysis. More data yields better insights.
  3. Trial Period Configuration: Your Stripe subscription plans should be configured with trial periods. This can be set at the product level or applied dynamically.
  4. Data Export Tools: Either familiarity with Stripe's API or access to their Dashboard export features. Basic knowledge of Python, SQL, or Excel is helpful but not required.
  5. Time Range: A minimum of 3-6 months of trial data to account for seasonality and ensure adequate sample size.

Understanding Stripe Trial Data Structure

Stripe tracks trial information within subscription objects. Key fields include:

A subscription that converts successfully will transition from status: "trialing" to status: "active" after the trial period, with the first payment processed.

Step 1: Extract Trial Subscription Data from Stripe

The first step in any conversion analysis is gathering your raw data. Stripe offers multiple methods to access this information.

Method A: Using the Stripe Dashboard (No Coding Required)

  1. Log into your Stripe Dashboard
  2. Navigate to Billing → Subscriptions
  3. Click the Export button in the top right
  4. Select your date range (recommend at least 6 months)
  5. Choose CSV format and download the file

Method B: Using the Stripe API (Recommended for Large Datasets)

For businesses with thousands of subscriptions, the API provides more flexibility and automation capabilities.

import stripe
import pandas as pd
from datetime import datetime, timedelta

# Initialize Stripe with your secret key
stripe.api_key = 'sk_test_your_secret_key_here'

# Define date range for analysis
end_date = datetime.now()
start_date = end_date - timedelta(days=180)  # 6 months

# Fetch all subscriptions with trials
subscriptions = []
starting_after = None

while True:
    params = {
        'limit': 100,
        'created': {
            'gte': int(start_date.timestamp()),
            'lte': int(end_date.timestamp())
        }
    }

    if starting_after:
        params['starting_after'] = starting_after

    response = stripe.Subscription.list(**params)

    # Filter for subscriptions that had trials
    trial_subs = [sub for sub in response.data if sub.trial_start is not None]
    subscriptions.extend(trial_subs)

    if not response.has_more:
        break

    starting_after = response.data[-1].id

print(f"Retrieved {len(subscriptions)} trial subscriptions")

Expected Output

After running the API script, you should see output like:

Retrieved 1,247 trial subscriptions

Your data should now include all subscriptions that included a trial period within your specified timeframe. Each subscription object contains the trial dates, conversion status, and associated customer information.

Step 2: Transform Data into Analysis-Ready Format

Raw Stripe data needs to be transformed into a structured format that makes analysis straightforward. We'll create a clean dataset with one row per trial subscription.

# Convert Stripe subscription objects to DataFrame
data_records = []

for sub in subscriptions:
    record = {
        'subscription_id': sub.id,
        'customer_id': sub.customer,
        'trial_start': datetime.fromtimestamp(sub.trial_start),
        'trial_end': datetime.fromtimestamp(sub.trial_end),
        'status': sub.status,
        'plan_id': sub.plan.id if sub.plan else None,
        'plan_amount': sub.plan.amount / 100 if sub.plan else None,  # Convert cents to dollars
        'plan_interval': sub.plan.interval if sub.plan else None,
        'created': datetime.fromtimestamp(sub.created),
        'current_period_start': datetime.fromtimestamp(sub.current_period_start),
        'canceled_at': datetime.fromtimestamp(sub.canceled_at) if sub.canceled_at else None
    }
    data_records.append(record)

df = pd.DataFrame(data_records)

# Calculate derived fields
df['trial_length_days'] = (df['trial_end'] - df['trial_start']).dt.days
df['converted'] = df['status'].isin(['active', 'past_due', 'unpaid'])
df['trial_complete'] = df['trial_end'] < datetime.now()

# Filter to only completed trials for conversion analysis
df_complete = df[df['trial_complete']].copy()

print(f"Total completed trials: {len(df_complete)}")
print(f"Converted to paid: {df_complete['converted'].sum()}")
print(f"Overall conversion rate: {df_complete['converted'].mean():.2%}")

Expected Output

Total completed trials: 1,089
Converted to paid: 387
Overall conversion rate: 35.54%

This transformation creates several important fields:

Note that we exclude incomplete trials (where trial_end is in the future) to avoid skewing conversion rates downward.

Step 3: Calculate Core Conversion Metrics

Beyond the basic conversion rate, several related metrics provide deeper insight into trial performance.

# Calculate comprehensive conversion metrics
metrics = {
    'total_trials': len(df_complete),
    'converted_count': df_complete['converted'].sum(),
    'conversion_rate': df_complete['converted'].mean(),
    'average_trial_length': df_complete['trial_length_days'].mean(),
    'median_trial_length': df_complete['trial_length_days'].median()
}

# Calculate time-to-conversion for converted trials
converted_df = df_complete[df_complete['converted']].copy()
converted_df['days_to_first_payment'] = (
    converted_df['current_period_start'] - converted_df['trial_start']
).dt.days

metrics['avg_days_to_conversion'] = converted_df['days_to_first_payment'].mean()
metrics['median_days_to_conversion'] = converted_df['days_to_first_payment'].median()

# Calculate conversion rate by trial length
conversion_by_length = df_complete.groupby('trial_length_days').agg({
    'converted': ['count', 'sum', 'mean']
}).round(3)

print("\n=== CORE CONVERSION METRICS ===")
for key, value in metrics.items():
    if 'rate' in key:
        print(f"{key}: {value:.2%}")
    else:
        print(f"{key}: {value:.1f}")

print("\n=== CONVERSION BY TRIAL LENGTH ===")
print(conversion_by_length)

Expected Output

=== CORE CONVERSION METRICS ===
total_trials: 1089
converted_count: 387
conversion_rate: 35.54%
average_trial_length: 16.2
median_trial_length: 14.0
avg_days_to_conversion: 15.8
median_days_to_conversion: 14.0

=== CONVERSION BY TRIAL LENGTH ===
                   converted
                       count    sum     mean
trial_length_days
7                        234     98    0.419
14                       623    215    0.345
30                       232     74    0.319

These metrics reveal important patterns. In this example, we can see that 7-day trials have the highest conversion rate (41.9%), despite 14-day trials being most common. This insight alone could justify A/B testing different trial lengths to optimize conversions.

Step 4: Identify Conversion Drivers Through Segmentation

The most valuable insights come from understanding which factors influence conversion rates. We'll segment the data multiple ways to uncover these drivers.

Segment by Plan Type and Price Point

# Analyze conversion by pricing tier
plan_analysis = df_complete.groupby('plan_id').agg({
    'converted': ['count', 'sum', 'mean'],
    'plan_amount': 'first',
    'plan_interval': 'first'
}).round(3)

plan_analysis.columns = ['_'.join(col).strip() for col in plan_analysis.columns]
plan_analysis = plan_analysis.sort_values('plan_amount_first')

print("\n=== CONVERSION BY PLAN ===")
print(plan_analysis)

# Analyze by price brackets
df_complete['price_bracket'] = pd.cut(
    df_complete['plan_amount'],
    bins=[0, 20, 50, 100, 500, float('inf')],
    labels=['$0-20', '$20-50', '$50-100', '$100-500', '$500+']
)

price_analysis = df_complete.groupby('price_bracket').agg({
    'converted': ['count', 'mean']
}).round(3)

print("\n=== CONVERSION BY PRICE BRACKET ===")
print(price_analysis)

Segment by Trial Cohort (Time-Based Analysis)

Understanding how conversion rates change over time helps identify the impact of product improvements, marketing campaigns, or seasonal factors.

# Create monthly cohorts based on trial start date
df_complete['cohort_month'] = df_complete['trial_start'].dt.to_period('M')

cohort_analysis = df_complete.groupby('cohort_month').agg({
    'converted': ['count', 'sum', 'mean'],
    'plan_amount': 'mean'
}).round(3)

cohort_analysis.columns = ['_'.join(col).strip() for col in cohort_analysis.columns]

print("\n=== CONVERSION BY MONTHLY COHORT ===")
print(cohort_analysis.tail(6))  # Show last 6 months

Expected Output

=== CONVERSION BY PLAN ===
              converted_count  converted_sum  converted_mean  plan_amount_first  plan_interval_first
plan_id
starter_monthly          345            156           0.452              19.00                month
pro_monthly              421            165           0.392              49.00                month
enterprise_monthly       323             66           0.204             199.00                month

=== CONVERSION BY PRICE BRACKET ===
                converted
                    count      mean
price_bracket
$0-20                 345     0.452
$20-50                421     0.392
$50-100               187     0.359
$100-500              136     0.235

=== CONVERSION BY MONTHLY COHORT ===
              converted_count  converted_sum  converted_mean  plan_amount_mean
cohort_month
2024-07                   178             68           0.382            62.450
2024-08                   192             71           0.370            58.230
2024-09                   185             63           0.341            64.180
2024-10                   201             75           0.373            61.920
2024-11                   168             55           0.327            59.440
2024-12                   165             55           0.333            63.150

This segmentation reveals several actionable insights:

Step 5: Advanced Analysis - Survival Analysis for Trial Conversions

For a more sophisticated understanding of trial behavior, we can apply survival analysis techniques. This approach, commonly used in medical research and increasingly in SaaS analytics, helps answer questions like "How long does it take for conversions to happen?" and "At what point do most users decide to convert or churn?"

This technique is related to Accelerated Failure Time (AFT) models, which can help predict conversion timing based on customer attributes.

from lifelines import KaplanMeierFitter
import matplotlib.pyplot as plt

# Prepare data for survival analysis
# "Event" is conversion, "Duration" is days from trial start to conversion or end
survival_df = df_complete.copy()
survival_df['duration'] = (
    survival_df['trial_end'] - survival_df['trial_start']
).dt.days
survival_df['event'] = survival_df['converted'].astype(int)

# Fit Kaplan-Meier model
kmf = KaplanMeierFitter()
kmf.fit(survival_df['duration'], survival_df['event'], label='Trial Conversion')

# Calculate conversion probability by day
print("\n=== CUMULATIVE CONVERSION PROBABILITY ===")
print(f"Day 3: {kmf.survival_function_at_times(3).values[0]:.2%}")
print(f"Day 7: {kmf.survival_function_at_times(7).values[0]:.2%}")
print(f"Day 14: {kmf.survival_function_at_times(14).values[0]:.2%}")
print(f"Day 30: {kmf.survival_function_at_times(30).values[0]:.2%}")

# Median conversion time
median_time = kmf.median_survival_time_
print(f"\nMedian time to conversion: {median_time:.1f} days")

This analysis helps you understand the timing of conversions, not just the final rate. You might discover that 80% of conversions happen in the first 5 days of a 14-day trial, suggesting opportunities to engage users earlier or potentially shorten the trial period.

Step 6: Interpreting Your Results and Taking Action

Now that you've calculated conversion metrics and identified key drivers, it's time to translate these insights into actionable strategies.

Benchmark Your Performance

Trial-to-paid conversion rates vary significantly by industry and price point:

Key Questions to Ask Your Data

  1. What is the optimal trial length? If shorter trials show higher conversion rates, you may be losing engaged users to indecision. If longer trials convert better, users may need more time to experience value.
  2. Which customer segments convert best? Look beyond the data we've analyzed here. Connect this Stripe data with your product analytics to understand which features correlate with conversion.
  3. When do conversions happen? If most conversions occur in the first few days, your onboarding is working. If they cluster near the trial end, users may need more urgency or earlier value delivery.
  4. What's your conversion rate trend? Are conversions improving or declining over time? Correlate changes with product updates, pricing changes, or marketing campaigns.

Common Optimization Strategies

Based on your analysis results, consider these evidence-based optimization tactics:

Automate Your Trial Conversion Analysis

While this tutorial provides a comprehensive framework for manual analysis, running these calculations every week or month can become time-consuming. MCP Analytics offers automated trial-to-paid conversion analysis that:

Try our Trial Conversion Analysis tool to get instant insights from your Stripe data without writing a single line of code.

For businesses needing custom analysis or strategic consulting on trial optimization, explore our Trial Conversion Optimization Service.

Next Steps and Advanced Topics

Once you've mastered basic trial conversion analysis, consider these advanced topics:

1. Predictive Conversion Modeling

Build machine learning models that predict conversion likelihood based on early trial behavior. Techniques like AdaBoost can help identify which combination of features best predicts conversion.

2. Multi-Touch Attribution

Connect Stripe data with marketing attribution tools to understand which channels and campaigns drive the highest-converting trial users.

3. Product Analytics Integration

Combine Stripe conversion data with product usage analytics (from tools like Mixpanel, Amplitude, or Heap) to identify which features correlate with conversion.

4. Cohort Retention Analysis

Track not just trial conversion, but long-term retention of converted users. High trial conversion with poor retention may indicate misaligned expectations.

5. AI-Powered Analysis Pipelines

Implement AI-first data analysis pipelines that automatically detect anomalies, identify opportunities, and suggest optimizations in your conversion funnel.

Recommended Reading

Troubleshooting Common Issues

Issue 1: Conversion Rate Seems Incorrect or Too Low

Symptoms: Your calculated conversion rate is far lower than expected, possibly under 10% when you know it should be higher.

Likely Causes:

Solution:

# Ensure you're only analyzing completed trials
df_complete = df[df['trial_end'] < datetime.now() - timedelta(days=3)].copy()

# Broaden conversion definition to include all paying statuses
df_complete['converted'] = df_complete['status'].isin([
    'active',
    'past_due',  # Payment failed but subscription still active
    'unpaid',    # Similar to past_due
    'trialing'   # Still in trial but already charged (rare)
])

# Check for subscriptions that converted after a brief cancellation
# Some users cancel then restart within days

Issue 2: Cannot Access Enough Historical Data

Symptoms: Stripe API only returns a small number of subscriptions despite having many trials.

Likely Causes:

Solution:

# Ensure proper pagination
all_subscriptions = []
has_more = True
starting_after = None

while has_more:
    params = {'limit': 100}
    if starting_after:
        params['starting_after'] = starting_after

    response = stripe.Subscription.list(**params)
    all_subscriptions.extend(response.data)

    has_more = response.has_more
    if has_more:
        starting_after = response.data[-1].id

    print(f"Fetched {len(all_subscriptions)} subscriptions so far...")

# Verify API key has correct permissions in Stripe Dashboard

Issue 3: Large Variance in Conversion Rates by Cohort

Symptoms: Monthly conversion rates swing wildly from 20% to 50% and back.

Likely Causes:

Solution:

# Calculate confidence intervals for conversion rates
from scipy import stats

def conversion_rate_with_ci(series, confidence=0.95):
    n = len(series)
    p = series.mean()

    # Wilson score interval (better for proportions than normal approximation)
    z = stats.norm.ppf((1 + confidence) / 2)
    denominator = 1 + z**2 / n
    center = (p + z**2 / (2*n)) / denominator
    margin = z * np.sqrt(p*(1-p)/n + z**2/(4*n**2)) / denominator

    return p, center - margin, center + margin

# Apply to cohort analysis
for cohort in df_complete['cohort_month'].unique():
    cohort_data = df_complete[df_complete['cohort_month'] == cohort]['converted']
    rate, lower, upper = conversion_rate_with_ci(cohort_data)
    print(f"{cohort}: {rate:.2%} (95% CI: {lower:.2%} - {upper:.2%})")

Issue 4: Slow API Performance with Large Datasets

Symptoms: Script takes 10+ minutes to fetch subscription data.

Solution:

Issue 5: Missing Customer Metadata

Symptoms: You want to segment by customer attributes (company size, industry, referral source) but this data isn't in Stripe.

Solution:

Enrich your Stripe data by joining with external sources:

# Fetch customer objects and join with subscriptions
customer_ids = df_complete['customer_id'].unique()
customers = {}

for cust_id in customer_ids:
    customer = stripe.Customer.retrieve(cust_id)
    customers[cust_id] = {
        'email': customer.email,
        'metadata': customer.metadata  # Any custom fields you've stored
    }

customers_df = pd.DataFrame.from_dict(customers, orient='index')
df_enriched = df_complete.merge(
    customers_df,
    left_on='customer_id',
    right_index=True
)

# Now you can segment by metadata fields
conversion_by_source = df_enriched.groupby('metadata.source')['converted'].mean()

Conclusion

Trial-to-paid conversion analysis is not a one-time exercise but an ongoing practice that should inform your product development, pricing strategy, and customer success efforts. By following the steps in this tutorial, you now have a framework for:

Remember that the goal isn't just to measure your conversion rate, but to understand the why behind it. Every percentage point improvement in trial conversion can translate to significant revenue growth, especially for high-volume SaaS businesses.

Start with the basic analysis outlined in Steps 1-4, then progressively add more sophisticated techniques as you become comfortable with the data. Most importantly, turn insights into experiments, measure the results, and iterate.

Ready to take your analysis to the next level? Try MCP Analytics' automated Trial Conversion Analysis and get actionable insights in minutes instead of hours.

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

Marketing Team? Get Channel-Level ROI — See which channels actually drive revenue with media mix modeling, multi-touch attribution, and ad spend analysis.
Explore Marketing Analytics →

Not sure which plan? Compare plans →