Metrics & Pricing

How to predict customer churn in SaaS with a weekly baseline

A spreadsheet method to spot B2B SaaS accounts likely to churn: compare each account with its own 8-week baseline, separate fixable risk from noise, and act on Mondays.

Isometric 3D illustration of a chunky acid lime green life ring with five off-white bands and a thin grey rope along its edge, tilted toward the viewer against a pale grey background.

You run a B2B SaaS with a few dozen or a few hundred accounts, and you find out a customer was unhappy when the cancellation email arrives. You do not have a customer success team or a churn model. You can still spot many cancellations weeks ahead: compare each account’s usage, seats and billing this week with that same account’s normal week, and act on the few that left their normal range.

Churn is the share of customers or revenue you lose in a period, and every lost account must be replaced before you grow. For the revenue side, see how NDR and GRR measure retention and how churn sets your LTV. This guide is about the step before the loss: spotting the accounts at risk while you can still do something.

What does it mean to predict churn?

Think of it like a doctor reading your resting heart rate. A rate of 75 is fine for one person and a warning for an athlete whose normal is 50. The doctor compares you with your own history, not with the average patient.

Predicting churn works the same way. An account with 40 active users is healthy if it always has 40, and in trouble if it usually has 60. So the method has three parts:

  1. A baseline per account: what a normal week looks like for that customer.
  2. A normal range: how much that customer moves in ordinary weeks.
  3. Signals: specific, checkable events that mean “look at this account now”.

You are not forecasting a probability. You are ranking accounts by evidence.

The data you need

Export one row per account per week. Keep at least 9 weeks: 8 for the baseline and 1 for the current week.

Column Example Where it comes from
account_id, account_name acc_1001, Example Co Your database or billing system
week_start 2026-11-02 The Monday of the week
active_users 29 People who logged in or used a core feature that week
seats 42 Paid seats on the subscription
mrr 900 Monthly recurring revenue (MRR) of the account
card_exp 2026-12 Card expiry month, from billing
failed_payments 0 Failed charge attempts that week

Define “active” once and keep it; using your core feature beats merely logging in. The SaaS analytics guide covers where this data usually lives.

Step 1: build each account’s baseline

For each account, take the previous 8 weeks of active users, not including the current week. The baseline is their median: sort the 8 values and take the middle one (with 8 values, the average of the 4th and 5th).

Use the median, not the average: one holiday week moves an average a lot and a median very little. If an account has fewer than 4 weeks of history, do not judge its usage yet.

Step 2: measure how much the account normally moves

Accounts differ in how jumpy they are. A team that varies between 41 and 58 is normal for them. A team that always sits between 38 and 43 is steady, so a drop to 29 means something.

To measure the normal movement, use the median absolute deviation (MAD):

  1. Subtract the baseline from each of the 8 weeks and drop the minus signs.
  2. Take the median of those 8 distances. That is the MAD.
  3. Multiply the MAD by 1.4826. The result plays the role of a standard deviation, but one wild week cannot distort it. The 1.4826 factor is the standard scaling constant, the same default R’s mad function uses.
  4. Set a floor: the spread is never smaller than 10% of the baseline, or 1 person. Without a floor, a perfectly flat account would flag a change of one user.

The normal range is the baseline plus or minus 2 times that spread.

In a spreadsheet with the 8 previous weeks in B2:I2, this is the setup in Excel 365:

  • Baseline: =MEDIAN(B2:I2)
  • MAD: =MEDIAN(ABS(B2:I2-MEDIAN(B2:I2)))
  • Spread: =MAX(1.4826*MAD, 0.1*Baseline, 1)
  • Low end of the normal range: =MAX(0, Baseline-2*Spread)

In Google Sheets, wrap the MAD formula in ARRAYFORMULA(...).

Step 3: check the signals

A usage dip alone is weak evidence. These are the signals that point to a fixable risk, each one a fact you can show the customer or yourself:

  1. Usage dropped. Active users are at least 25% below the baseline, below the low end of the normal range, and at least 2 people lower. All three conditions, so tiny teams and jumpy accounts do not trigger it.
  2. Usage stopped. Zero active users for 2 weeks in a row, on an account that used to have users.
  3. Seats removed. Fewer seats than the account’s seat baseline. Write it as a number and a percent, such as “-3 seats (7%)”: 3 seats out of 5 is a different story from 3 out of 200.
  4. Failed payments. Any failed charge this week. Some cancellations are not decisions at all: the card stops working.
  5. Card expiring. The card on file expires within 31 days, or has already expired.
  6. MRR down. MRR is at least 10% below its own baseline: a partial downgrade, which often comes before a full cancel.
  7. Paying but unused. Zero active users in the current week and over the baseline weeks, while the account still pays. Every renewal is a fresh chance for them to cancel.

Treat everything else as noise: dips inside the normal range, changes of one person on a tiny team, and weeks when your whole customer base dips together. If the typical account is down 15% or more in the same week, you are probably looking at a holiday, not at churn, so judge each dip against that shared shift.

Worked example: two accounts, one real risk

Example: two hypothetical accounts with round numbers. Account A has 45 seats; Account B has 60. Both have 8 weeks of history before the week of November 2, 2026.

Week Account A active users Account B active users
Weeks 1-8 40, 42, 38, 41, 39, 43, 40, 41 50, 44, 56, 47, 53, 41, 58, 51
This week 29 40
Change vs last week 41 to 29, -29% 51 to 40, -22%

A rule like “alert on a 20% weekly drop” flags both. The baseline method flags only one.

Account A. Sorted, the 8 weeks are 38, 39, 40, 40, 41, 41, 42, 43. The baseline is (40 + 41) / 2 = 40.5. The distances from 40.5 have a median of 1, so the raw spread is 1.4826 x 1 = 1.48. The floor is 10% of 40.5 = 4.05, which is larger, so the spread is 4.05. The normal range is 40.5 plus or minus 8.1: 32.4 to 48.6.

This week’s 29 users is (29 - 40.5) / 40.5 = -28% versus the baseline. It is below 32.4, and 11.5 people lower. All three conditions hold: usage dropped. Seats also fell from 45 to 42, which is -3 seats (7%). Two signals, both fixable.

Account B. Sorted: 41, 44, 47, 50, 51, 53, 56, 58. The baseline is (50 + 51) / 2 = 50.5. The distances are 9.5, 6.5, 3.5, 0.5, 0.5, 2.5, 5.5 and 7.5; their median is (3.5 + 5.5) / 2 = 4.5. The spread is 1.4826 x 4.5 = 6.67, above the floor of 5.05. The normal range is 50.5 plus or minus 13.3: 37.2 to 63.8.

This week’s 40 users is -21% versus the baseline, not -25%, and it sits inside the normal range. Account B has dipped like this before. It is noise: check again next Monday.

Account A Account B
Baseline (median of 8 weeks) 40.5 50.5
Normal range 32.4 to 48.6 37.2 to 63.8
This week 29 (-28%) 40 (-21%)
Seats -3 seats (7%) No change (0%)
Verdict Fixable risk Noise

Both fell more than 20% in a week, but only the steady account left its normal range.

Turn signals into a Monday list

You now have signals for every account. Rank them so the list fits in one inbox view:

  1. Give each fixable signal a weight (more for payment problems and large usage drops, less for a small seat change) and keep noise near zero.
  2. Add the weights per account, sort from high to low and break ties by MRR.
  3. Keep the top 5, a list you will actually work through on a Monday.
  4. For each one, write 1 or 2 reasons with the raw numbers (“29 active users vs 40.5 baseline, normal range 32 to 49”) and one next step.

The next step matters more than the score. A failed payment needs a link to update the card. A usage drop needs a question: “What changed for your team?” Removed seats need a check on whether your main contact left.

Doing it with a tool

The spreadsheet above works, but someone has to export the data, update it and read it every week. MondayKeep runs this same method for you. You upload a weekly CSV with the columns from the table above, and every Monday it emails you the 5 accounts most likely to churn. Each account is compared with its own 8-week baseline and normal range as above, seats are shown as a number and a percent, and each account is labeled “fixable risk” or “noise” with 1 or 2 reasons and the raw evidence behind them. The landing page shows a sample report built from a fictional sample dataset, not from real customers.

It costs US$29 a month, billed by Stripe, and you can cancel anytime (as of November 2026). Version 1 reads CSV only; it does not connect to your billing system or product database directly.

Disclosure: MondayKeep is SaaSCron’s own product. The method in this guide works the same in a spreadsheet.

Common mistakes

  1. Comparing accounts with each other. A 30-person drop is huge for a 40-seat customer and normal for a 400-seat one. Why it happens: a single company-wide threshold is easy to set. Instead: compare each account with its own baseline and range.
  2. Ignoring billing signals. Failed payments and expiring cards are churn you can often prevent with one email, and they never show up in usage data. Why it happens: product and billing data live in different tools. Instead: put card expiry and failed payments in the same weekly export.
  3. Treating every dip as a risk. If 20 accounts are flagged every week, you will stop reading the list. Why it happens: it feels safer to flag more. Instead: label dips inside the normal range, tiny-team changes and base-wide dips as noise, and keep the list to 5.
  4. Flagging without a next step. A score tells you who; it does not tell you what to do. Why it happens: the analysis feels like the work. Instead: attach one concrete action to every flagged account and do it that week.

Sources

  • R documentation, mad: median absolute deviation (default constant 1.4826), checked November 9, 2026.
  • MondayKeep scoring rules and pricing, from its product page and documentation, checked November 9, 2026.

In three sentences

You can spot B2B SaaS churn risk without a model by comparing each account’s week with its own 8-week median and its normal range of plus or minus 2 x 1.4826 x MAD. In the example, Account A’s drop from a 40.5 baseline to 29 users is a fixable risk while Account B’s dip to 40 stays inside its 37.2 to 63.8 range and is noise. Add billing signals like failed payments, expiring cards, removed seats and downgrades, keep the top 5 accounts, and give each one a concrete next step on Monday.

Frequently asked questions

Can you predict churn without a machine learning model?

Yes. A weekly comparison of each account with its own recent history catches the common warning signs: usage out of its normal range, seats removed, failed payments, expiring cards and downgrades. A model needs many past cancellations to learn from, which a young company rarely has.

What data do I need to predict churn in a B2B SaaS?

One row per account per week: account id, week start, active users, seats and MRR, plus card expiry and failed payments from your billing system.

Why use the median instead of the average for a baseline?

One unusual week, like a holiday or an onboarding spike, moves an average a lot and a median very little, so the median of the last 8 weeks describes a normal week for that account.