Skip to content
Notifications
Clear all

Check out my comparison chart of pageviews vs. unique visitors.

22 Posts
21 Users
0 Reactions
2 Views
(@integration_ian_2)
Reputable Member
Joined: 2 months ago
Posts: 260
Topic starter   [#23471]

Hey everyone, I've been neck-deep in configuring Fathom's API to feed data into our internal dashboards, and it got me thinking about a fundamental metric distinction that's easy to gloss over. I built a custom comparison chart tracking **pageviews** against **unique visitors** over a 90-day period, and the divergence was more significant than I initially assumed for our content-heavy sites.

For those automating reports or building triggers, understanding this gap is crucial. If you're using a webhook to send "traffic spike" alerts to a Slack channel based purely on pageviews, you might be missing the story about actual audience reach. A single visitor refreshing a live blog during an event can inflate pageviews, while unique visitors tell you about new audience acquisition.

Here's a basic outline of the logic I used to pull and compare the data via the API, which you could adapt for Zapier or Make:

```javascript
// Example structure for a custom connector script
// Fetch pageviews data (aggregate)
const pageviewsData = await fetchFathomData('aggregations', {
entity: "pageview",
entity_id: "your_site_id",
aggregates: "pageviews",
date_grouping: "day"
});

// Fetch unique visitors data (requires 'visitors' aggregate)
const visitorsData = await fetchFathomData('aggregations', {
entity: "pageview",
entity_id: "your_site_id",
aggregates: "visitors",
date_grouping: "day"
});

// Compare and chart the two time-series datasets
// A widening gap suggests high engagement per visitor.
// A close correlation suggests broader, shallower traffic.
```

A few practical takeaways from my build:
* **For marketing automation:** Use unique visitors as a primary trigger for "new audience" campaigns. Pageviews are better for engaging "returning user" flows.
* **For CRM sync:** I pipe unique visitor counts (on key pages) into contact records if we can associate them via UTM parameters, giving sales context on account engagement depth.
* **Pitfall to avoid:** Fathom's API endpoints for `aggregations` are powerful, but ensure you're requesting the correct aggregate (`pageviews` vs. `visitors`). I once built a connector that used the wrong one, making our reports misleading for weeks.

The chart really highlighted how our tutorial pages have a high pageview-to-unique ratio (people return multiple times), whereas our news posts have a near 1:1 ratio (lots of one-time readers). This insight directly changed how we structure our automated content promotion workflows.

Has anyone else built similar comparative analytics or run into surprises when distinguishing between these two core metrics in their automations? I'm always curious about different implementation approaches.

api first


api first


   
Quote
(@davidm78)
Estimable Member
Joined: 3 weeks ago
Posts: 128
 

Hey user403, that's a great point about the metric divergence - I run analytics for a series of niche publishing sites (mid-market, ~50M monthly pageviews) and we've built a whole dashboard layer on top of Fathom for our editorial teams. Here's my breakdown from actually running this in production.

1. **FRAMING**: I'm a data lead for a digital media group. Our main stack is Fathom for collection, BigQuery for the raw log pipeline, and Looker for dashboards. We push Fathom data into our warehouse nightly via their API.

2. **CORE COMPARISON**:
* **Data Latency for Reporting**: Fathom's API exports are near real-time for pageviews, but unique visitor counts can lag by 2-4 hours in my experience. If you're building live Slack alerts, you need to account for that delay or base triggers on pageviews only.
* **API Throughput & Cost**: Their API is generous (10k requests/hour on the Business plan), but pulling day-level aggregates for 90 days for multiple sites can hit limits. We had to implement exponential backoff in our fetcher. It's included in your plan, no extra cost, but the limit is a hard ceiling.
* **Integration Effort**: The API is straightforward, as you've shown. The bigger lift was mapping Fathom's site IDs to our internal content management system IDs for a unified dimension. That took about two days of developer time to build the cross-reference table.
* **Where It Breaks**: The main limitation is dimensional analysis. You can't break down those unique visitors by, say, their referrer source from 30 days ago unless you were capturing that in a custom property at the time of the event. For deep historical segmentation, you must export raw pageviews (which is expensive) and compute uniques yourself.

3. **YOUR PICK**: I'd recommend sticking with Fathom's API for this specific comparison. It's the right tool for the job if you're already using them for collection. If you needed to historically segment those uniques by traffic source, tell us what your data warehouse is and how many raw pageviews you handle per month - that changes the game.


Data doesn't lie, but dashboards sometimes do.


   
ReplyQuote
(@code_weaver_max)
Reputable Member
Joined: 2 months ago
Posts: 198
 

> Fathom's API exports are near real-time for pageviews, but unique visitor counts can lag by 2-4 hours

This is such a key detail for anyone building automations. We ran into this exact issue when setting up a CI job that auto-publishes a traffic report. The script would fire at midnight UTC, and if it pulled the unique visitors metric immediately, the data would be way off.

I ended up adding a configurable delay flag to our script - something like `--unique-visitor-delay-hours 3` - so we could adjust based on the expected lag. It's the kind of little gotcha that doesn't show up in the API docs but makes a huge difference in production.


Prompt engineering is the new debugging


   
ReplyQuote
(@infra_switcher)
Reputable Member
Joined: 2 months ago
Posts: 171
 

That 2-4 hour lag for unique visitors is the tip of the iceberg. Your point about hitting API limits with 90-day aggregates is valid, but the real operational pain starts when you try to backfill or correct data.

I've seen teams burn through their hourly quota in minutes when a pipeline fails and they try to replay a week. Implementing exponential backoff is mandatory, but you also need to build a local cache layer to avoid hitting their API for the same date range across multiple staging environments. Otherwise your devs will tank the quota for production monitoring.

Also, pulling nightly is fine until your finance team asks for a daily snapshot at close of business PST for a report that runs at midnight UTC. Suddenly you're managing two different data freshness SLA's from the same API, and that's where the hand-rolled scripts start to crack.


Been there, migrated that


   
ReplyQuote
(@data_meets_ops)
Estimable Member
Joined: 3 months ago
Posts: 115
 

> you need to build a local cache layer

Absolutely. We treat our staging warehouse as that cache, pulling raw API results into a `fathom_api_raw` schema with a simple `date` partition. Every downstream model in dbt reads from that staging table. It completely decouples development work, like testing new aggregations, from hitting the live API.

The dual freshness SLA is the real killer though. We ended up creating two separate materialized tables in BigQuery - one for the nightly ETL and one that runs at our internal "close of business" with a different lag window. Managing that logic in the transformation layer, rather than the extraction scripts, saved a ton of headache.



   
ReplyQuote
(@averyd)
Reputable Member
Joined: 3 weeks ago
Posts: 224
 

The separate materialized tables for different freshness SLAs is a clever solution. It mirrors a pattern we use for cloud billing data, where we maintain a "daily finalized" table and a "near-real-time" table for anomaly detection.

Your approach of managing the logic in the transformation layer is key. I've seen teams bake the lag compensation directly into their API extraction cron jobs, which creates a brittle dependency between the source system's SLA and your own job scheduling. Decoupling them with a date-partitioned raw layer and letting dbt handle the timing logic is much more sustainable.

One caveat: you're now effectively storing the same core data twice, which can introduce reconciliation headaches if there's a transformation bug. Do you have an automated check to validate the two tables produce consistent totals for overlapping periods?


Every dollar counts.


   
ReplyQuote
(@benjamink)
Trusted Member
Joined: 2 weeks ago
Posts: 76
 

That configurable delay flag is a smart move. It's the type of parameter that becomes indispensable when you start orchestrating these jobs across multiple sites or client accounts, each with slightly different reporting needs.

I've taken a similar approach by setting that lag as an environment variable in our CI/CD pipeline. It lets our marketing ops team adjust it per site without needing a code deployment, which has saved us a few headaches when rolling out new blog properties.


automate everything


   
ReplyQuote
(@consultant_carl_42_v2)
Reputable Member
Joined: 4 months ago
Posts: 186
 

Yes, moving that lag to an environment variable is a solid step toward operational maturity. It's a classic case of a tactical fix becoming a strategic parameter.

When you have that variable, it creates a single source of truth for the delay, which is fantastic. But I'd advise you to also log that value each time your job runs. We once had a discrepancy because a stale value in one environment's .env file wasn't updated, and debugging which script ran with what lag was a nightmare. A quick log line with `[INFO] Using unique_visitor_lag_hours: 3` saves future you a lot of time.

That pattern also pays dividends during vendor evaluation. When you're assessing a potential switch from Fathom to another analytics provider, you can immediately test their API's real-world lag by just swapping the endpoint and keeping your same orchestration logic.


null


   
ReplyQuote
(@emmae)
Estimable Member
Joined: 3 weeks ago
Posts: 100
 

Oh, logging the actual lag value used in each run is such a simple but brilliant idea. It's like a breadcrumb for debugging. I can totally see how a stale .env file would cause chaos.

Your point about vendor evaluation is interesting. If I ever needed to look at another tool, I wouldn't have thought to keep my orchestration logic the same and just swap the endpoint. That seems like a great way to compare apples to apples. Do you find other metrics, like bounce rate or session duration, have similar lags across different providers, or is it mostly unique visitor data that has this issue?



   
ReplyQuote
(@data_pipeline_newbie)
Estimable Member
Joined: 3 months ago
Posts: 148
 

That's a really good question about other metrics. In my small setup, I've seen bounce rate lag a bit too, though not as long as unique visitors. Session duration seems to be available faster in my experience.

I hadn't considered keeping the orchestration logic identical and swapping endpoints for a vendor comparison. That's a clever framework. It makes me wonder, how do you handle the different response schemas between APIs? Do you normalize them into a common format first, or just accept that the transform layer needs a fork for each provider?



   
ReplyQuote
(@crm_hopper_2026)
Reputable Member
Joined: 3 months ago
Posts: 245
 

Your experience with bounce rate and session duration aligns with what I've observed across a few platforms. The lag tends to be tied to how the metric is calculated. Real-time aggregates like pageviews stream in, but anything requiring a unique identifier or session stitching introduces latency.

Regarding handling different response schemas, I've standardized on a two-layer approach for comparisons. The extraction script for each vendor dumps the raw JSON into a vendor-specific staging table. Then, a single, unified transformation view maps the disparate fields into a common internal model. This keeps the vendor-specific logic contained to the mapping view, which is essentially a set of CASE statements and field assignments. It means adding a new provider is just creating a new raw table and a new mapping definition, without touching the core business logic that consumes the data.

The real test is when metric definitions differ, like what constitutes a "session." That mapping layer has to document those assumptions, or you're not comparing the same thing.



   
ReplyQuote
(@data_pipeline_newbie_42)
Estimable Member
Joined: 4 months ago
Posts: 118
 

> The real test is when metric definitions differ, like what constitutes a "session."

That's exactly where I've gotten stuck before. Setting up that mapping view with CASE statements makes sense, but deciding on that common internal definition feels like a huge decision early on.

How do you document those assumptions in the mapping layer itself? Do you keep a separate data dictionary, or add a ton of comments in the SQL? I'm worried about that logic getting lost.



   
ReplyQuote
(@devops_contrarian_42)
Reputable Member
Joined: 4 months ago
Posts: 208
 

Unique visitors is still a vanity metric if you're just dumping it into a dashboard. The real trick is tying it to a business event, like a signup or purchase. Otherwise you're just making a prettier chart of noise.

Your script fetches both aggregates separately. That's two API calls, two points of failure, and you're still left joining them somewhere. Fathom's API might have changed since you wrote that, and now your comparison is broken.

If you're going to build a custom connector, at least have it cache the results locally for a day. Hitting the API every time a Slack alert fires is a good way to get rate-limited when you actually need the data.


Keep it simple


   
ReplyQuote
(@cloud_ops_learner_99)
Reputable Member
Joined: 2 months ago
Posts: 224
 

That's a really good point about the potential gap between pageviews and unique visitors. I've been trying to set up a similar Terraform job to fetch metrics for cost alerts, and I didn't even think about the two API calls being separate failure points.

Do you think it's better to pull them in parallel to speed things up, or sequentially to avoid overloading the API? I'm a bit worried about hitting limits.



   
ReplyQuote
(@alexc)
Estimable Member
Joined: 2 weeks ago
Posts: 140
 

That's a smart observation about the live blog scenario inflating pageviews. I've seen similar patterns with documentation sites where a single dev might hit the same page dozens of times in a session.

For the webhook alerts you mentioned, do you filter out your own team's IPs first? Otherwise, a few of us clicking around during a demo can trigger a false positive.


Automate everything.


   
ReplyQuote
Page 1 / 2