Как посчитать Free-to-Paid в SQL
Содержание:
Зачем Free-to-Paid
Freemium core funnel: signup → free use → convert. Без healthy Free-to-Paid невозможно масштабироваться. Slack, Dropbox, Notion — все built on этом.
Формула
Free-to-Paid = users_paid_within_X / total_signups × 100%Window обычно 30/60/90 days post-signup.
Базовый расчёт
WITH signups AS (
SELECT user_id, signed_up_at
FROM users
WHERE signed_up_at BETWEEN '2026-01-01' AND '2026-02-01'
),
first_paid AS (
SELECT
user_id,
MIN(created_at) AS first_payment_at
FROM transactions
WHERE status = 'paid'
AND amount > 0
GROUP BY user_id
)
SELECT
COUNT(s.user_id) AS signups,
COUNT(p.user_id) AS converted_to_paid,
COUNT(p.user_id) FILTER (WHERE p.first_payment_at <= s.signed_up_at + INTERVAL '30 days') AS converted_30d,
COUNT(p.user_id) FILTER (WHERE p.first_payment_at <= s.signed_up_at + INTERVAL '90 days') AS converted_90d,
COUNT(p.user_id) FILTER (WHERE p.first_payment_at <= s.signed_up_at + INTERVAL '30 days')::NUMERIC * 100 / COUNT(s.user_id) AS free_to_paid_30d_pct
FROM signups s
LEFT JOIN first_paid p ON p.user_id = s.user_id;По cohort
WITH cohort AS (
SELECT
DATE_TRUNC('week', signed_up_at) AS cohort_week,
user_id
FROM users
WHERE signed_up_at >= CURRENT_DATE - INTERVAL '12 weeks'
),
paid AS (
SELECT user_id, MIN(created_at) AS paid_at
FROM transactions
WHERE status = 'paid'
GROUP BY user_id
)
SELECT
c.cohort_week,
COUNT(c.user_id) AS signups,
COUNT(p.user_id) AS paid,
COUNT(p.user_id)::NUMERIC * 100 / NULLIF(COUNT(c.user_id), 0) AS conversion_pct
FROM cohort c
LEFT JOIN paid p ON p.user_id = c.user_id
AND p.paid_at - (
SELECT signed_up_at FROM users WHERE user_id = c.user_id
) <= INTERVAL '30 days'
GROUP BY c.cohort_week
ORDER BY c.cohort_week;Time-to-conversion
WITH conversions AS (
SELECT
u.user_id,
u.signed_up_at,
MIN(t.created_at) AS first_paid_at,
EXTRACT(EPOCH FROM (MIN(t.created_at) - u.signed_up_at)) / 86400 AS days_to_pay
FROM users u
JOIN transactions t ON t.user_id = u.user_id
WHERE t.status = 'paid'
AND u.signed_up_at >= CURRENT_DATE - INTERVAL '6 months'
GROUP BY u.user_id, u.signed_up_at
)
SELECT
PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY days_to_pay) AS p25_days,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY days_to_pay) AS median_days,
PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY days_to_pay) AS p75_days,
AVG(days_to_pay) AS avg_days
FROM conversions;Частые ошибки
Ошибка 1. Snapshot vs cohort. «Free-to-Paid 10%» today — including users who signed up 5 years ago? Use cohort.
Ошибка 2. Window choice. 30d vs 90d очень разные. Long-tail conversions (paid после 6 months) common.
Ошибка 3. Trial vs free. Trial-to-paid и Free-to-paid — разные funnels. Не путать.
Ошибка 4. Multiple price points. Paid for $1 trial — does it count as paid? Define «paid».
Ошибка 5. Refunds. Paid, refunded в week 2. Filter.
Связанные темы
- Как посчитать trial conversion в SQL
- Как посчитать paying users в SQL
- Как посчитать activation rate в SQL
- Кейс: free-to-paid упал — как диагностировать
FAQ
Какой Free-to-Paid ok?
SaaS B2C: 2-5% — average, 5-10% — good. SaaS B2B: 5-15%. Slack: ~30% (workspace-level).
30d vs 90d window?
30d — early signal. 90d — more realistic для long sales cycles.
Free-to-Paid падает?
Check funnels: signup quality, activation, time-to-value, paywall friction.
Conversion от activated users?
Activated → Paid лучше signup → Paid. Filter to activated.
Long-tail conversions?
В SaaS many users convert после года+. Track full lifetime curve.