Interpreting GA4 Metrics & Their Relationships (+ SQL Code)

You may already know what are some of the most common Google Analytics 4 metrics… but what about their interpretation?

And how to calculate them in BigQuery?

Look, I will do both: show you what they are and why they matter for the business.

Once you understand why some of these options are subpar, you will certainly love to calculate your own (as I show you).

P.S. I won’t cover every single metric as in most cases you only need the usual ones and custom ones I will show in my future article(s).

Before We Start

Go read my article where I showcase the GA4 BigQuery schema, it’s super important to level up and truly understand these metrics.

If you base yourself on the GA4 UI, you will see the final result but don’t understand how we got there.

But if you don’t know anything about BigQuery, it’s still fine, read the text and focus on the part where I simply explain the metric.

CategoryMetric
Traffic
Users
Sessions
Pageviews
New Users
Returning Users
Engagement
Event Count
DAU/MAU (User Stickiness)
Engagement Rate
Active Users
Engaged Sessions
Bounce Rate
Entrances
Exits
Avg. Engagement Time
Conversions
Conversion Rate
Financial/Customer
Lifetime Value (LTV)
Purchase Revenue
Average Revenue per User (ARPU)

I didn’t use Google classification and grouped the core metrics by their use cases.

There may be updates of this article in the next future but this is what I usually care about when evaluating a website.

When showcasing queries I will use different styles for variety, the important is that you focus on the concepts or the bolded parts.

And sometimes, you need your own definitions and not what Google gives you.

Traffic-related Metrics

The word traffic is misused in Web Analytics and Digital Marketing. It refers to anything that attracts people to your website.

It’s important to understand how all of the metrics we will cover relate to each other and in which way:

As you can see, user metrics are the most conservative of the bunch.

These metrics love being broken down by source/medium/channel because they are more expressive:

Publishers love to have more traffic on their websites because this can signal where to invest more resources.

Given you make more money (and sell subscriptions) with popular content, these are good proxies to understand what to write next.

The context is quite different for any other business type where you want to have tailored traffic and of high quality.

Total Users (aka Users)

A user is not really a user, despite GA4 documentation claims it’s about people.

In most cases, namely anonymous users, you deal with a browser-device pair, a first-party cookie if you may.

If the user is identified (e.g. login/mobile app), then it’s another story as you can assign a persistent User ID (user_id in BigQuery).

This metric is the most conservative of the traffic metrics and my favorite in most situations.

SELECT COUNT(DISTINCT user_pseudo_id) as tot_users
FROM `project_name.dataset_name.events_*`

Alternativel, if you have a User ID:

SELECT COUNT(DISTINCT user_id) as tot_users
FROM `project_name.dataset_name.events_*`

Total Sessions (aka Sessions)

A session describes a timeframe during which events happen.

You could change the timeout of a session but I advise against doing so because it would complicate future analysis.

Pretty much everyone agrees the timeframe is 30 minutes, the default value.

This is tied to a session_start event along a ga_session_id and a ga_session_number.

It’s recommended to rely on sessions for websites and users for apps but I am not exactly sure this is great advice.

If you are after taking action, the most conservative metric is often better.

In terms of calculating it in BigQuery, you need to concatenate the user pseudo id to the ga session id because the same id can be tied to multiple users:

SELECT
  COUNT(DISTINCT session_id) AS sessions
FROM
  (
    SELECT
      CONCAT(
        user_pseudo_id,
        (
          SELECT
            value.int_value
          FROM
            UNNEST (event_params)
          WHERE
            key = 'ga_session_id'
        )
      ) AS session_id
    FROM
      `project_name.dataset_name.events_*`
    WHERE
      _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_TRUNC(CURRENT_DATE(), MONTH)) AND FORMAT_DATE(
        '%Y%m%d',
        DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)
      )
    GROUP BY
      session_id
  )

To be more specific, the ga_session_id represents the starting time of a session as a timestamp.

If you feel more daring, you can define a session with different criteria, as long as you don’t rely on that single event parameter.

P.S. According to Google, this is the closest metric to GSC clicks. As explained in my article about GSC data and metrics, this is wishful thinking.

Pageviews (Views)

This is just counting how many times the event “page_view” is taking place.

If a person visits a page multiple times, it counts as multiple pageviews.

Not the best metric for conservative estimates as it tends to be inflated.

👀
Unique Pageviews from Universal Analytics doesn’t exist anymore.

As you may have noticed, a user can load the same page multiple times, inflating your pageviews. Guess what, bots do it all the time!

In BigQuery, Pageviews is simply a count of all of your page_view events.

SELECT COUNT (*) as pageviews
FRoM `your_project_id.your_dataset_id.events_*`
WHERE event_name = "page_view"

And if you really want to calculate Unique Pageviews, you can just calculate Sessions instead.

The Universal Analytics definition of unique pageview is that it represents the number of sessions during which that page was viewed one or more times.

New Users

Users who have firstly visited your website during a given timeframe.

The word timeframe changes everything. I may have visited 3 months ago with the same pseudo ID and be considered as a new user next month.

Or even worse, I can visit again in the same timeframe so I will be considered both a new user and a returning user.

You can verify this by running a code like the one below:

SELECT
  user_pseudo_id,
  'Both new and returning' AS user_classification
FROM
  (
    SELECT
      MAX(
        CASE
          WHEN event_name = 'first_visit' THEN user_pseudo_id
          ELSE NULL
        END
      ) AS new_users,
      user_pseudo_id,
      MAX(
        CASE
          WHEN (
            SELECT
              value.int_value
            FROM
              UNNEST (event_params)
            WHERE
              event_name = 'session_start'
              AND key = 'ga_session_number'
          ) > 1 THEN user_pseudo_id
          ELSE NULL
        END
      ) AS returning_users
    FROM
      `project_name.dataset_name.events_*`
    WHERE
      _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_TRUNC(CURRENT_DATE(), MONTH)) AND FORMAT_DATE(
        '%Y%m%d',
        DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)
      )
    GROUP BY
      user_pseudo_id
  )
WHERE
  new_users IS NOT NULL
  AND returning_users IS NOT NULL
ORDER BY
  user_pseudo_id 

This will show you which Pseudo IDs are both new and returning users.

So, the code to calculate a new user is to detect the event called “first_visit” (bold).

Returning Users

Before we start, Returning Users is NOT the opposite of New Users and in fact:

👀
New + Returning Users ≠ Total Users.

That’s because this metric is also affected by the timeframe you select.

As seen before in the second bolded part of the code, a returning user has a ga_session_number parameter > 1.

For this reason, you can create your own definition of new and returning user in BigQuery as I often do.

SELECT DISTINCT
  user_pseudo_id,
  CASE 
    WHEN event_date = MIN(event_date) OVER (PARTITION BY user_pseudo_id)
    THEN 'New'
    ELSE 'Returning'
  END AS user_status
FROM
  `project_name.dataset_name.events_*`

As implied by the logic of the pseudo user ID, we don’t actually count people… a person can visit your website from another device and have a different pseudo ID.

So, the best tracking for new vs returning users is once again given by logged users.

Sessions per User

Another “random” metric, calculated as:

Total number of sessions / Total users

This is an approximation, of course.

It doesn’t mean that every single user (or cookie) will have that exact number of sessions.

There is also Views per User that is calculated similarly except you are considering Pageviews over Sessions.

Engagement-related metrics

User activity aka engagement is a big part of the web journey… if it wasn’t that GA4 uses criteria that are useless.

We will see some nice metrics that have a LOT of use cases but require some changes.

I DO recommend custom definitions of engagement after you test with your data to have more expressive results.

Event Count

How many times events happened.

The GA4 BigQuery events table records one event per row, so they are central to the data model.

GA4 UI makes it available as a metric in Explorations but this is simply a COUNT(*) in SQL jargon:

The OR in the picture is NOT SQL syntax, I am just telling you there are 2 ways to calculate them.

So Pageviews that I covered before is just an event count with a filter on the event name.

The same goes for:

  • downloads
  • add to cart
  • promotion clicks
  • purchases

It’s all events you keep counting over time.

Daily/Weekly/Monthly Active Users + User Stickiness

This subcategory of metrics sits within the same group of Active Users but with a twist: they are timebound.

The daily active users is an indicator of how many users you usually have per day.

DAU/MAU is defined as User Stickiness because it tells you how long people stick to your website.

% of users who engaged for the calendar day out of the users who engaged in the last 30 days.

I don’t consider any of these metrics as worthy of being a KPI BUT if you work with B2B SaaS/software companies, you need to know these concepts.

👀
Some companies like Duolingo put great emphasis on their daily active users because app usage is a good way to gauge if your business is healthy.

Engagement Rate

An engaged user is a user with an engaged session:

  • minimum 2 pageviews
  • a key event
  • been there for > 10 seconds

If you have some common sense, you’d realize this doesn’t mean much.

WITH session_engagement AS (
  SELECT
    (
      SELECT
        value.string_value
      FROM
        UNNEST (event_params)
      WHERE
        event_name = 'page_view'
        AND key = 'page_location'
    ) AS page_location,
    MAX(
      (
        SELECT
          value.string_value
        FROM
          UNNEST (event_params)
        WHERE
          key = 'session_engaged'
      )
    ) AS engaged_sessions,
    CONCAT(
      user_pseudo_id,
      (
        SELECT
          value.int_value
        FROM
          UNNEST (event_params)
        WHERE
          key = 'ga_session_id'
      )
    ) AS session_id
  FROM
    `project_name.dataset_name.events_*`
  WHERE
    _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_TRUNC(CURRENT_DATE(), MONTH)) AND FORMAT_DATE(
      '%Y%m%d',
      DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)
    )
  GROUP BY
    page_location,
    session_id
)

SELECT
  page_location,
  SAFE_DIVIDE(
    SUM(CAST(engaged_sessions AS INT)),
    COUNT(DISTINCT session_id)
  ) AS engagement_rate
FROM
  session_engagement
GROUP BY
  page_location

It’s better to craft your own definition(s) based on your specific needs.

The GA4 BigQuery export gives you (almost) all of this information.

You can create a custom formula like the one below:

WITH page_events AS (
  SELECT
    (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') AS page_location,
    event_name
  FROM
    `project_name.dataset_name.events_*`
  WHERE
    _TABLE_SUFFIX BETWEEN '20250101' AND '20251231'
)

SELECT
  page_location,
  COUNTIF(event_name IN ('purchase', 'add_to_cart', 'view_item', 'scroll', 'click')) 
    / COUNT(*) AS event_engagement_rate
FROM
  page_events
WHERE
  page_location IS NOT NULL
GROUP BY
  page_location
ORDER BY
  event_engagement_rate DESC

If you really need to use it, do it by page and never as site-wide.

Checking if your site-wide Engagement Rate is going up or down is completely useless and non-actionable!

Active Users

The number of people who engaged with your site, as explained above plus those with an event like first_visit, first_open, user_engagement.

Not a very useful metric per se due to the broad definition of engagement given by Google.

SELECT
  COUNT(DISTINCT active_users) AS active_users
FROM
  (
    SELECT
      MAX(
        CASE
          WHEN is_active_user THEN user_pseudo_id
          ELSE NULL
        END
      ) AS active_users,
      user_pseudo_id
    FROM
      `project_name.dataset_name.events_*`
    WHERE
      _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_TRUNC(CURRENT_DATE(), MONTH)) AND FORMAT_DATE(
        '%Y%m%d',
        DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)
      )
    GROUP BY
      user_pseudo_id
  )

The GA4 UI loves to show it by default in some reports, for example for Pages and screens under Engagement.

Engaged Sessions

Simply put, engaged sessions showcase how many of your sessions were more “valuable”.

In reality, this is often a pointless metric I’d not rely about.

WITH session_data AS (
  SELECT
    MAX(
      (
        SELECT
          value.string_value
        FROM
          UNNEST (event_params)
        WHERE
          key = 'session_engaged'
      )
    ) AS engaged_sessions
  FROM
    `project_name.dataset_name.events_*`
  WHERE
    _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_TRUNC(CURRENT_DATE(), MONTH)) AND FORMAT_DATE(
      '%Y%m%d',
      DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)
    )
)

SELECT
  SUM(CAST(engaged_sessions AS INT)) AS engaged_sessions
FROM
  session_data

For this reason, it’s better to create your own definition of engagement in GA4.

Bounce Rate

Many of you still remember the old definition of Bounce Rate from Universal Analytics, namely:

Total single-page sessions / total sessions

or

% sessions on your site in which users viewed only a single page

But as of GA4, the formula is as follows:

1 – (% engaged users / total users) = 1 – Engagement Rate

i.e. the opposite of the Engagement Rate.

So Bounce Rate has a completely different meaning from the good old UA definition.

This metric follows the same considerations of the Engagement Rate.

Entrances

The number of times that the first event recorded in a session occurred on a page or screen.

The GA4 schema has a nice event parameter for that purpose that you can use:

WITH session_data AS (
  SELECT
    user_pseudo_id,
    event_name,
    (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'entrances') AS entrances,
    (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS session_id
  FROM
    `project_name.dataset_name.events_*`
  WHERE
    _table_suffix BETWEEN '20250101' 
    AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
    AND event_name = 'page_view'
)

SELECT
  COUNT(DISTINCT CONCAT(user_pseudo_id, session_id)) AS entrances
FROM
  session_data
WHERE
  entrances = 1

I’ve made the query longer by using a CTE for readability.

Entrances per se don’t mean anything BUT you can piece this information together with page location to detect landing pages:

SELECT
  CASE 
    WHEN (
      SELECT value.int_value 
      FROM UNNEST(event_params) 
      WHERE event_name = 'page_view' 
      AND key = 'entrances'
    ) = 1 
    THEN (
      SELECT value.string_value 
      FROM UNNEST(event_params) 
      WHERE event_name = 'page_view' 
      AND key = 'page_location'
    )
  END AS landing_page,
  COUNT(DISTINCT user_pseudo_id) AS total_users
FROM
  `project_name.dataset_name.events_*`
WHERE
  _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_TRUNC(CURRENT_DATE(), MONTH))
  AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
GROUP BY
  ALL
HAVING
  landing_page IS NOT NULL
ORDER BY
  total_users DESC

Now this is more clear! You can figure out where users enter your website and optimize those pages even more.

Exits

The number of times that the last event recorded in a session occurred on a page or screen.

Unlike entrances, there is no quick way to detect an exit.

So we need to rely on our lovely window functions as I show below when searching for the pages with the most exits:

WITH session_events AS (
  SELECT
    user_pseudo_id,
    event_name,
    event_timestamp,
    (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS session_id,
    (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') AS page_location
  FROM
    `project_name.dataset_name.events_*`
  WHERE
    _table_suffix BETWEEN '20240701' 
    AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
    AND event_name = 'page_view'
),

exit_pages AS (
  SELECT
    user_pseudo_id,
    session_id,
    FIRST_VALUE(page_location) OVER (
      PARTITION BY user_pseudo_id, session_id 
      ORDER BY event_timestamp DESC
    ) AS exit_page
  FROM
    session_events
)

SELECT
  exit_page,
  COUNT(DISTINCT CONCAT(user_pseudo_id, session_id)) AS exits
FROM
  exit_pages
WHERE
  exit_page IS NOT NULL
GROUP BY
  exit_page
ORDER BY
  exits DESC

Now, the logic for the snippet can be tough, especially the window function (aka FIRST_VALUE()).

Imagine this scenario:

user_123, session_456, 10:00:00, /homepage
user_123, session_456, 10:01:30, /products
user_123, session_456, 10:03:45, /contact

What the function does is as follows:

  • PARTITION BY user_pseudo_id, session_id – Groups all events from the same user session together
  • ORDER BY event_timestamp DESC – Sorts events within each session from newest to oldest
  • FIRST_VALUE(page_location) – Takes the first value in that sorted order (which is the most recent page)

As before, check pages and not the single metric, which is useless.

The business interpretation here is the opposite of entrances, these are the pages where people often leave.

Do you expect users to leave from there? If not, you have some detective work to do.

Average Engagement Time

The average time a user has spent on your page(s).

You can build a table like the one below to assess the overall engagement time situation by page.

The code is as follows:

WITH session_metrics AS (
  SELECT
    user_pseudo_id,
    (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS session_id,
    (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') AS page_location,
    MAX((SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'session_engaged')) AS is_engaged,
    SUM((SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'engagement_time_msec')) / 1000 AS engagement_seconds
  FROM
    `project_name.dataset_name.events_*`
  WHERE
    _TABLE_SUFFIX BETWEEN '20240701'
    AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
  GROUP BY
    user_pseudo_id,
    session_id,
    page_location
)

SELECT
  page_location,
  ROUND(SAFE_DIVIDE(
    SUM(engagement_seconds),
    COUNTIF(is_engaged = '1')
  ), 2) AS avg_engagement_time,
  
  COUNTIF(is_engaged = '1') AS engaged_sessions,
  COUNT(*) AS total_sessions,
  ROUND(COUNTIF(is_engaged = '1') / COUNT(*), 4) AS engagement_rate,
  SUM(engagement_seconds) AS total_engagement_time
FROM
  session_metrics
WHERE
  page_location IS NOT NULL
GROUP BY
  page_location
ORDER BY
  avg_engagement_time DESC

As usual, the default engagement calculation can be poor and pointless, so adjust what you read here accordingly.

I never use this metric and go straight to Microsoft Clarity.

Qualitative analysis is so much better for me and actually shows me what to do in terms of next actions.

Conversions (prev. Key Events)

Event Count but for key events. This is extremely important to understand if people are doing what you expect them to do.

A conversion can refer to anything, depending on the events you defined before. Unfortunately BigQuery doesn’t keep track of what you defined as a key event in GA4.

For this reason, it’s recommended to have a mapping in DBT/Dataform to identify your conversions.

This can be a KPI and it’s relevant for every business.

Even better if you use the Conversion Rate:

# of Conversions / Total Events

I love to calculate this on a page level:

SELECT
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') AS page_location,
  COUNTIF(event_name IN ('purchase', 'sign_up', 'generate_lead')) AS conversions,
  COUNTIF(event_name IN ('purchase', 'sign_up', 'generate_lead')) 
    / COUNT(*) AS conversion_rate
FROM
  `project_name.dataset_name.events_*`
WHERE
  _TABLE_SUFFIX BETWEEN '20250101' AND '20251231'
GROUP BY
  page_location
HAVING
  page_location IS NOT NULL
ORDER BY
  conversion_rate DESC

Conversion Rate

It’s often better to report on a % per page to see which ones are actually converting.

The calculation is simple, as shown in the query in the previous section:

# of Conversions / Total Events

Financial/Customer Metrics

Yes, GA4 can also be used to report on financial metrics like revenue and so on.

Should you do it?

Normally no, GA4 is severely inaccurate and transactional data requires 100% accuracy.

Use your other systems where financlial data ends up and use GA4 as a mere reference.

P.S. I won’t be covering all of them since most are easy to understand or derivatives of other metrics.

Lifetime Value (LTV)

You can think of it as the total value a user brings over their entire relationship with your business.

This is one of the many ways you can look at LTV, as the sum of total revenue from purchases:

SELECT
  user_pseudo_id,
  SUM(ecommerce.purchase_revenue) AS ltv,
  COUNT(DISTINCT ecommerce.transaction_id) AS total_purchases,
  MIN(PARSE_DATE('%Y%m%d', event_date)) AS first_seen_date,
  MAX(PARSE_DATE('%Y%m%d', event_date)) AS last_seen_date,
  SUM(ecommerce.purchase_revenue) / COUNT(DISTINCT ecommerce.transaction_id) AS avg_order_value,
  DATE_DIFF(
    MAX(PARSE_DATE('%Y%m%d', event_date)), 
    MIN(PARSE_DATE('%Y%m%d', event_date)), 
    DAY
  ) AS customer_lifespan_days
FROM
  `bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_*`
WHERE
  event_name = 'purchase'
  AND ecommerce.purchase_revenue IS NOT NULL
  AND ecommerce.transaction_id IS NOT NULL
GROUP BY
  user_pseudo_id
ORDER BY
  ltv DESC

This metric is purely an estimate, an expectation you have for every user.

This is what most marketers get completely wrong, they think LTV can be used for important decisions.

Don’t do it!!!

It’s a mere reference, not an important metric.

Related to this metric, you can find a lot of similar metrics like lifetime engagement duration and lifetime transactions.

All of these can be calculated by considering the first_visit event.

LTV can mean different things and be calculated in other ways, e.g. B2B SaaS VS DTC Ecommerce.

Purchase Revenue

One of the most straightforward concepts in GA4 or maybe not so much:

purchases + in-app purchases + subscriptions – refund

Refunds should be excluded when calculating this metric in BigQuery.

But in 99% of the cases you can just write this:

SELECT
  SUM(revenue) AS revenue
FROM
  (
    SELECT
      SUM(ecommerce.purchase_revenue) AS revenue
    FROM
      `project_name.dataset_name.events_*`
    WHERE
      _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_TRUNC(CURRENT_DATE(), MONTH)) AND FORMAT_DATE(
        '%Y%m%d',
        DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY)
      )
  )

Average Revenue Per User (ARPU)

Another key Ecommerce metric that you can think of as:

(Total ad revenue + purchase revenue + in-app purchase revenue + subscriptions – refunds) / Active users

As usual, you can tweak this formula to your needs, for example by replacing Active Users with some other metric we have covered before.

However, using Total Users or some other metric will produce different results:

SELECT
  COUNT(DISTINCT user_pseudo_id) AS total_users,
  SUM(ecommerce.purchase_revenue) AS total_revenue,
  ROUND(SUM(ecommerce.purchase_revenue) / COUNT(DISTINCT user_pseudo_id), 2) AS avg_revenue_per_user,
  COUNT(DISTINCT CASE WHEN ecommerce.purchase_revenue > 0 THEN user_pseudo_id END) AS paying_users,
  ROUND(SUM(ecommerce.purchase_revenue) / COUNT(DISTINCT CASE WHEN ecommerce.purchase_revenue > 0 THEN user_pseudo_id END), 2) AS avg_revenue_per_paying_user
FROM
  `bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_*`
WHERE event_name = 'purchase'

An example of ARPU for ALL the data in the sample Ecommerce events table:

This is a sample case, having that many paying users is completely abnormal for any normal business.

So the avg. revenue per user is actually $89.07 in this scenario.

P.S. Google documentation is confusing as they mention ARPU twice with some minimal differences between the 2.

Create your own definitions, please.

The Winner Metrics

Out of all the traffic metrics, Users is my favorite because it’s conservative.

I am not a fan of standard engagement metrics as you have seen but custom engagement definitions can make every other metric viable.

Conversions is what you usually want to focus on.

Even how to define which are your conversions and why they matter for the business.

Then, you can consider other financial metrics but remember, core analyses should be done on your FULL data.

GA4 isn’t the best candidate for accurate transactional data.

Relationships Between Metrics

Metrics don’t live in a vacuum and they can interact with each other, so be sure to always compare them.

In most cases, discrepancies can be explained by sudden and noticeable increases in traffic (bots or Paid) and/or in the quality of your visitors.

Growing Total Users, Decreasing Engagement

This is a textbook case, it can mean multiple things:

  • bot traffic
  • you attracted the wrong audience
  • you created more informational content

I am talking about abnormal changes, for example massive dips in engagement or big spikes in traffic.

This is where Anomaly Detection as a technique can help you detect problematic situations.

Massive Surge In Pageviews

In the majority of cases, these spikes are caused by bots and this is another reason why I generally prefer Users as a metric.

Also check if you had dips in engagement or short sessions, pretty much like above.

Growing Conversions, Decreasing Conversion Rate

The most likely explanation is that you had more traffic or more events triggered disproportionately.

This caused an increase in conversions that is not exactly as good as the one you should get.

As you can see in the above infographic, it’s not that easy in most cases.

But from a purely mathematical perspective, you got more events triggering IF the conversions (numerator) went up.

Growing Avg. Time

This sounds good on paper but can also mean your website is hard to navigate and people waste more time to find what they want.

I have never been a fan of this metric unless you calculate it on a page level.

Don’t use it to evaluate a website without other metrics and more context on the user journey.

It’s also important to assess how many steps are taken BEFORE a conversion.

N.B. People don’t come to websites and buy immediately, remember that.

Growing Revenue, Decreasing Traffic

Well, this is a good scenario, depending on how much was the traffic lost.

Many websites, especially if big, have worthless content that doesn’t serve any purpose and may attract traffic.

This is also the case for when Google ranks your pages for random queries.

As already covered in my article about GSC data and metrics, a drop in Organic Traffic isn’t always bad.

Remember to cross-validate with GSC to see for which queries and/or search types and go more granular.

Decreasing LTV, Growing Purchase Frequency

People may be buying more cheap items or abusing your coupons.

If this happens:

  • check CAC by segment
  • assess the (contribution) margin per transaction
  • analyze coupon codes usage

Growing ARPU, Stable/Decreasing Purchase Revenue

This scenario can have many explanations but it’s usually about retaining whales (high-paying clients) and losing cheaper segments.

As you may have noticed from my traffic analysis, it’s important to analyze the distribution of a metric.

Check where revenue is concentrated and you will have your answers.

It’s OK to have temporary decreases in revenue after you pivot to new segments or modify your offer.

It’s also acceptable to have a decreasing revenue IF your net profit (aka what matters in the end) is higher.

If serving a specific set of customers is cheaper, this can lead to an increase in Net Profit.

Naturally, take this with a grain of salt, ideally your goal is to also increase Revenue, not just cut costs!!!

A Better Way Of Measuring

Using one data source is never enough and this is why you combine GA4 data with other sources, like GSC and crawl data.

Yes, the GA4 UI offers nice integrations with GSC and Google Ads but you almost never want to rely on them.

It’s better to use their BigQuery exports instead and combine them your way.

This is what I teach in my dedicated course:

Unlock Powerful Insights Now!

There are other metrics that are more suitable for most analyses and I will cover them in my next articles!

Leave a Comment