Executives were flying blind. Subscription activity lived in one system, costs in another, and customer value was scattered across spreadsheets. Without a unified, real-time view, leadership couldn’t answer basic questions: Are we profitable this quarter? Are we retaining customers? Is growth sustainable? Decisions were slowed, and financial risk mounted until Lunexa Insights built a centralized dashboard that brought clarity in minutes, not months.
RavenStack, a mid-stage SaaS startup, needed a comprehensive investor-ready KPI report built from multiple raw datasets. Their data included accounts, subscriptions, churn events, feature usage, and support tickets, but it was unstructured and siloed across systems. The company sought clarity on financial performance, customer behavior, and churn dynamics heading into a critical fundraising round.
To design and implement a dynamic, investor-ready KPI dashboard that gives startup founders and stakeholders clear, up-to-date visibility into key business metrics:
Senior Data Analyst (designed and implemented all reporting and analysis)
3 weeks (data exploration, metric development, analysis and visual dashboard delivery)
Key SaaS Metrics Analysis
1. Monthly Recurring Revenue (MRR)
MRR has shown exceptional growth, reaching $10.7M in December 2024. The growth trajectory is particularly strong in the latter half of 2024.
subscriptions['start_month'] = subscriptions['start_date'].dt.to_period('M')subscriptions['end_month'] = subscriptions['end_date'].dt.to_period('M')date_range = pd.period_range(start='2023-01', end='2024-12', freq='M')mrr_timeline = []for month in date_range: active_subs = subscriptions[ (subscriptions['start_month'] <= month) & ((subscriptions['end_month'] >= month) | (subscriptions['end_date'].isna())) ] total_mrr = active_subs['mrr_amount'].sum() mrr_timeline.append({'month': month, 'mrr': total_mrr})mrr_df = pd.DataFrame(mrr_timeline)mrr_df['month_str'] = mrr_df['month'].astype(str)print("MRR Growth Over Time:")print(mrr_df.tail(10))
2. Churn Rate Analysis
120% churn rate, meaning more customers have left than you've ever acquired. This is mathematically possible when:
This data suggests either a major product failure, competitive disruption, or fundamental business model breakdown in late 2024
churn_events['churn_month'] = churn_events['churn_date'].dt.to_period('M')monthly_churn = churn_events.groupby('churn_month').size().reset_index(name='churned_accounts')monthly_active = []for month in date_range:month_start = month.start_timeactive_accounts = accounts[accounts['signup_date'] <= month_start]churned_before = churn_events[churn_events['churn_date'] < month_start]net_active = len(active_accounts) - len(churned_before)monthly_active.append({'month': month, 'active_accounts': max(0, net_active)})active_df = pd.DataFrame(monthly_active)churn_analysis = active_df.merge(monthly_churn, left_on='month', right_on='churn_month', how='left')churn_analysis['churned_accounts'] = churn_analysis['churned_accounts'].fillna(0)churn_analysis['churn_rate'] = (churn_analysis['churned_accounts'] / churn_analysis['active_accounts'] * 100).round(2)print("Monthly Churn Analysis (Last 10 months):")print(churn_analysis[['month', 'active_accounts', 'churned_accounts', 'churn_rate']].tail(10))
3. Customer Lifetime Value (CLTV)
Key LTV Insights:
subscription_ltv = subscriptions.groupby('account_id').agg({ 'mrr_amount': 'mean', 'start_date': 'min', 'end_date': 'max', 'subscription_id': 'count'}).reset_index()subscription_ltv['duration_days'] = (subscription_ltv['end_date'] - subscription_ltv['start_date']).dt.dayssubscription_ltv['duration_months'] = (subscription_ltv['duration_days'] / 30.44).round(2)subscription_ltv['total_revenue'] = (subscription_ltv['mrr_amount'] * subscription_ltv['duration_months']).round(2)ongoing_mask = subscription_ltv['end_date'].isna()subscription_ltv.loc[ongoing_mask, 'duration_months'] = 12 # Assume 12 months for ongoingsubscription_ltv.loc[ongoing_mask, 'total_revenue'] = subscription_ltv.loc[ongoing_mask, 'mrr_amount'] * 12print("LTV Analysis Summary:")print("Average LTV: $" + str(subscription_ltv['total_revenue'].mean().round(2)))print("Median LTV: $" + str(subscription_ltv['total_revenue'].median().round(2)))print("LTV by Plan Tier:")ltv_with_plans = subscription_ltv.merge(accounts[['account_id', 'plan_tier']], on='account_id', how='left')ltv_by_plan = ltv_with_plans.groupby('plan_tier')['total_revenue'].agg(['mean', 'median', 'count']).round(2)print(ltv_by_plan)
4. Customer Acquisition Analysis
Acquisition is well-distributed across channels, with organic and ads being strong performers. Enterprise customers show good acquisition numbers across all channels.
acquisition_analysis = accounts.groupby(['referral_source', 'plan_tier']).agg({ 'account_id': 'count', 'seats': 'mean'}).round(2)acquisition_analysis.columns = ['accounts_acquired', 'avg_seats']print("Customer Acquisition by Source and Plan:")print(acquisition_analysis)
5. Growth Rate Analysis
mrr_df['mrr_growth'] = mrr_df['mrr'].pct_change() * 100mrr_df['mrr_growth'] = mrr_df['mrr_growth'].round(2)print("MRR Growth Rate Analysis:")print("Recent MRR Growth Rates (Last 6 months):")print(mrr_df[['month_str', 'mrr', 'mrr_growth']].tail(6))current_mrr = mrr_df['mrr'].iloc[-1]previous_mrr = mrr_df['mrr'].iloc[-2]mom_growth = ((current_mrr - previous_mrr) / previous_mrr * 100).round(2)print("\Key Growth Metrics:")print("Current MRR: $" + str(current_mrr))print("Previous Month MRR: $" + str(previous_mrr))print("Month-over-Month Growth: " + str(mom_growth) + "%")
Executive Summary Table: A summary matrix outlining key metrics, status indicators and trends.
To design and implement a dynamic, investor-ready KPI dashboard that gives startup founders and stakeholders clear, up to date visibility into key business metrics:
With the right analytical approach, raw CSVs can be turned into actionable dashboards that drive funding, retention, and growth strategy. RavenStack is now equipped with a strong narrative for investors and internal alignment around performance metrics.
Copyright © 2025 Lunexa Insights. All rights reserved.