How to fix duplicate events in GA4

The two biggest complaints I often hear about GA4 are exceeding the data API quota limits and the daily BigQuery export limits. These two limits are good enough for some businesses to give up on GA4 completely. 

First, you need to evaluate your tracking requirements seriously.

Are you collecting unnecessary event data, esp. at the expense of business-critical information?

The second thing that you need to do is to find and remove duplicate events.

While there is no one-size-fits-all approach, as the best setup will vary from website to website, you can do certain things to remove duplicate events from being tracked.

12 Techniques to fix duplicate events in GA4 (Google Analytics 4)

  1. Use a unique ‘event_id’ parameter.
  2. Don’t do double tagging.
  3. Check the configuration settings of your GTM triggers.
  4. Be careful when using generic GTM triggers.
  5. Track State Changes.
  6. Use the dataLayer.push() method correctly.
  7. Check for built-in events before you create a new event.
  8. Keep detailed documentation of your tracking setup.
  9. Use a unique transaction ID.
  10. Monitor GA4 reports for anomalies.
  11. Use debouncing techniques.
  12. Use throttling techniques.

#1 Use a unique ‘event_id’ parameter

If the same event occurs multiple times, using a unique event ID parameter can help you deduplicate the events. 

When an event is sent to GA4, you include an Event ID unique to that specific instance of the event. 

The Event ID could be generated based on a combination of user ID, session ID, timestamp, or a unique order ID, depending on the context of the event.

If the same event is sent to GA4 more than once with the same Event ID, GA4 recognizes that this is a duplicate of an event it has already received and will ignore it.

By deduplicating events based on the Event ID, GA4 helps maintain the integrity of your analytics data. 

Without the Event ID, repeated events could be counted multiple times, leading to inflated numbers that don’t accurately represent user behaviour.

For example,

If a user refreshes the checkout page, you could use the current timestamp as the Event ID. 

This would ensure that each refresh of the checkout page would have a unique Event ID and that duplicate events would not be counted.

However, use the timestamp as the Event ID only if you are tracking the number of times a user refreshes the checkout page.

Another example.

Suppose you want to track when a user begins the checkout process and ensure that this event is only logged once, even if the user refreshes the page.

When the user begins the checkout process, send the ‘begin_checkout’ event to GA4 along with ‘event_id’ parameter.

gtag(‘event’, ‘begin_checkout’, {

  ‘event_id’: ‘12345-67890’, // Example of unique Event ID

  ‘value’: 50.00,

  ‘currency’: ‘USD’,

  ‘items’: [

    // Details about the items in the cart

  ]

});

If a user begins the checkout process and refreshes the page without an Event ID, this might be logged as two separate ‘begin_checkout’ events. 

If you use an Event ID unique to that checkout session, GA4 would recognize that both events pertain to the same action by the user, and it would only count the event once.

Do you want expert help in setting up/fixing GA4 and GTM?

If you are not sure whether your GA4 property is setup correctly or you want expert help migrating to GA4 then contact us. We can fix your website tracking issues.

#2 Don’t do double tagging. 

Double tagging is a common issue that can cause duplicate events in GA4.

The double tagging occurs when you fire the same event multiple times via different methods (gtag.js, GTM, plugin and/or the GA4 UI). 

Using the ‘create event‘ feature, you can also fire events directly via the GA4 UI.

Using the ‘create event‘ feature in ga4

Check your entire website for multiple implementations of the same tracking code or event tags.

Ensure you’re not tracking the same event via gtag.js, GTM, a plugin, or through manual tagging in the GA4 UI.

Choose a single method for tracking each event, whether it’s gtag.js, GTM, a plugin, or the GA4 UI and then stick to it.

Use tools like the GA Debugger or Google Tag Assistant to inspect what’s being sent to GA4. Make sure that each user action triggers the expected number of events.

Regularly review your GA4 reports for anomalies, such as unexpected spikes in event counts that might indicate double tagging.

Keeping detailed documentation of your tagging setup can help ensure that everyone understands what’s being tracked and how to reduce the risk of accidental double tagging in the future.

#3 Check the configuration settings of your GTM triggers.

Poorly configured GTM triggers can cause the same event to fire multiple times.

For example:

begin_checkout: Trigger this event when a user begins the checkout process for the first time rather than on page load. If a user refreshes the checkout page or navigates back and forth, it shouldn’t trigger the event again.

add_payment_info: Trigger this event once the payment information is added or modified, not on every page load or refresh.

add_shipping_info: Trigger this event when the shipping information is added or modified.

purchase : Trigger this event only when the purchase is successfully completed. This might include using the Page View trigger for the specific URL of the thank you page or utilizing a Custom Event that your website sends when a purchase is completed.

#4 Be careful when using generic GTM triggers

Avoid firing events using generic GTM triggers, as they lead to duplicate events and inflate event count.

Be careful when using generic triggers like ‘all pages’, ‘all clicks’, ‘all forms’, etc., that might unintentionally fire an event in the wrong circumstances. This could greatly increase the number of duplicate events.

For example,

Using the ‘all pages’ trigger to fire a ‘begin_checkout’ event every time the checkout page is loaded could lead to duplicate events if a user refreshes/navigates to the page multiple times.

I often encounter tracking setups where an event is fired on page load.

Say you fire a ‘form_submit’ event on the ‘thank_you’ page load.

That’s a guaranteed way to make sure that your form tracking is not accurate.

Whenever a user navigates to the ‘thank_you’ page, they fire ‘form_submit’ events even when no form submission has actually occurred.

As a result, you could end up with higher form submissions with lower user counts.

The ‘form_submit’ event should fire only when the form is actually submitted.

You can use a data layer variable to record when a form has been successfully submitted and configure your tag to fire based on this variable.

Or you could limit the firing of the ‘form_submit’ event to once per session or use a throttling function so that repeated visits to the ‘thank_you’ page do not continue to trigger the event.

Following is an example of a throttling function that limits the firing of the GA4 ‘form_submit’ event to once a day per unique user.

// Function to handle form submit
function handleFormSubmit() {
  // Get the current date in 'YYYY-MM-DD' format
  const today = new Date().toISOString().split('T')[0];

  // Get the last submit date from localStorage
  const lastSubmitDate = localStorage.getItem('lastFormSubmitDate');

  // Check if the form was already submitted today
  if (lastSubmitDate === today) {
    console.log('Form already submitted today. Event not fired.');
    return;
  }

  // If not, proceed to fire the GA4 'form_submit' event
  // Replace the line below with your GA4 event tracking code
  console.log('Form submitted. Firing GA4 event.');
  
  // Uncomment the line below when integrating with GA4
  // gtag('event', 'form_submit', { /* your event parameters here */ });

  // Update the last submit date in localStorage
  localStorage.setItem('lastFormSubmitDate', today);
}

// Attach this function to your form's submit event
document.getElementById('yourFormID').addEventListener('submit', handleFormSubmit);

You may need to put a lot of checks and balances to ensure that the events are only fired when you want them fired. Generic GTM triggers are not designed for this purpose.

Because of the event-based measurement model, GA4 tracking is far more prone to errors than universal analytics tracking.

#5 Track State Changes 

If a user updates the quantity or adds a new product, consider sending an update to the existing event rather than logging a new one. 

You can use custom parameters to include additional information about the changes.

Tracking state changes rather than duplicate events in GA4 can provide a more accurate representation of user interactions on your website. 

For example, 

If a user repeatedly performs an action (e.g., clicking an “add to cart” button multiple times), tracking state changes helps you avoid counting each click as a separate event, which could inflate your ‘add to cart’ metric and provide an inaccurate view of user behaviour.

You can use a data layer to manage and track state changes effectively. 

The data layer can hold information about the current state and can be used to trigger specific tags based on changes to that state.

GA4 allows for custom parameters with events, enabling you to send additional information about the state change (e.g., the old and new quantities).

Consider a scenario where a user is adjusting the quantity of an item in their shopping cart:

Without State Change Tracking: 

You might send an ‘add_to_cart’ event whenever the user changes the quantity. 

If the user increases the quantity from 1 to 5, one unit at a time, this might result in 5 separate ‘add_to_cart’ events, leading to duplicate events.

With State Change Tracking: 

You can send a single custom event, like ‘cart_quantity_updated’, along with parameters that reflect the old and new quantities. This provides a more accurate view of the user’s actions without creating duplicate events.

6) Use the dataLayer.push() method correctly.

The incorrect implementation of the dataLayer.push() method in GTM can cause the same dataLayer.push() method to be called multiple times, resulting in duplicate events being fired. 

Make sure that the method is only being called once per interaction.

Here’s how you can ensure that the dataLayer.push() method is only being called once per interaction:

1) Use conditional logic to ensure that dataLayer.push() is only called when certain conditions are met. For example, push data only when a particular form is submitted or a specific button is clicked.

2) Use tools like the developer console and GTM preview mode to see what’s happening with the dataLayer as you interact with the website. Ensure that dataLayer.push() is only being called once for the interaction you are tracking.

7) Check for built-in events before you create a new event.

Google recommends that before you create a custom event, ensure there is no automatically collected event, enhanced measurement event or recommended event that already provides what you need. 

Utilizing these built-in events can simplify your tracking setup and reduce the risk of creating duplicate events.

Audit your existing tracking implementation. 

Check what is being tracked automatically or through enhanced measurement, and remove any custom tracking duplicating these built-in features.

Ensure that everyone involved in tracking understands what’s being tracked automatically, through enhanced measurement, or via recommended events, and how to utilize these built-in options.

If you are not happy with the functionality provided by a built-in event and you decide to use the custom event instead, then make sure that the built-in event is not tracked. 

For example, if you use a custom implementation to track scrolling, you should not also track the same event via enhanced measurement. 

#8 Keep detailed documentation of your tracking setup.

Maintain a document listing all the events you currently track. Use this document to keep track of the different parameters you are tracking for each event.

Share this document with all the people involved in implementation work. This document should always be kept up to date.

In your document, clearly describe each event’s purpose and what specific interaction or data it is meant to track and how.

Plan the names of your events and parameters in advance to avoid accidentally naming the same event twice.

Keep detailed documentation of your tagging setup, including tags, triggers, variables, and any related configurations (like data layers) to maintain a reliable and effective tracking setup.

Ensure that everyone involved in tracking understands what’s being tracked automatically and what is being tracked via custom events.

#9 Use a unique transaction ID

Use a unique transaction ID with each ‘purchase’ event to avoid duplicate transactions. 

Two or more transactions with the same transaction ID are called duplicate transactions. 

To learn more, check out this article: Duplicate Transactions (orders) in Google Analytics 4

#10 Monitor GA4 reports for anomalies

At least once a week, check your GA4 reports for any anomalies or unexpected duplications in your events. 

If you find something unexpected, document it. 

Review recent changes to your tracking setup, check for issues with your website or app, or consult with other team members who might have insights.

You may need to test your tracking setup across different browsers and devices to find duplicate events.

If you’re using third-party plugins or integrations, make sure they behave consistently across browsers and devices.

#11 Use debouncing techniques

Implementing debouncing techniques can effectively prevent rapid, repeated actions from causing duplication in your event tracking.

Debouncing ensures that a function (such as an event tracking call) does not execute until a certain amount of time has passed since the last time the function was executed.

This technique can be useful for preventing rapid, repeated clicks from causing duplicate events.

For example, suppose you want to track when a user stops scrolling on a page. 

Implementing the following debouncing function could make sure that the scrolling event is only tracked once the user has stopped scrolling for a specific amount of time (e.g., 500 milliseconds).

// Debouncing function

function debounce(func, wait) {

  var timeout;

  return function() {

    var context = this;

    var args = arguments;

    clearTimeout(timeout);

    timeout = setTimeout(function() {

      func.apply(context, args);

    }, wait);

  };

}

// Create the debounced function

var debouncedFunction = debounce(function() {

  // Fire the event

  dataLayer.push({

    event_name: “event_name”,

    event_value: “event_value”

  });

}, 500);

// Event handler

function onEvent() {

  // Fire the debounced function

  debouncedFunction();

}

// Example of binding to a scroll event

window.addEventListener(‘scroll’, onEvent);

#12 Use throttling techniques

Implementing throttling techniques can also effectively prevent rapid, repeated actions from causing duplication in your event tracking.

A throttling function limits the number of times a function can be executed in a certain amount of time. 

For example,

If you want to track a user dragging a slider on a webpage, you might implement throttling to ensure that the event is only tracked once every 500 milliseconds.

// Throttling function

function throttle(func, limit) {

  var lastFunc;

  var lastRan;

  return function() {

    var context = this;

    var args = arguments;

    if (!lastRan) {

      func.apply(context, args);

      lastRan = Date.now();

    } else {

      clearTimeout(lastFunc);

      lastFunc = setTimeout(function() {

        if ((Date.now() – lastRan) >= limit) {

          func.apply(context, args);

          lastRan = Date.now();

        }

      }, limit – (Date.now() – lastRan));

    }

  };

}

// Create the throttled function

var throttledFunction = throttle(function() {

  // Fire the event

  dataLayer.push({

    event_name: “event_name”,

    event_value: “event_value”

  });

}, 500);

// Event handler

function onEvent() {

  // Fire the throttled function

  throttledFunction();

}

// Example of binding to a drag event

var slider = document.getElementById(‘slider’);

slider.addEventListener(‘input’, onEvent);

My best selling books on Digital Analytics and Conversion Optimization

Maths and Stats for Web Analytics and Conversion Optimization
This expert guide will teach you how to leverage the knowledge of maths and statistics in order to accurately interpret data and take actions, which can quickly improve the bottom-line of your online business.

Master the Essentials of Email Marketing Analytics
This book focuses solely on the ‘analytics’ that power your email marketing optimization program and will help you dramatically reduce your cost per acquisition and increase marketing ROI by tracking the performance of the various KPIs and metrics used for email marketing.

Attribution Modelling in Google Analytics and BeyondSECOND EDITION OUT NOW!
Attribution modelling is the process of determining the most effective marketing channels for investment. This book has been written to help you implement attribution modelling. It will teach you how to leverage the knowledge of attribution modelling in order to allocate marketing budget and understand buying behaviour.

Attribution Modelling in Google Ads and Facebook
This book has been written to help you implement attribution modelling in Google Ads (Google AdWords) and Facebook. It will teach you, how to leverage the knowledge of attribution modelling in order to understand the customer purchasing journey and determine the most effective marketing channels for investment.

About the Author

Himanshu Sharma

  • Founder, OptimizeSmart.com
  • Over 15 years of experience in digital analytics and marketing
  • Author of four best-selling books on digital analytics and conversion optimization
  • Nominated for Digital Analytics Association Awards for Excellence
  • Runs one of the most popular blogs in the world on digital analytics
  • Consultant to countless small and big businesses over the decade