MRR by Marketing Channel: Stripe Revenue Analytics
Split Stripe MRR, churn and expansion by the channel that first brought each customer: the join, the script, the math and a worked example with real numbers.
To see MRR by marketing channel, tag every Stripe subscription with your own user id, record which channel first brought each user to your site, and join the two: sum each active subscription's monthly-normalized amount by the user's first-touch channel. Do the same for new, expansion, contraction and churned MRR each month, and you can see not just which channels acquire customers but which ones keep paying.
Stripe knows the money and nothing about marketing; your analytics knows the marketing and, at best, the first payment. This guide shows the join, a script that produces the report, the formulas, and a worked example where the channel that looks best on first payments turns out to be the worst on retained revenue.
Key takeaways
- MRR by channel is the sum of each customer's monthly-normalized subscription amount, grouped by the channel of their first visit.
- Stripe doesn't store traffic source, so you need a shared key, usually your own user id, on both the subscription and the analytics record.
- First-payment attribution answers which channel acquires customers; MRR by channel answers which channel's customers stay and grow.
- Churn and expansion often differ more between channels than acquisition does, which changes LTV:CAC rankings.
- Normalize yearly plans to monthly, exclude trials, and decide once how to treat past-due subscriptions.
Why can't Stripe show MRR by marketing channel on its own?
A Stripe subscription has a customer, prices, a status and metadata. It has no idea that the customer read a blog post in March, clicked a newsletter link in April and signed up in May. Stripe's own reports can break revenue down by product, price or country, but not by referrer or campaign, because that data never reaches Stripe unless you put it there.
Revenue attribution tools close part of the gap by passing an analytics visitor id into checkout. That is how VisitTrack credits a Stripe Checkout payment to the referrer, campaign and landing page of the visitor's first touch; the setup is in how to track revenue by traffic source. Be precise about what that gives you: the Stripe integration records the payment made in Checkout, which for a subscription is the first charge. Renewals are billed later as invoices, with no page involved. That answers “which channel acquires paying customers,” but not “which channel's customers are still paying in month nine.” For that, you need the join below.
What do you need before you can split MRR by channel?
| Piece | Where it lives | How to get it |
|---|---|---|
| Your user id on every subscription | Stripe subscription metadata | Set subscription_data.metadata.user_id when creating the Checkout Session |
| Your user id on the analytics side | Analytics identify record | Call identify(userId) at signup or login |
| First touch per user | Analytics | Referrer, UTMs and landing page of the first visit, kept per visitor |
| Monthly amount per subscription | Stripe | Price × quantity, normalized to a month, after discounts |
| A channel mapping | Your spreadsheet or script | Rules that turn referrer + UTMs into a channel name |
// When creating the Checkout Session (server)
const session = await stripe.checkout.sessions.create({
mode: "subscription",
line_items: [{ price: priceId, quantity: 1 }],
client_reference_id: visitorId, // first-payment attribution
subscription_data: { metadata: { user_id: user.id } }, // the join key for MRR
success_url: "https://example.com/welcome",
cancel_url: "https://example.com/pricing",
});On the analytics side, identify the user with the same id. In VisitTrack, every identified person keeps the source that first brought them in, and the people export includes it: GET /api/v1/people?format=csv returns externalId, plan and the first-touch referrer, UTM source, medium, campaign and landing path for each person. The Stripe-specific checkout setup is in the Stripe revenue docs.
How do you calculate MRR by channel, step by step?
- 1.List all subscriptions with status active (and past_due, if you count it), expanding prices.
- 2.For each, compute monthly amount: unit amount × quantity, divided by 12 for yearly prices (or by the interval in months), minus any recurring discount.
- 3.Read metadata.user_id. Subscriptions without it go to an “Unattributed” bucket; fix that at the source rather than guessing.
- 4.Export people with their first touch from analytics and map each to a channel.
- 5.Join on user id and sum monthly amounts by channel. That is MRR by channel.
- 6.Repeat at the end of each month and diff the snapshots per customer to get new, expansion, contraction and churned MRR by channel.
import Stripe from "stripe";
import { parse } from "csv-parse/sync";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
// 1. Monthly-normalized MRR per user from Stripe
const mrrByUser = new Map<string, number>();
for await (const sub of stripe.subscriptions.list({ status: "active", limit: 100 })) {
const userId = sub.metadata.user_id ?? "unattributed";
let cents = 0;
for (const item of sub.items.data) {
const { unit_amount, recurring } = item.price;
if (!unit_amount || !recurring) continue;
const months = recurring.interval === "year" ? 12 * recurring.interval_count
: recurring.interval === "month" ? recurring.interval_count
: recurring.interval === "week" ? recurring.interval_count / 4.345
: recurring.interval_count / 30.44; // daily
cents += (unit_amount * (item.quantity ?? 1)) / months;
}
// Apply recurring discounts here if you use coupons.
mrrByUser.set(userId, (mrrByUser.get(userId) ?? 0) + cents);
}
// 2. First touch per user from VisitTrack
const res = await fetch("https://visitrack.app/api/v1/people?format=csv", {
headers: { Authorization: `Bearer ${process.env.VISITRACK_API_KEY}` },
});
const people = parse(await res.text(), { columns: true }) as Record<string, string>[];
const channelByUser = new Map(people.map((p) => [p.externalId, toChannel(p)]));
// 3. Sum by channel
const mrrByChannel: Record<string, number> = {};
for (const [userId, cents] of mrrByUser) {
const channel = channelByUser.get(userId) ?? "Unattributed";
mrrByChannel[channel] = (mrrByChannel[channel] ?? 0) + cents / 100;
}
console.table(mrrByChannel);
function toChannel(p: Record<string, string>): string {
const src = (p.firstTouchUtmSource || p.firstTouchReferrer || "").toLowerCase();
if (p.firstTouchUtmMedium === "newsletter") return "Newsletter sponsorships";
if (p.firstTouchUtmMedium === "cpc") return "Paid search";
if (/google|bing|duckduckgo/.test(src)) return "Organic search";
if (/ycombinator|reddit/.test(src)) return "Communities";
return src ? "Other referral" : "Direct / unknown";
}Store each month's per-user snapshot (user id, channel, monthly amount). Comparing two snapshots gives the MRR movements: a user present only in the new one is new MRR, absent from the new one is churned MRR, and a changed amount is expansion or contraction. If you want to check the arithmetic on a single month by hand, the MRR calculator does the same normalization.
Can you do this without writing a script?
Yes, with two exports and a spreadsheet, which is a reasonable way to start before automating anything. Export active subscriptions from the Stripe dashboard (customer, price, interval, quantity and the user_id metadata column), and export people with their first touch from your analytics as CSV. In the spreadsheet, add a monthly-amount column (divide yearly prices by 12), add a channel column to the people sheet with a few lookup rules, join the two on user id with a lookup function, and build a pivot table of monthly amount by channel.
Save a copy of the joined sheet at the end of every month. Two saved months are enough to compute new, churned, expansion and contraction MRR per channel; six are enough to start looking at cohorts. When the monthly copy-paste becomes annoying, that is the moment to turn it into the script above and run it on a schedule.
The same join works for other payment providers. Paddle, Polar, Lemon Squeezy and Razorpay all let you attach your own data to a checkout or subscription (custom data, metadata or notes fields), so put your user id there and join on it exactly as with Stripe.
What does MRR by channel look like with real numbers?
A worked, illustrative example: a SaaS at $18,400 MRR with 460 paying customers, at the end of September.
| First-touch channel | Customers | MRR | ARPA |
|---|---|---|---|
| Organic search | 140 | $5,880 | $42 |
| Newsletter sponsorships | 60 | $3,000 | $50 |
| Communities (HN, Reddit) | 90 | $2,970 | $33 |
| Paid search | 70 | $2,520 | $36 |
| Direct / unknown | 100 | $4,030 | $40 |
| Total | 460 | $18,400 | $40 |
The snapshot is useful, but the movements are where the decisions are. Here is the same business's September, diffed against August:
| Channel | New MRR | Expansion | Contraction | Churned | Net new MRR | MRR churn rate |
|---|---|---|---|---|---|---|
| Organic search | +$630 | +$210 | −$40 | −$170 | +$630 | 3.2% |
| Newsletter sponsorships | +$450 | +$150 | $0 | −$100 | +$500 | 4.0% |
| Communities | +$330 | +$30 | −$30 | −$300 | +$30 | 10.2% |
| Paid search | +$540 | +$40 | −$20 | −$290 | +$270 | 12.9% |
| Direct / unknown | +$400 | +$120 | −$50 | −$150 | +$320 | 4.0% |
| Total | +$2,350 | +$550 | −$140 | −$1,010 | +$1,750 | — |
On first payments alone, paid search looks like the second-best channel: $540 of new MRR this month, behind only organic search. On retained revenue it is the worst: 12.9% of its MRR churned in a month, so nearly all of its new revenue is spent replacing customers it already lost. Communities have the same problem at a smaller scale. Newsletter sponsorships, which brought in less new MRR than paid search, are adding almost all of it to the base.
How does MRR by channel change LTV:CAC?
Continuing the example, compare the two paid channels using each one's own churn rate rather than a company-wide average:
| Paid search | Newsletter sponsorships | |
|---|---|---|
| Monthly spend | $1,800 | $1,200 |
| New customers | 15 at $36 | 9 at $50 |
| CAC | $120 | $133 |
| Monthly MRR churn | 12.9% | 4.0% |
| Expected lifetime (1 ÷ churn) | 7.8 months | 25 months |
| LTV (ARPA × lifetime) | $279 | $1,250 |
| LTV:CAC | 2.3 | 9.4 |
With a company-wide churn rate, these channels look similar: paid search even has the lower CAC. Per-channel churn reverses the decision. The definitions behind these numbers are in the glossary under LTV:CAC ratio and net revenue retention; the CAC calculator handles the acquisition side if your spend is spread across tools and people.
Which MRR metrics should you track per channel?
| Metric | Formula | Question it answers |
|---|---|---|
| MRR by channel | Sum of active monthly amounts, grouped by first touch | Where does today's recurring revenue come from? |
| New MRR by channel | MRR of customers new this month | Which channel is acquiring now? |
| MRR churn rate by channel | Churned MRR ÷ starting MRR | Whose customers leave? |
| Expansion rate by channel | Expansion MRR ÷ starting MRR | Whose customers grow into bigger plans? |
| Net revenue retention by cohort | MRR of a cohort now ÷ its starting MRR | After 6 or 12 months, did a channel's cohort grow or shrink? |
| Payback by channel | CAC ÷ (ARPA × gross margin) | How fast is spend recovered? |
What mistakes make MRR by channel misleading?
- Using last touch. A customer who first came from search and later clicked a retargeting email would be credited to email. For MRR by channel, use the first touch you recorded at signup and never overwrite it.
- Counting trials as MRR. A trialing subscription hasn't paid anything; include it only once it converts.
- Mixing yearly plans in at full value. A $480-a-year plan is $40 of MRR, not $480.
- Ignoring the Unattributed bucket. If it is more than about 10% of MRR, fix the metadata or identify step before trusting the split.
- Reading tiny channels. A channel with 8 customers can show 0% or 25% churn in a month by chance. Use trailing three-month averages for small channels.
- Comparing cohorts of different ages. A channel you started last quarter has only young customers, who churn at different rates than old ones. Compare cohorts at the same age.
For the earlier question of which channels bring paying customers in the first place, including how much data you need before cutting a channel, see which marketing channels bring paying customers.
How do I see MRR by marketing channel in Stripe?
Stripe doesn't store marketing channels, so you need to join it with your analytics. Put your user id in the subscription's metadata, record each user's first-touch channel in analytics, then sum each active subscription's monthly amount by that channel.
Should MRR by channel use first-touch or last-touch attribution?
First touch, recorded at signup and kept fixed. It reflects which channel created the customer, and it doesn't change every time the customer clicks a later email or ad, which would make month-over-month comparisons meaningless.
How do I handle annual plans in MRR?
Divide the annual amount by 12 and count that as monthly recurring revenue for every month of the term. Don't add the full annual payment to the month it was collected.
What is a good MRR churn rate by channel?
There is no universal number; it depends on price point and customer type. The useful comparison is between your own channels: a channel whose MRR churn is several times higher than the others is buying customers who don't stay.
Does revenue attribution track subscription renewals?
It depends on the integration. VisitTrack's Stripe integration records the payment made through Checkout, which for subscriptions is the first charge; renewals billed as invoices aren't attributed. MRR by channel needs the subscription join described here.
How many customers do I need before MRR by channel is meaningful?
Roughly 30 or more customers per channel for monthly churn rates to be stable. Below that, use trailing three-month numbers and focus on cohort retention over several months.
Keep reading
- How to Increase SaaS Landing Page Conversion RateMeasure landing page conversion per traffic source first, then fix the biggest leak: message match, the offer above the fold, signup friction and proof.
- AEO: How to Write Pages AI Answer Engines QuoteAnswer engine optimization at the page level: question headings, answer-first sentences, quotable specifics and clear limits that ChatGPT and AI Overviews lift.
- How to Track Signups by Traffic Source Without CookiesCapture each visit's referrer, UTMs and landing page, carry that first touch into your signup, and credit every new account to the source that earned it.
See which channels actually bring paying customers
VisitTrack is cookie-free analytics with revenue attribution built in. One script tag, no consent banner, live in two minutes. 14 days free, no card required.
14-day free trial · No card required · Cookie-free · Cancel anytime
Or explore the live demo first →