Skip to main content

Marketing Data Hub

Your marketing data, on your machine, free. An open-source Windsor.ai alternative: pulls Google Analytics 4, Search Console (plus Google Ads, Meta Ads, YouTube) into one local database — queryable via a REST API, scheduled CSV exports, and by AI assistants like Claude (MCP). No hosted service, no subscription, your tokens never leave your computer.

Quick start

Windows: download MarketingDataHub-Setup.exe (or see all versions on the releases page) and run it — the setup page opens in your browser.

Mac: download the disk image for your Mac — Apple Silicon (M1 and later) or Intel — open it and drag Marketing Data Hub onto Applications. The first time, macOS warns about an unidentified developer: right-click the app, choose Open, then Open again. The setup page opens in your browser.

Anywhere with Python 3.11+:

python3 -m pip install marketing-data-hub
python3 -m hub.cli setup

(Use python3 -m pip/python3 -m hub.cli rather than bare pip/hub — on Mac especially, pip on your PATH can point at a different Python than python3, so a bare pip install can succeed while hub still isn't found. python3 -m ... always installs into and runs from the same interpreter.)

(Developers: git clone https://github.com/Bhargava-R-dev/marketing-data-hub and pip install -e ".[dev]" instead.)

The setup page walks you through: sign in to Google → tick the GA4 properties / Search Console sites you want → watch the first fetch load, account by account → connect Claude (one click) → done, with a Marketing Data Hub icon on your Desktop / Start Menu. That icon is the everyday entry point: add or remove accounts, refresh, open the dashboard — no command window (hub shortcut recreates it).

Data is live. Nothing syncs in the background and the laptop doesn't need to be on at any particular time: when you ask Claude a question (or call the API / run an export), the hub fetches what that question needs from Google, keeps it briefly so repeat questions are instant, and drops it after 45 days so the folder stays small. If Google can't be reached or a login has expired, you still get the last cached numbers, plus a plain warning saying why they may be old. hub cache status / hub cache clear show or free the space. Prefer the old behaviour (only read what hub sync stored)? Set mode: stored in config.yaml.

Your hub lives in %LOCALAPPDATA%\MarketingDataHub (Windows) or ~/.marketing-data-hub (Mac/Linux); nothing to configure. A config.yaml in the current folder takes precedence, so existing checkouts keep working. Prefer your own Google Cloud project over the bundled sign-in? Drop your google_client.json in that folder's secrets/ — the setup page shows which one is in use (SETUP.md step 2).

Never used a terminal? → GUIDE.md is a complete, plain-English walkthrough from installing Python through asking your first question — written for non-technical teammates, and made to be shared.

Then ask Claude things like "How did organic traffic do in June vs May?" or "Top non-branded search queries this month?". Want to keep Search Console history longer than Google's ~16 months? Store it with hub backfill (stored data is never evicted), optionally on a schedule (SETUP.md, step 8).

Reports (analysis shapes)

Each source syncs several named reports — different dimensional shapes of the same data, stored side by side and never mixed (mixing granularities would double-count):

Source Report Answers
ga4 core daily campaign totals (sessions, users, conversions, revenue)
ga4 channels traffic mix: organic vs paid vs direct, engagement, pageviews
ga4 landing_pages entry-page performance per channel
ga4 pages page behaviour: views, engagement time, events per path
ga4 audience device × country segmentation
ga4 visitors new vs returning (cohort-lite)
gsc core exact daily search totals per site
gsc queries per-query performance (branded split = string-match)
gsc pages per-URL search performance
gsc devices / countries mobile/desktop and geo splits
ga4 events per-event counts by name (brand-specific: form_submit, call_click...)

GA4 breakdown reports exclude GA4's unattributable (other) bucket, so they sum to slightly under the topline (on very large properties, well under for high-cardinality dims like landing pages) — use core for exact totals, breakdowns for composition/ranking. Same idea as GSC query anonymisation.

Pass report=<name> to the API/MCP query_metrics; default is core. MCP query_metrics also supports compare= (prev_period / prev_day / prev_week / prev_month / prev_year — returns value, previous, and %-change per metric for any date range) and filters= (exact match on any dimension incl. report extras, e.g. {"event": "form_submit"} or {"device": "MOBILE"}). Rates are computed, not stored: engagement rate = engaged_sessions/sessions, ctr = clicks/impressions, avg engagement time = engagement_seconds/pageviews. GSC breakdown reports undercount totals slightly (Google anonymises rare queries) — use core for toplines. True user-level cohorts need the GA4 BigQuery export; visitors + the live tools cover cohort-lite analysis.

For anything the synced reports don't cover, the MCP tools query_ga4_live and query_gsc_live pass arbitrary dimension/metric combinations straight to the APIs on demand.

Setup

New here / installing on another machine? Follow SETUP.md — a step-by-step guide including the Google Cloud OAuth setup. Quick version:

  1. python -m pip install -e ".[dev]"
  2. Copy config.yaml.example → config.yaml; fill in your GA4 property_id and Search Console site_url. Have multiple GA4 properties or Search Console sites under the same Google login? Use property_ids: [...] / site_urls: [...] instead — all of them sync, and every row is tagged with its own account_id so they stay distinguishable downstream.
  3. Copy .env.example → .env; set a random HUB_API_KEY.
  4. Google Cloud Console → create a project → enable Google Analytics Data API, Google Analytics Admin API, Search Console API, YouTube Analytics API → create an OAuth client (Desktop app) → download JSON to secrets/google_client.json. (See SETUP.md for the OAuth consent-screen steps and the 7-day token-expiry gotcha.)
  5. hub doctor — first run opens a browser to authorize; then all checks go green.
  6. hub accounts --add — pick which GA4 properties / GSC sites to sync from everything your Google login can see.

Daily use

Command What it does
hub sync all sync every configured source (rolling 30-day window)
hub backfill ga4 --from 2024-01-01 load history in 90-day chunks
hub status row counts + last sync per source
hub serve query API on 127.0.0.1:8000 + cron scheduler
hub export all write configured CSVs to exports/
hub mcp MCP server (stdio) for Claude

Query API

GET /connectors/all/data?fields=date,source,clicks,spend&date_preset=last_30d
X-API-Key: <HUB_API_KEY>

format=csv for CSV, report=<name> for a breakdown report. /connectors lists sources; /connectors/{source}/reports lists report shapes; /connectors/{source}/fields?report=<name> lists fields.

Claude MCP

claude mcp add marketing-hub -- python -m hub.cli mcp --config <absolute-path>/config.yaml Then ask Claude: "How did my campaigns do last week?"

Note: use an absolute path for --config; the MCP process may be launched from a different working directory.

trigger_sync starts the sync in the background and returns immediately (output goes to logs/mcp_sync.log); poll sync_status to see when it finishes. While a sync holds the write lock, query tools return a readable "database is busy" error instead of hanging.

Activating the ad connectors

  • Google Ads: apply for a developer token (API Center), then uncomment google_ads in config.yaml and fill options.
  • Meta Ads: create a Meta app, generate a long-lived token with ads_read, uncomment meta_ads and fill options.

Known limitations

  • DuckDB allows one writer: run hub mcp OR hub serve, not both at once (trigger_sync from MCP spawns the CLI, which needs the write lock free). While any sync runs, MCP query tools report "database is busy" until it finishes (~3 min for sync all).
  • Extras fields (e.g. position, ctr, views) are returned as strings by the query API — cast numerically as needed.

Metadata

Release files for marketing-data-hub 0.7.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for marketing-data-hub 0.7.1
File Size Uploaded Provenance Actions
marketing_data_hub-0.7.1.tar.gz 1.6 MB With provenance Details for marketing_data_hub-0.7.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for marketing-data-hub 0.7.1
File Interpreter ABI Platform Actions
marketing_data_hub-0.7.1-py3-none-any.whl Python 3 none any Details for marketing_data_hub-0.7.1-py3-none-any.whl

Total release size: 3.0 MB

Release files / marketing_data_hub-0.7.1.tar.gz

Download URL marketing_data_hub-0.7.1.tar.gz
Size 1.6 MB
Tags Source
SHA-256 checksum
How to use checksums
daeb9970261bdead4ac38428867d95a600e239b4e06194899cc63d782837c788
BLAKE2b-256 checksum
How to use checksums
6d65225a7c60275262e02e4bf3565f83ae20d3895a6085d94c742d779c0e130a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release files / marketing_data_hub-0.7.1-py3-none-any.whl

Download URL marketing_data_hub-0.7.1-py3-none-any.whl
Size 1.3 MB
Tags Python 3
SHA-256 checksum
How to use checksums
b3999e6447d57b0b15d8c1c3cf6cde7fa4e657b6058fbe348fa3c0dfc6810a75
BLAKE2b-256 checksum
How to use checksums
9607cfdace66f3e8a6db5aaece495138ec15a70fec948f6483ee83e3c09eb759
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Security for marketing-data-hub 0.7.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for marketing_data_hub-0.7.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page