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.
Windsor vs Coupler.io

