Trials¶
Trial-to-paid conversion rates, funnel analysis, and monthly trends from the Tidemill engine.
How Stripe models trials¶
A subscription with a trial period has:
trial_start— Unix timestamp when the trial beginstrial_end— Unix timestamp when the trial expiresstatus = "trialing"— during the trial window
When the trial ends, Stripe automatically attempts to charge the customer.
If payment succeeds, status transitions to "active" (converted).
If payment fails or the customer cancels before trial end, the sub moves
to "canceled" or "incomplete_expired" (expired/not converted).
Tidemill's trial funnel — cohort-based¶
A trial is attributed to the period of its trial_started event, and
its eventual outcome rolls up to that same cohort no matter when the
outcome event arrives. A January-cohort trial that converts in March
counts toward January's conversion rate — and will update January's
number when the March conversion lands.
| Metric | Definition |
|---|---|
| started | Trials whose trial_started fell in the period |
| converted | …of those, that eventually converted to paid |
| expired | …of those, that eventually expired without pay |
| conversion_rate | converted / started |
from tidemill import reports
from tidemill.reports.client import TidemillClient
reports.setup()
START, END = "2025-07-01", "2026-04-30"
tm = TidemillClient()
1. Overall Trial Conversion Rate¶
rate = tm.trial_rate(START, END)
print(
f"Overall trial-to-paid conversion rate: {rate:.1%}"
if rate is not None
else "No trial data available"
)
Overall trial-to-paid conversion rate: 96.7%
2. Trial Funnel¶
Breakdown of trial outcomes: started, converted to paid, and expired.
funnel_data = reports.trials.funnel(tm, START, END)
reports.trials.style_funnel(funnel_data)
| Started | Converted | Expired | Conversion Rate |
|---|---|---|---|
| 30 | 29 | 1 | 96.7% |
reports.trials.plot_funnel(funnel_data)
3. Monthly Cohort Conversion Rate¶
One row per started-in-month cohort with the eventual outcomes of its trials. Recent months may still show pending trials that haven't converted or expired yet — their conversion rate will update as those trials reach their terminal state.
trial_df = reports.trials.timeline(tm, START, END)
reports.trials.style_timeline(trial_df)
| started | converted | expired | conversion_rate | |
|---|---|---|---|---|
| period | ||||
| Jul 2025 | 6 | 5 | 1 | 83% |
| Aug 2025 | 4 | 4 | 0 | 100% |
| Sep 2025 | 4 | 4 | 0 | 100% |
| Oct 2025 | 4 | 4 | 0 | 100% |
| Nov 2025 | 2 | 2 | 0 | 100% |
| Dec 2025 | 5 | 5 | 0 | 100% |
| Jan 2026 | 5 | 5 | 0 | 100% |
reports.trials.plot_timeline(trial_df)