Analytics & Data8 min read

How to Integrate Fathom with Your Headless CMS

Connect Fathom to your headless CMS so every published page, campaign, and CTA can report privacy-first performance data without extra cookies or ad-tech scripts.

Published April 29, 2026
01Overview

What is Fathom?

Fathom is a privacy-focused web analytics platform for teams that want pageviews, referrers, conversions, and top content without cookie banners in many regions. It’s used by publishers, SaaS teams, agencies, and privacy-conscious companies that need simple traffic reporting instead of product analytics with user profiles. Its core workflow is a lightweight tracking script, custom events, site dashboards, and an API for site setup and analytics reporting.


02The case for integration

Why integrate Fathom with a headless CMS?

If your content team publishes 20 articles, 6 landing pages, and 3 campaign pages a week, analytics gets messy fast. Fathom can show which URLs are getting traffic, where visitors came from, and which custom events fired, but it only sees what your frontend sends. Your headless CMS has the missing context: article type, author, campaign, locale, topic, canonical URL, and publish date.

Connecting Fathom to structured content means you can tie performance data back to the content model instead of cleaning up spreadsheets of slugs after launch. With an AI Content Operating System like Sanity, content lives as typed JSON in the Content Lake, so a webhook can fire on publish, a GROQ query can fetch exactly the tracking fields, and a server-side handler can configure Fathom or prepare reporting metadata without polling every 15 minutes.

The trade-off is worth being clear about. Fathom isn’t a data warehouse, search index, or place to sync full article bodies. You use it for privacy-first web analytics and custom events. The integration should sync site IDs, domains, canonical paths, campaign labels, and event names, while the frontend sends pageviews and events to Fathom when real visitors load or interact with a page.


03Architecture

Architecture overview

A typical Sanity and Fathom setup has two paths. First, editorial content moves from Sanity’s Content Lake to your frontend. The page query uses GROQ to fetch the slug, title, locale, campaign ID, author, and any Fathom event names needed by the template. The frontend renders the page and loads Fathom’s tracking script with the correct site ID, so the end user’s pageview and custom events are sent directly to Fathom. Second, publish events can trigger server-side sync. When a site settings document, campaign document, or landing page is published, a Sanity webhook or Function receives the mutation. The handler uses @sanity/client and GROQ to fetch the current published fields, then calls Fathom’s API with a server-side bearer token. For example, a multi-brand company can create a Fathom site through the Sites API when a new brand site document is published, store the returned Fathom site ID back on the Sanity document, and use that ID in every page query. For reporting, a dashboard inside Sanity Studio or a separate internal app can call Fathom’s Aggregations API from a server route, grouped by pathname and filtered by date range. That gives editors a content-aware view: not just “/blog/fathom-guide had 8,421 visits,” but “the English blog post in the Analytics topic by Maya had 8,421 visits in the first 7 days.” Keep the Fathom API token server-side, validate Sanity webhook signatures in production, and avoid sending unpublished draft paths to analytics.


04Use cases

Common use cases

📈

Privacy-first content performance

Track visits, referrers, bounce rate, and average duration for published Sanity pages without building a cookie-heavy analytics stack.

🎯

Campaign CTA tracking

Model CTA event names in Sanity Studio, render them in landing pages, and send Fathom custom events like “Pricing CTA click” or “Demo form start.”

🧭

Editor-facing analytics snapshots

Query Fathom’s Aggregations API by canonical pathname and show 7-day or 30-day traffic next to the matching content document in Sanity Studio.

🏢

Multi-site analytics setup

Create a Fathom site when a new brand, microsite, or regional site is published in Sanity, then store the returned site ID for frontend tracking.


05Implementation

Step-by-step integration

  1. 1

    Set up Fathom

    Create a Fathom account, add a site, copy the site ID, and create an API token for server-side calls. If you’re tracking a single site, the site ID may be enough. If you’re provisioning many sites from content, keep the API token ready for the Sites API.

  2. 2

    Add tracking fields to Sanity Studio

    Model fields such as canonicalPath, fathomSiteId, campaignKey, primaryCtaEventName, secondaryCtaEventName, and analyticsEnabled. For multi-locale sites, store locale and canonical path separately so Fathom reports can be grouped cleanly.

  3. 3

    Fetch only the fields the frontend needs

    Use GROQ in your page query to select tracking fields alongside page content. For example, fetch the page title, slug, canonical path, Fathom site ID from site settings, and CTA event names from referenced campaign documents.

  4. 4

    Create the sync mechanism

    Use a Sanity webhook or Functions to react when site settings, campaigns, or landing pages are published. The handler should fetch the published document from the Content Lake, then call Fathom’s API from the server. Don’t put your Fathom API token in browser code.

  5. 5

    Install Fathom on the frontend

    For a static site, add Fathom’s script tag with data-site set to the site ID. For a single-page app, install fathom-client, call Fathom.load(siteId), and call Fathom.trackPageview() on route changes.

  6. 6

    Test pageviews, events, and reporting

    Publish a test page, load it in production or a Fathom-allowed domain, click tracked CTAs, and confirm that pageviews and custom events appear in Fathom. Then test your reporting route by querying Fathom’s Aggregations API for the published canonical path.


06Code

Code example

This webhook handler provisions a Fathom site from a published Sanity site settings document. Visitor pageviews still come from Fathom’s frontend script.

typescriptapp/api/sanity/fathom-site/route.ts
import { createClient } from '@sanity/client';
import type { NextRequest } from 'next/server';

const sanity = createClient({
  projectId: process.env.SANITY_PROJECT_ID!,
  dataset: process.env.SANITY_DATASET!,
  apiVersion: '2025-01-01',
  token: process.env.SANITY_WRITE_TOKEN!,
  useCdn: false,
});

const query = `*[_id == $id && _type == 'siteSettings'][0]{
  _id,
  title,
  domain,
  fathomSiteId
}`;

export async function POST(req: NextRequest) {
  const { _id } = await req.json();
  const site = await sanity.fetch(query, { id: _id });

  if (!site || site.fathomSiteId) {
    return Response.json({ skipped: true });
  }

  const fathomRes = await fetch('https://api.usefathom.com/v1/sites', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.FATHOM_API_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ name: site.title }),
  });

  if (!fathomRes.ok) {
    return Response.json({ error: await fathomRes.text() }, { status: 502 });
  }

  const created = await fathomRes.json();

  await sanity.patch(site._id).set({ fathomSiteId: created.id }).commit();

  return Response.json({ fathomSiteId: created.id });
}

07Why Sanity

How Sanity + Fathom works

Build your Fathom integration on Sanity

Sanity gives you the structured content foundation, real-time event system, and flexible APIs you need to connect Fathom to every published page and campaign.

Start building free →

08Comparison

CMS approaches to Fathom

CapabilityTraditional CMSSanity
Page analytics contextContent Lake structures analytics metadata as JSON, so Fathom paths can be matched to authors, topics, locales, and campaign records.
Publish-time tracking setupWebhooks or Functions can trigger on specific publish events and call Fathom’s API from server-side code without polling.
Field-level query controlGROQ selects the exact fields Fathom-related workflows need, including referenced campaign labels and site-level Fathom IDs.
Custom event governanceSanity Studio can validate event-name fields, show previews, and keep CTA tracking labels consistent before publish.
Editor-facing reportingA custom Sanity Studio view can query Fathom’s Aggregations API by canonical path and show traffic next to the exact content document.
Multi-channel deliveryOne structured back end can provide content and Fathom tracking metadata to web, mobile, campaign pages, and AI agents.

09Next steps

Keep building

Explore related integrations to complete your content stack.

Ready to try Sanity?

See how Sanity's Content Operating System powers integrations with Fathom and 200+ other tools.