calendar Last updated: 1 September 2026
Connecting data with Google Meridian for MMM using Windsor.ai
Attribution modelling & analytics
How to's

Connecting Data with Google Meridian for MMM Using Windsor.ai [Python Tutorial]

Try It Now

Connect your data in 1 min. Start free, no credit card

Got insights from this post? Give it a boost by sharing with others!

Google Meridian is an open-source library developed by Google to implement Bayesian Marketing Mix Modeling (MMM). It helps marketers understand the effectiveness of their advertising channels by estimating how different media inputs (like ad spend or impressions) impact a key business metric (KPI), such as conversions or revenue.

Meridian uses probabilistic modeling to provide insights into return on investment (ROI), channel contribution, and forecasted outcomes, so you can make data-driven decisions about budget allocation.

Using the Windsor.ai data integration platform, you can automatically fetch, transform and prepare your marketing data for use with Meridian. Windsor.ai connectors aggregate data from your ad platforms into one dataset the MMM pipeline can read directly.

How to use Windsor.ai data with Google Meridian for MMM

This tutorial explains how to connect Windsor.ai’s marketing data with Google’s Meridian library to build a Marketing Mix Model (MMM), using the working example in our WindsorMeridian repository.

If you would rather not work with the code at all, we also have a no-code Docker version of this guide, which runs the same model from a pre-built container and prompts you for your inputs. This page is the route to take when you want control over the model configuration.

You will learn how to:

  • Fetch marketing data from Windsor.ai
  • Shape it into the format Meridian expects
  • Configure the model priors
  • Fit the model
  • Generate a summary report

Prerequisites:

  • Python 3.11 or higher
  • A Windsor.ai API URL for your ads data, with your API key, date_preset and fields
  • A second Windsor.ai API URL for your conversions data (the example uses GA4)
  • Working knowledge of Google Meridian MMM

How to run the source code

The repository contains two files you need: data.py, which pulls and shapes your Windsor.ai data, and model.py, which builds and fits the model.

1. Install the required libraries.

pip install google-meridian tensorflow tensorflow-probability pandas requests psutil

2. Add your Windsor.ai API URLs. Open data.py and replace the two placeholders with the API URLs from your Windsor.ai dashboard:

api_url = "[your_ads_api_url]"                    # your ads data
conversions_api_url = "[your_conversions_api_url]"  # your conversions data

3. Adjust the reporting dates. Near the end of model.py, start_date and end_date are fixed values. Change them to a range that exists inside your own dataset, otherwise the summary will not cover your data:

start_date = '2024-04-24'   # change to match your data
end_date = '2025-04-23'     # change to match your data

4. Run the model.

python model.py

When the run finishes, summary_output.html is written to the folder you ran it from.

Step 1: Fetch data from Windsor.ai

Get your API URL from the Windsor.ai dashboard and use it to fetch daily performance metrics across your marketing channels. The example pulls:

  • Date
  • Source
  • Impressions
  • Clicks
  • Spend

Conversions come from a second call. In the example these are GA4 event counts, grouped by date and renamed to conversions. You can swap in whatever conversion source you use.

What data.py does with it. The script groups the data by date and source, pivots it so each channel gets its own columns, and flattens the result into names like facebook_impressions and google_spend. It then merges in conversions, resamples to one row per day, and fills any gaps with zero. Meridian needs an evenly spaced daily time series, which is what this produces.

In the example we use data from Google, Facebook, Bing and Reddit Ads, modelling conversions against spend.

The function returns the finished dataset as an in-memory CSV, which is handed straight to Meridian. Nothing is written to disk at this stage.

Step 2: Map your columns to Meridian’s format

Meridian needs to be told which column means what. CoordToColumns does that mapping, and two dictionaries connect each data column to the channel it belongs to.

from meridian.data import load

coord_to_columns = load.CoordToColumns(
    time="date",            # timestamp column
    geo="geo",              # region column, if you have one
    controls=[],            # external control variables
    kpi="conversions",      # the target variable
    revenue_per_kpi=None,   # set if you are modelling revenue
    media=["facebook_impressions", "google_impressions", "reddit_impressions", "bing_impressions"],
    media_spend=["facebook_spend", "google_spend", "reddit_spend", "bing_spend"],
)

correct_media_to_channel = {
    "google_impressions": "Google_Ads",
    "facebook_impressions": "Facebook_Ads",
    "reddit_impressions": "Reddit_Ads",
    "bing_impressions": "Bing_Ads",
}

correct_media_spend_to_channel = {
    "google_spend": "Google_Ads",
    "facebook_spend": "Facebook_Ads",
    "reddit_spend": "Reddit_Ads",
    "bing_spend": "Bing_Ads",
}

loader = load.CsvDataLoader(
    csv_path=csv_data,
    kpi_type='non_revenue',   # use 'revenue' if you supply revenue_per_kpi
    coord_to_columns=coord_to_columns,
    media_to_channel=correct_media_to_channel,
    media_spend_to_channel=correct_media_spend_to_channel,
)
data = loader.load()

Edit these lists to match your own channels. The column names above come from whatever sources you connected in Windsor.ai, so if you are pulling TikTok or LinkedIn instead, the names change accordingly.

A note on the geo column. The example maps geo="geo", but the data prepared in data.py has no geo column, since it aggregates nationally. If you are not modelling by region, either add a dummy geo column or set this to None before running.

For better results, consider adding control variables such as seasonality and time effects, economic indicators, competitor activity, product or price changes, and organic demand proxies, along with any organic media or non-media treatments like promotions.

Step 3: Configure model priors

Set your ROI expectations using a LogNormal distribution, which is used because ROI is strictly positive. The important detail is that roi_mu is on the log scale, not the raw ROI scale.

import tensorflow_probability as tfp
from meridian import constants
from meridian.model import prior_distribution, spec, model

# roi_mu is ln(ROI). If you expect about 0.5 conversions per $1, ln(0.5) is about -0.6931
roi_mu = -0.6931
roi_sigma = 0.5   # uncertainty around the ROI prior

prior = prior_distribution.PriorDistribution(
    roi_m=tfp.distributions.LogNormal(roi_mu, roi_sigma, name=constants.ROI_M)
)

model_spec = spec.ModelSpec(prior=prior)
mmm = model.Meridian(input_data=data, model_spec=model_spec)

mmm.sample_prior(100)

mmm.sample_posterior(
    n_chains=2,     # independent chains
    n_adapt=100,    # adaptation steps
    n_burnin=100,   # warm-up iterations
    n_keep=200,     # posterior samples kept
    seed=1,
)

Work out roi_mu from your own numbers rather than copying the value above. Take the ROI you realistically expect per dollar of spend and use its natural logarithm.

The sampling values here are deliberately small so the model finishes quickly while you are experimenting. Raise them once you are running it for real.

If your baseline comes out negative, the model is crediting all of your KPI to media. Two things usually fix it: tighten the ROI priors for paid channels so they are lower and narrower, and add control variables so seasonality, organic demand and economic effects have somewhere to go.

Step 4: Summarize and export results

After fitting, generate a summary report covering channel effectiveness, ROI estimates and contribution breakdowns.

from meridian.analysis import summarizer

mmm_summarizer = summarizer.Summarizer(mmm)
file_path = './'            # where to save the report
start_date = '2024-04-24'   # must fall inside your dataset
end_date = '2025-04-23'

mmm_summarizer.output_model_results_summary(
    'summary_output.html', file_path, start_date, end_date
)

The report shows how each channel is performing and whether the spend behind it is justified.

Conclusion

That is Windsor.ai data connected to Google Meridian for marketing mix modeling. The two things worth getting right are the column mappings in Step 2 and the ROI prior in Step 3, since both are specific to your data rather than copy-and-paste values.

Explore the full source code in our GitHub repository to try it on your own: https://github.com/windsor-ai/WindsorMeridian.

Ready to optimize your marketing spend? Get started with Windsor.ai and connect your data to Google Meridian. Connect your data in 1 minute. Free forever plan.

Tired of juggling fragmented data? Get started with Windsor.ai today to create a single source of truth

Let us help you automate data integration and AI-driven insights, so you can focus on what matters, growth strategy.
g logo
fb logo
big query data
youtube logo
power logo
looker logo