Klarix Book a call
Case study 04 · Growth analytics

Spend grew 71%. Paying customers grew 32%. Where did the money go?

A marketing performance diagnostic on a full year of anonymized campaign data from a Berlin-based subscription business, written for a CEO audience. It answers five questions, from "how are we doing?" to "where should the next euro go?", and ships as a tested, reproducible web app.

Open the live dashboard Preview it on this page
Python · pandas Flask · Chart.js Docker GCP Cloud Run GitHub Actions

€5.95M

marketing spend analysed across 4 markets and 24 channels, one full year

1.03×

blended return on ad spend, on confirmed payments only

62% at 0.52×

of paid budget went to one channel returning half of what it cost

€1.3M

proposed reallocation at the same total budget, with a 90-day plan

About the numbers

The company's name and identifying details are withheld. Every figure under "What happened" is computed from the dataset: 1,152 rows of channel-by-month data for 2024. The reallocation results are modeled estimates under stated assumptions, not observed outcomes.

My role: solo, covering the analysis, the metric definitions, the data validation, the web app and the deployment pipeline.

Share of paid budget vs return: paid social took 62% of budget at 0.52x return, paid search returned 3.80x on 1.3% of spend

The brief

Five questions a CEO asks about a marketing budget.

  1. 01

    How are we doing?

    An executive summary with the few numbers that matter.

  2. 02

    What should we do?

    Specific recommendations, each backed by a chart.

  3. 03

    Where should money move?

    A channel-by-channel verdict: scale, reduce or stop.

  4. 04

    What is unusual?

    Patterns and outliers, and what they imply.

  5. 05

    What can't we see?

    The data gaps that limit how far any of this can be trusted.

What happened

Volume went up. The quality of that growth went down.

Indexed from January to December, spend grew 39 points faster than paid conversions. By the fourth quarter, cost per paying customer was 23% higher than in the first, and the channel mix hid it.

Finding 1

Paying more for the same growth

€60 → €90

The dominant paid channel took 62% of budget at €68.97 per paying customer and 0.52× return. Its spend grew 132% across the year while its cost per customer rose 50%.

Finding 2

The best channels were underfunded

3.80× on 1.3%

Paid search returned 3.80× at €9.71 per paying customer on 1.3% of spend. Influencer partnerships converted 29% of trials to paid, the highest of any paid channel.

Finding 3

One market performing, one leaking

26% vs 17.6%

The core market converted 26% of trials to paid at 1.17× return. The weakest converted 17.6%: a funnel problem, not a media problem, so more spend there would not fix it.

Markets

Cost per customer alone would have picked the wrong market.

Only one market paid back its spend. Trial-to-paid conversion separated a market that needs better media from one that needs a better checkout.

Market Cost per paying customer Return on spend Trial to paid Verdict
Core €33.41 1.17× 26.0% Flagship
Secondary €48.04 0.85× 27.5% Optimize media
Emerging €43.37 0.87× 17.6% Fix funnel first

Core: protect and feed

The only market above 1.0× return. Its paid search returned 7.71×.

Secondary: buy smarter

The highest intent and revenue per user, but 44% higher acquisition cost than core and the heaviest coupon use.

Emerging: don't scale yet

Low trial-to-paid points at payment options, content depth or paywall friction. More spend would buy more trials that don't convert.

The recommendation

Move €1.3M without spending a euro more.

Same total budget, moved from the saturated channel and two weak ones to the channels with the best cost and conversion quality, plus a small test budget. These are modeled estimates, not results.

Channel Current Proposed Change Paid customers (modeled)
Paid social (dominant)€3.71M€2.40M−€1.31M−19,000
Influencer partnerships€1.70M€2.40M+€701k+18,750
Paid search€75k€600k+€525k+54,070
Podcast partnerships€3k€250k+€247k+25,000 to 49,000
Underperforming social€447k€0−€447k−6,400
Test budget (new)·€298k+€298k+7,500

+30k to 55k

net paid customers, the conservative range

+€1.2M to 2.2M

implied revenue lift

€38 → ~€28

blended cost per paying customer

The rows assume each channel keeps its current cost per customer as it grows, with influencer spend capped at +60% for diminishing returns. The headline range is well below the sum of the rows, because small channels rarely keep their efficiency at ten or eighty times the spend.

90-day plan

Each phase has an owner and a number that says whether it worked.

  1. Weeks 1 to 2 · Stop the bleeding

    Cap the dominant channel's spend, stop two low-converting channels, and confirm the mid-year pause of a third.

    Success: the dominant channel's cost per customer stops rising.

  2. Weeks 3 to 6 · Reallocate

    Raise influencer budget 40% in the two strong markets, scale paid search to 10% of paid budget, and pilot podcasts.

    Success: blended cost per paying customer below €35.

  3. Weeks 7 to 12 · Fix the funnel

    Deep-dive the weak market, A/B test checkout, payment options and paywall copy, and run a geo-holdout test to measure true incrementality.

    Success: trial-to-paid above 22%.

Definitions

Three metric choices that change the answer.

Most marketing debates are definition debates in disguise. Each choice below is written down, tested, and shown in the app next to the numbers it produces.

Cost per paying customer

spend ÷ (monthly + yearly paid conversions)

Counted after the trial, not at sign-up. A channel that buys trials that never pay looks cheap on sign-ups and expensive here.

Return on ad spend

invoiced revenue ÷ spend

On confirmed payments, by invoice date. Revenue by sign-up date would count trial starters who never pay and overstate the return.

Trial to paid

yearly paid ÷ yearly conversions

The quality signal cost alone can't show. It is what separated the influencer channel, and the weak market, from the rest.

How it is built

An analysis that refuses to publish if the data or the formulas are wrong.

  1. Step 1 · Test the formulas

    11 tests you can check by hand

    Every metric formula runs against a tiny dataset where the right answer is mental arithmetic. Free channels must return "undefined", never zero.

  2. Step 2 · Validate the data

    8 checks before any analysis runs

    Missing columns, missing months, negative spend or impossible conversions stop the build. Minor oddities are flagged for the data team instead.

  3. Step 3 · Compute once

    All analysis at build time

    Five scripts, one per question, write their results as JSON. The web server only serves those results; it never touches raw data.

  4. Step 4 · Ship

    Push to main, live in minutes

    GitHub Actions builds the Docker image and deploys it to Cloud Run in the EU. If a test or a data check fails, the broken version is never deployed.

One the checks caught

One row reported a coupon share of 1.33, which is impossible for a share. The build flags it for the data team and caps it at 1.0 for the analysis, so it is visible instead of silently skewing the coupon numbers.

The live dashboard

The full analysis, as the CEO would read it.

Interactive charts, every channel's verdict, the market cards and the metric definitions. Scroll inside the frame, or open it in its own tab for the full view.

Open the dashboard in a new tab Hosted on Google Cloud Run in the EU. The first load can take a few seconds.

What the data can't show

The most important finding was about the data itself.

A recommendation is only as strong as the data under it. These gaps set how far the numbers above can be trusted, and what to collect next.

64% of one market's revenue had no spend booked

Across 119 active rows, paid channels booked conversions and revenue with zero matching spend. That market's cost and return figures can't be compared with the others until it is fixed, and channels affected by it may look more efficient than they are.

No lifetime value or churn by channel

Without them there is no LTV-to-CAC ratio. A channel can be cheap and still acquire customers who leave after a month.

62% of rows have no conversion data

718 of 1,152 channel-months have no conversions or revenue recorded, so conversion analysis covers only the tracked subset.

A quarter of rows have no market

288 rows can't be attributed to any market, which limits per-market budget decisions.

Correlation, not incrementality

Monthly channel totals show what happened alongside spend, not what spend caused. That is why the plan ends with a geo-holdout test before the reallocation is locked in.

One year, no experiments marked

A single year gives no seasonal baseline, and without experiment flags a trend can't be separated from a test running at the same time.

If your spend grows faster than your customers

The answer is usually in data you already have.

Measure cost per paying customer, not per sign-up, and return on confirmed revenue, not on trials.

Look at trial-to-paid next to cost. It is the fastest way to tell a media problem from a funnel problem.

Check that spend and revenue are booked against the same channels before you trust any return figure.

If your marketing budget is growing and you are not sure what it is buying, I am glad to walk through what a diagnostic like this would look like on your data.

Privacy

The company is anonymized throughout. All processing runs inside a Docker container at build time; no raw data is reachable through the web app. The code is on GitHub; the dataset is not.

More work

A three-part benchmark on putting AI on top of a semantic layer: BigQuery and Cube, dbt and MetricFlow, and Power BI.