Fathom Analytics Tracking and API Reference | OpsBlu Docs

Fathom Analytics Tracking and API Reference

Implement Fathom's cookie-free tracking with fathom.trackGoal() and trackPageview(), configure EU isolation, set up custom domains, and query the Stats...

How Fathom Works

Fathom Analytics collects data through a JavaScript tracker (script.js, under 2KB) that sends requests to Fathom's edge infrastructure. Each request contains the page URL, referrer, viewport dimensions, and browser User-Agent. Fathom does not set cookies, use localStorage, or perform fingerprinting.

Visitor uniqueness is determined using a hash of the visitor's IP address, User-Agent, and the site domain, combined with a rotating salt. The salt rotates, so Fathom cannot build long-term visitor profiles. All processing happens at the edge before any data reaches storage, and the raw IP address is never persisted.

The data model is deliberately simple:

  • Pageviews -- URL path, referrer, device category, browser, country, and timestamp
  • Events (Goals) -- Named conversion actions with an optional monetary value
  • Uniques -- Deduplicated visitor counts derived from the hashed identifier

Fathom is a managed SaaS product. There is no self-hosted option. All infrastructure is operated by Fathom, with optional EU-only data processing.


Installing the Tracking Script

Add the tracking snippet to the <head> of every page. The data-site attribute is your site ID from the Fathom dashboard:

<script src="https://cdn.usefathom.com/script.js"
  data-site="ABCDEFGH"
  defer></script>

The script loads asynchronously with defer and does not block page rendering.

Script Attributes

Attribute Description
data-site Required. 8-character site ID
data-spa Set to auto for SPA support (default: not set)
data-auto Set to false to disable automatic pageview tracking
data-canonical Set to false to use the raw URL instead of canonical
data-honor-dnt Set to true to respect Do Not Track browser setting
data-excluded-domains Comma-separated domains to exclude from tracking
data-included-domains Comma-separated domains to include (exclude all others)

SPA Support

For single-page applications that use pushState navigation (React Router, Next.js, Vue Router, SvelteKit), enable automatic SPA tracking:

<script src="https://cdn.usefathom.com/script.js"
  data-site="ABCDEFGH"
  data-spa="auto"
  defer></script>

With data-spa="auto", Fathom hooks into the History API and tracks a pageview on each pushState or replaceState call. This works with all frameworks that use the History API.

For hash-based routing (/#/page), data-spa="auto" also listens for hashchange events.

Manual Pageview Tracking

Disable auto-tracking and fire pageviews yourself:

<script src="https://cdn.usefathom.com/script.js"
  data-site="ABCDEFGH"
  data-auto="false"
  defer></script>
// Track current page
fathom.trackPageview();

// Track a specific URL
fathom.trackPageview({ url: '/virtual/checkout-complete' });

Excluding Pages

Prevent specific pages from being tracked:

// In your page logic, conditionally load or block tracking
if (window.location.pathname === '/admin') {
  // Don't track admin pages
} else {
  fathom.trackPageview();
}

Or exclude staging/dev environments:

<script src="https://cdn.usefathom.com/script.js"
  data-site="ABCDEFGH"
  data-included-domains="example.com,www.example.com"
  defer></script>

Goal Tracking (Events)

Fathom calls custom events "Goals." Each goal has an ID (assigned in the dashboard) and an optional monetary value in cents.

Creating Goals

Goals are created in the Fathom dashboard under Site Settings > Events. Each goal gets a unique 5-character ID (e.g., XYZAB).

fathom.trackGoal()

// Track a goal completion
fathom.trackGoal('XYZAB', 0);

// Track a goal with revenue (value in cents)
fathom.trackGoal('XYZAB', 4999);  // $49.99

// Track on button click
document.getElementById('signup-btn').addEventListener('click', () => {
  fathom.trackGoal('XYZAB', 0);
});

// Track on form submission
document.getElementById('contact-form').addEventListener('submit', (e) => {
  fathom.trackGoal('ABCDE', 0);
  // Form continues to submit normally
});

The second parameter is the revenue value in cents (integer). Pass 0 for non-revenue goals.

Tracking Goals Before Script Loads

If you need to track goals in inline scripts that may execute before the Fathom script loads:

window.fathom = window.fathom || {
  trackGoal: function() {
    window.__fathomClientQueue = window.__fathomClientQueue || [];
    window.__fathomClientQueue.push(arguments);
  }
};

This creates a queue that Fathom processes once it initializes.

Goal Completion Data

In the Fathom dashboard, each goal shows:

  • Total completions in the selected period
  • Revenue generated (sum of values)
  • Completion rate relative to pageviews
  • Referrer attribution for completions

Custom Domains for Script Serving

Fathom supports serving the tracking script from your own subdomain. This bypasses ad blockers that block requests to cdn.usefathom.com and improves data accuracy.

DNS Setup

Create a CNAME record pointing to Fathom's infrastructure:

stats.example.com  CNAME  cdn.usefathom.com

Fathom automatically provisions a TLS certificate for your custom domain via Let's Encrypt.

Updated Script Tag

<script src="https://stats.example.com/script.js"
  data-site="ABCDEFGH"
  defer></script>

The script automatically detects the serving domain and sends tracking requests to the same domain, making all analytics traffic first-party from the browser's perspective.

DNS Propagation

After adding the CNAME record, allow up to 24 hours for DNS propagation and certificate issuance. Test with:

curl -I https://stats.example.com/script.js
# Should return 200 with the Fathom script

EU Data Isolation

Fathom offers EU-isolated processing for businesses that need European data to stay in Europe. When enabled:

  • European visitor data is processed entirely within EU infrastructure
  • Data never touches US servers
  • Non-EU visitor data is processed in the US (or the nearest edge)
  • The tracking script and API remain identical -- no code changes needed

EU isolation is a per-site setting configured in the Fathom dashboard. It applies to data processing and storage only; the script CDN still serves from global edge nodes for performance.

This addresses Schrems II compliance requirements by ensuring EU personal data (IP addresses, used only transiently for hashing) is never transferred to the US.


Fathom Stats API

Fathom provides a read-only API for retrieving site statistics. Authenticate with an API key generated in Account Settings.

Authentication

All requests use Bearer token authentication:

curl "https://api.usefathom.com/v1/..." \
  -H "Authorization: Bearer YOUR_API_KEY"

Site Aggregation

# Get aggregate stats for a date range
curl "https://api.usefathom.com/v1/aggregations?\
entity=pageview&\
entity_id=ABCDEFGH&\
aggregates=visits,uniques,pageviews,avg_duration,bounce_rate&\
date_from=2025-01-01&\
date_to=2025-01-31" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response:

[
  {
    "visits": "18320",
    "uniques": "12543",
    "pageviews": "45230",
    "avg_duration": "155.2",
    "bounce_rate": "0.423"
  }
]

Grouped Aggregation (Breakdown)

# Top pages
curl "https://api.usefathom.com/v1/aggregations?\
entity=pageview&\
entity_id=ABCDEFGH&\
aggregates=visits,pageviews&\
field_grouping=pathname&\
date_from=2025-01-01&\
date_to=2025-01-31&\
sort_by=pageviews:desc&\
limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Breakdown by referrer hostname
curl "https://api.usefathom.com/v1/aggregations?\
entity=pageview&\
entity_id=ABCDEFGH&\
aggregates=visits,uniques&\
field_grouping=referrer_hostname&\
date_from=2025-01-01&\
date_to=2025-01-31&\
sort_by=uniques:desc&\
limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

Event (Goal) Aggregation

# Goal completions and revenue
curl "https://api.usefathom.com/v1/aggregations?\
entity=event&\
entity_id=XYZAB&\
aggregates=completions,value" \
  -H "Authorization: Bearer YOUR_API_KEY"

Filtering

Apply filters to any aggregation:

# Only mobile visitors
&filters=[["pathname","is","/pricing"]]

# Exclude specific referrer
&filters=[["referrer_hostname","is not","t.co"]]

# Combine filters (AND logic)
&filters=[["country_code","is","US"],["device_type","is","mobile"]]

Available Grouping Fields

pathname, hostname, referrer_hostname, referrer_pathname, referrer_url, utm_source, utm_medium, utm_campaign, utm_content, utm_term, browser, browser_version, os, os_version, device_type, country_code, region_name, city_name.

Available Aggregates

For entity=pageview: visits, uniques, pageviews, avg_duration, bounce_rate.

For entity=event: completions, value (total revenue in cents).

Site Management API

# List all sites
curl "https://api.usefathom.com/v1/sites" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Get a specific site
curl "https://api.usefathom.com/v1/sites/ABCDEFGH" \
  -H "Authorization: Bearer YOUR_API_KEY"

# List events (goals) for a site
curl "https://api.usefathom.com/v1/sites/ABCDEFGH/events" \
  -H "Authorization: Bearer YOUR_API_KEY"

Common Issues

Script blocked by ad blockers: Set up a custom domain (CNAME to cdn.usefathom.com) and serve the script from your own subdomain. This is the most effective solution and is built into Fathom's infrastructure.

SPA route changes not tracked: Add data-spa="auto" to the script tag. Without it, only the initial page load is tracked. With it, Fathom listens for History API changes and hash changes.

Goal completions showing zero: Verify the goal ID in fathom.trackGoal('XYZAB', 0) matches the ID in the Fathom dashboard exactly. Goal IDs are 5 uppercase alphanumeric characters. Also confirm the script has loaded before fathom.trackGoal() is called.

fathom is not defined: The script loads with defer, so it is available after HTML parsing but before DOMContentLoaded. If calling fathom.trackGoal() in an inline script that runs before the Fathom script, use the queue pattern described above or move your script after the Fathom script tag.

Pageview counts lower than expected: Fathom filters bots, crawlers, and prefetch requests. Visitors who block JavaScript entirely are not counted. If you set data-honor-dnt="true", visitors with Do Not Track enabled are excluded.

Revenue values appear wrong: The value parameter in fathom.trackGoal() is in cents, not dollars. 4999 = $49.99. If you pass 49.99, it is interpreted as $0.50 (rounded to 50 cents).

Canonical URL tracking: By default, Fathom reads the <link rel="canonical"> tag and uses it as the tracked URL. If your canonical tags are misconfigured, pageview paths in the dashboard will be wrong. Set data-canonical="false" to use the actual URL instead.


Platform-Specific Considerations

No Self-Hosting: Fathom is a managed service only. There is no open-source or self-hosted version. If self-hosting is a requirement, consider Plausible or Umami instead.

Email Reports: Fathom sends automated weekly and monthly email reports for each site. Configure recipients in Site Settings. Reports include pageviews, top pages, top referrers, and goal completions.

Shared Dashboards: Generate a public share link for any site. The shared dashboard is read-only and requires no authentication. Useful for client reporting.

Embedding Fathom Data: Use the API to build custom dashboards or embed analytics in your own application:

async function getFathomStats(siteId, apiKey) {
  const res = await fetch(
    `https://api.usefathom.com/v1/aggregations?` +
    `entity=pageview&entity_id=${siteId}&` +
    `aggregates=visits,uniques,pageviews&` +
    `date_from=${thirtyDaysAgo()}&date_to=${today()}`,
    { headers: { Authorization: `Bearer ${apiKey}` } }
  );
  return res.json();
}

Uptime Monitoring: Fathom includes built-in uptime monitoring. Configure alert recipients in Site Settings. Fathom pings your site from multiple locations and notifies you of downtime via email.

Data Retention: Fathom retains all data for the lifetime of your account. There is no data expiration or automatic deletion. Historical data is accessible via the dashboard and API at any time.