SaaS Analytics
Analytics implementation guide for SaaS businesses, covering product analytics, trial tracking, and subscription metrics.
SaaS Analytics Framework
Key Metrics to Track
| Category | Metrics | Why It Matters | |----------|---------|----------------| | Acquisition | MQL, SQL, Trial starts | Pipeline health | | Activation | First value moment, Feature adoption | User success | | Retention | DAU/MAU, Churn rate | Product stickiness | | Revenue | MRR, LTV, Expansion | Business health | | Referral | NPS, Invites sent | Growth potential |
The SaaS Funnel
┌─────────────────────────────────────────────────────────┐
│ SaaS Analytics Funnel │
├─────────────────────────────────────────────────────────┤
│ │
│ Website Visit │ Trial Start │ Activation │
│ ───────────────────────────────────────────────────── │
│ • Page views │ • Sign up │ • First key │
│ • Demo request │ • Plan select │ action │
│ • Pricing view │ • Verification │ • Aha moment │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Conversion │ Paid Conversion │ Retention │
│ ───────────────────────────────────────────────────── │
│ • Purchase │ • Subscription │ • Feature use │
│ • Plan selection │ • Payment │ • Renewal │
│ • Contract │ • Upgrade │ • Expansion │
│ │
└─────────────────────────────────────────────────────────┘
GA4 Implementation
Event Schema
// User signs up for trial
dataLayer.push({
event: 'sign_up',
method: 'email',
user_id: 'user_123',
plan_type: 'free_trial',
plan_tier: 'professional'
});
// User completes activation step
dataLayer.push({
event: 'tutorial_complete',
tutorial_name: 'onboarding',
step_number: 3,
step_name: 'create_first_project'
});
// Feature usage
dataLayer.push({
event: 'feature_use',
feature_name: 'report_builder',
feature_category: 'analytics',
usage_count: 1
});
// Subscription purchase
dataLayer.push({
event: 'purchase',
transaction_id: 'sub_123',
value: 99.00,
currency: 'USD',
items: [{
item_id: 'plan_professional',
item_name: 'Professional Plan',
item_category: 'subscription',
price: 99.00,
quantity: 1
}],
subscription_type: 'monthly',
trial_converted: true
});
Custom Dimensions
Set up these user properties in GA4:
| User Property | Scope | Example Values | |---------------|-------|----------------| | account_type | User | free, trial, paid | | plan_tier | User | starter, professional, enterprise | | company_size | User | 1-10, 11-50, 51-200, 200+ | | industry | User | technology, finance, healthcare | | signup_date | User | 2024-01-15 | | trial_end_date | User | 2024-01-29 |
Event Dimensions
| Parameter | Events | Purpose | |-----------|--------|---------| | feature_name | feature_use | Feature adoption tracking | | workflow_step | workflow_* | Funnel analysis | | error_type | error | Issue identification |
Product Analytics Events
Onboarding Funnel
// Step 1: Account created
dataLayer.push({
event: 'onboarding_start',
step: 1,
step_name: 'account_created'
});
// Step 2: Profile completed
dataLayer.push({
event: 'onboarding_progress',
step: 2,
step_name: 'profile_completed'
});
// Step 3: First project created
dataLayer.push({
event: 'onboarding_progress',
step: 3,
step_name: 'first_project'
});
// Step 4: Team member invited
dataLayer.push({
event: 'onboarding_progress',
step: 4,
step_name: 'team_invited'
});
// Completion
dataLayer.push({
event: 'onboarding_complete',
steps_completed: 4,
time_to_complete_seconds: 1800
});
Feature Adoption
// Generic feature tracking
function trackFeatureUse(featureName, category, metadata = {}) {
dataLayer.push({
event: 'feature_use',
feature_name: featureName,
feature_category: category,
...metadata
});
}
// Examples
trackFeatureUse('create_report', 'analytics');
trackFeatureUse('invite_team_member', 'collaboration');
trackFeatureUse('api_integration', 'developer', { api_type: 'rest' });
trackFeatureUse('export_data', 'data', { format: 'csv', rows: 1500 });
Activation Tracking
Define your activation metric (the "Aha moment"):
// Example: User activated when they create 3 reports
const ACTIVATION_THRESHOLD = 3;
function checkActivation(reportCount) {
if (reportCount >= ACTIVATION_THRESHOLD && !userActivated) {
dataLayer.push({
event: 'user_activated',
activation_trigger: 'report_creation',
days_to_activation: daysSinceSignup(),
reports_at_activation: reportCount
});
}
}
Subscription Analytics
MRR Events
// New subscription
dataLayer.push({
event: 'subscription_started',
subscription_id: 'sub_abc123',
plan_tier: 'professional',
billing_period: 'monthly',
mrr: 99.00,
is_trial_conversion: true
});
// Upgrade
dataLayer.push({
event: 'subscription_upgraded',
subscription_id: 'sub_abc123',
previous_plan: 'starter',
new_plan: 'professional',
previous_mrr: 29.00,
new_mrr: 99.00,
mrr_change: 70.00
});
// Downgrade
dataLayer.push({
event: 'subscription_downgraded',
subscription_id: 'sub_abc123',
previous_plan: 'professional',
new_plan: 'starter',
previous_mrr: 99.00,
new_mrr: 29.00,
mrr_change: -70.00
});
// Churn
dataLayer.push({
event: 'subscription_cancelled',
subscription_id: 'sub_abc123',
plan_tier: 'professional',
mrr_lost: 99.00,
cancellation_reason: 'too_expensive',
months_as_customer: 8
});
Revenue Attribution
Connect marketing spend to subscription revenue:
// Include attribution data with subscription
dataLayer.push({
event: 'subscription_started',
// ... subscription details
attribution: {
first_touch_source: 'google',
first_touch_medium: 'cpc',
first_touch_campaign: 'brand_search',
last_touch_source: 'direct',
signup_to_paid_days: 14
}
});
BigQuery Analysis
Trial Conversion Analysis
-- Trial to paid conversion by source
WITH trials AS (
SELECT
user_pseudo_id,
user_id,
traffic_source.source,
traffic_source.medium,
MIN(event_timestamp) as trial_start
FROM `project.analytics.events_*`
WHERE event_name = 'sign_up'
AND event_params.key = 'plan_type'
AND event_params.value.string_value = 'free_trial'
GROUP BY 1, 2, 3, 4
),
conversions AS (
SELECT
user_id,
MIN(event_timestamp) as conversion_time
FROM `project.analytics.events_*`
WHERE event_name = 'subscription_started'
GROUP BY 1
)
SELECT
t.source,
t.medium,
COUNT(DISTINCT t.user_id) as trials,
COUNT(DISTINCT c.user_id) as conversions,
SAFE_DIVIDE(COUNT(DISTINCT c.user_id), COUNT(DISTINCT t.user_id)) as conversion_rate
FROM trials t
LEFT JOIN conversions c ON t.user_id = c.user_id
GROUP BY 1, 2
ORDER BY trials DESC
Feature Adoption Cohorts
-- Feature adoption by signup cohort
WITH user_cohorts AS (
SELECT
user_id,
DATE(TIMESTAMP_MICROS(MIN(event_timestamp))) as signup_date
FROM `project.analytics.events_*`
WHERE event_name = 'sign_up'
GROUP BY 1
),
feature_usage AS (
SELECT
user_id,
event_params.value.string_value as feature_name,
DATE(TIMESTAMP_MICROS(MIN(event_timestamp))) as first_use_date
FROM `project.analytics.events_*`
WHERE event_name = 'feature_use'
AND event_params.key = 'feature_name'
GROUP BY 1, 2
)
SELECT
DATE_TRUNC(uc.signup_date, WEEK) as signup_week,
fu.feature_name,
COUNT(DISTINCT uc.user_id) as users,
COUNT(DISTINCT fu.user_id) as feature_users,
SAFE_DIVIDE(COUNT(DISTINCT fu.user_id), COUNT(DISTINCT uc.user_id)) as adoption_rate
FROM user_cohorts uc
LEFT JOIN feature_usage fu ON uc.user_id = fu.user_id
GROUP BY 1, 2
ORDER BY 1 DESC, adoption_rate DESC
LTV Calculation
-- Calculate customer lifetime value
WITH customer_revenue AS (
SELECT
user_id,
DATE(TIMESTAMP_MICROS(MIN(event_timestamp))) as first_purchase,
DATE(TIMESTAMP_MICROS(MAX(event_timestamp))) as last_purchase,
SUM(event_params.value.double_value) as total_revenue,
COUNT(*) as transactions
FROM `project.analytics.events_*`
WHERE event_name = 'purchase'
AND event_params.key = 'value'
GROUP BY 1
)
SELECT
DATE_TRUNC(first_purchase, MONTH) as cohort_month,
COUNT(DISTINCT user_id) as customers,
AVG(total_revenue) as avg_ltv,
AVG(DATE_DIFF(last_purchase, first_purchase, DAY)) as avg_customer_days
FROM customer_revenue
GROUP BY 1
ORDER BY 1 DESC
Dashboards
Key SaaS Metrics Dashboard
| Metric | Calculation | Target | |--------|-------------|--------| | Trial Starts | Count of sign_up events | Increasing | | Trial-to-Paid | Conversions / Trials | 15-25% | | Time to Activation | Avg days to activated | < 3 days | | Feature Adoption | Users using key feature / Total users | 60%+ | | Monthly Active Users | Unique users with events | Increasing | | Net MRR Growth | New + Expansion - Churn | Positive |
Looker Studio Components
- Acquisition Overview - Traffic sources, trial signups
- Activation Funnel - Onboarding completion rates
- Product Engagement - Feature usage, DAU/MAU
- Revenue Metrics - MRR, LTV, churn
- Cohort Analysis - Retention by signup date
Integration Points
Connect to Your Stack
| Tool | Integration | Data Flow | |------|-------------|-----------| | Stripe | Webhook → Server → GA4 | Subscription events | | Segment | CDP | Unified user tracking | | Mixpanel | Parallel tracking | Product analytics | | Amplitude | Parallel tracking | Product analytics | | HubSpot | GA4 events → CRM | Lead scoring |
Server-Side Events
For subscription events, use server-side:
// Server-side (Node.js example)
const measurement_id = 'G-XXXXXXXXXX';
const api_secret = 'your_api_secret';
fetch(`https://www.google-analytics.com/mp/collect?measurement_id=${measurement_id}&api_secret=${api_secret}`, {
method: 'POST',
body: JSON.stringify({
client_id: 'server_generated_id',
user_id: 'user_123',
events: [{
name: 'subscription_started',
params: {
subscription_id: 'sub_abc',
plan_tier: 'professional',
mrr: 99.00
}
}]
})
});
Previous: Debug & Validation Next: B2B Lead Gen