HomeBilling Exports

Exporting subscription data from Stripe for churn analysis

Stripe's trap is not a word, it is a flag. A subscription with cancel_at_period_end set to true has status: "active" and will keep billing until the period closes. It is a customer who has already left, wearing the status of one who has not. Read the status column alone and you will find the churn a month or two late — which, in a diligence window, is exactly the churn you were trying to find early.

TL;DR: In Stripe, status alone does not tell you who has left. You need cancel_at_period_end, canceled_at and ended_at alongside it, and you need to build MRR yourself from subscription items, because Stripe does not store an MRR field.

Every churn calculation needs the same seven things: a stable customer identifier, subscription status, recurring amount and currency, billing interval and term, start date, cancellation or end date, and ideally the date of the last successful payment. (Why each one, and what breaks without it →) What changes between platforms is where those fields live and which values mislead you.

How Stripe models this

Stripe separates the customer from what they buy. A Customer owns zero or more Subscriptions. Each subscription holds one or more SubscriptionItems, and each item references a Price, which belongs to a Product and carries the recurring.interval and unit_amount. Invoices and charges sit alongside as the payment record.

That shape matters for two reasons. First, the money is on the items, not the subscription — a subscription with three items has three amounts and three quantities, and the subscription object itself has no total. Second, a customer with two subscriptions is one logo and two revenue lines, so logo churn and revenue churn have to be computed from different grains of the same export.

The subscription status values are trialing, active, past_due, canceled, unpaid, paused, incomplete and incomplete_expired. Only two of those mean what an outsider assumes.

Field mapping for Stripe

Stripe-specific traps

1. cancel_at_period_end hides churn in plain sight

A subscription cancelled today with an annual term ending in nine months reports status: "active" for nine more months. Every one of those accounts is a departure already committed and not yet visible. Count them: status = active AND cancel_at_period_end = true is your pending-churn book, and against an annual base it is frequently the largest single number in a diligence file. It is also the number a seller's dashboard is least likely to surface.

2. incomplete and incomplete_expired are not churn

When a first payment needs authentication and never gets it, Stripe leaves the subscription incomplete and moves it to incomplete_expired after 23 hours. These customers never started. Counting them as churn inflates the rate and, worse, makes a payment-friction problem look like a retention problem. Exclude them from both numerator and denominator, then count them separately — a high incomplete_expired rate is its own finding about checkout, not about the product.

3. past_due and unpaid are involuntary churn, and they are reversible

past_due means a payment failed and Stripe is retrying under the dunning settings. unpaid means retries are exhausted. Both are still churn in revenue terms while they last, but a meaningful share recovers. Split voluntary from involuntary before you compare anything to a benchmark, because a book with 3% voluntary and 4% involuntary churn is a very different asset from one with 7% voluntary — the second is a product problem, the first is largely a card-updater problem.

4. There is no MRR field, so everyone's MRR is a construction

Stripe does not store MRR. Every MRR number you have been shown — in the CIM, in the seller's dashboard, in a third-party tool — is somebody's arithmetic over these objects, with their own choices about trials, discounts, taxes, one-off invoice items, annual amortisation and which statuses count. Ask which choices were made. The gap between two defensible constructions on the same Stripe account is routinely double digits.

5. Trials inflate the customer count and not the revenue

trialing subscriptions carry a price but bill nothing until trial_end. Include them in the logo base and retention looks worse when they fail to convert; exclude them and you miss that trial-to-paid conversion is falling. Report both, and check trial_end against the export date — a cohort of trials ending just after diligence closes is worth asking about.

6. Test-mode data looks identical

Stripe keeps test and live data in separate modes with identically-shaped objects and ids that differ only by a _test_ segment on keys. An export taken in the wrong mode, or a file that merges the two, is not obviously wrong at a glance. Confirm which mode produced the file before anything else, then confirm the row count against the live dashboard.

What to ask for, in words you can paste

“Please send a Stripe export covering all subscriptions, active and cancelled, for the last 24 months. From the Subscriptions export I need: customer id, subscription id, status, cancel_at_period_end, canceled_at, ended_at, start_date, trial_end, currency, and for each subscription item the price id, unit_amount, quantity, recurring interval and interval_count. Please also include any discount or coupon applied. Separately, the Invoices export for the same period with customer id, invoice status, amount_paid and the paid date. Live mode, not test.”

In the Stripe Dashboard this is Billing → Subscriptions → Export and Billing → Invoices → Export, with the column set chosen at export time. Accounts on Stripe Sigma can run the equivalent as SQL against the subscriptions, subscription_items, prices and invoices tables, which is the cleaner route when the book is large.

Before you trust the file

What to do with the file

The analysis is the same regardless of where the file came from: recompute churn in both logo and revenue terms, build the renewal calendar from current_period_end on annual plans, check concentration on the customer rather than the subscription, and test whatever the seller has claimed. The seller-claims pages cover the twelve claims worth testing and the arithmetic for each, and the 23-point checklist is the short version.

Official Stripe documentation: https://docs.stripe.com/api/subscriptions. Export layouts change; the data model and the traps above do not.

Other billing systems

All billing platforms →  ·  No connector needed: the free analyzer reads the CSV in your browser and never uploads it.

Verify it against the raw rows

Every check on this page can be run by hand in a spreadsheet, and if you have the time you should. If you would rather not: send us the target's subscription export and we run the full human-reviewed analysis — logo churn, revenue churn, customer concentration, annual-plan decay, zombie MRR and an A–F revenue-quality grade. The free Starter tier covers one CSV per month, which is enough to check a single deal.

See a sample report →  ·  Get the free 23-point checklist →

Frequently asked questions

Does Stripe give you an MRR number you can export?

No. Stripe has no MRR field. MRR is always constructed from subscription items — unit_amount × quantity, normalised by recurring interval — with judgement calls about trials, discounts, taxes and which statuses count. Any MRR figure in a CIM is somebody's arithmetic, so ask which choices were made before comparing it to anything.

Why does a cancelled Stripe subscription still show as active?

Because cancelling at period end sets cancel_at_period_end to true and leaves status as active until the term closes. The customer has already left; the status has not caught up. Count status = active AND cancel_at_period_end = true separately — on an annual book it is often the largest pending-churn number in the file.

Should incomplete_expired subscriptions count as churn?

No. Those customers never completed a first payment, usually because an authentication step failed, so they never started. Counting them as churn inflates the rate and disguises a checkout problem as a retention problem. Exclude them from the churn calculation and report the rate separately.

9%
Median B2B SaaS revenue churn
88%
Median gross revenue retention
23
Audit Checklist Points

The seller's churn number is almost always wrong. Send the CSV and find out before you wire.

Get the Free Checklist →

🛡️ Run the full 5-Risk analysis free in your browser — unlimited, no account, and the CSV never leaves your machine. The $9 report is the same analysis, human-reviewed.