Sending hits from R to Google Analytics 4 properties via the Measurement Protocol v2

From the Google docs:

The Google Analytics Measurement Protocol for Google Analytics 4 allows developers to make HTTP requests to send events directly to Google Analytics servers. This allows developers to measure how users interact with their business from any HTTP-enabled environment. Notably, this makes it easy to measure interactions that happen server-to-server.

library(measurementProtocol)

The measurementProtocol library is the one actually making the hits, with googleAnalyticsR calling the same functions. Use library(measurementProtocol) directly if you don’t need the rest of the googleAnalyticsR functions and would like a lighter package to work with.

Usage

Using the ga_mp_send() function you can send tracking hits from any R environment that is connected to HTTP. This can be used in variety of ways including R package tracking; extracting user data then running statistical analysis on GA4 user data and sending it back into GA4; server side tracking implementations of R web apps such as Shiny.

googleAnalyticsR includes opt-in GA4 tracking of the package usage via the ga_trackme() function.

Sending GA4 hits from R

To send hits you need:

  1. A GA4 account
  2. A valid GA4 measurement Id
  3. A secret API key.
    • Create an API secret in your GA4 interface via Admin > Data Streams > choose your stream > Measurement Protocol > Create
  4. A client Id for that user - use ga_mp_cid() if you don’t have one already
  5. Configure custom definitions in GA4 for the events you want to send
    • See Custom definitions > Create custom dimensions

An example is shown then example below:

# preferably set this in .Renviron
Sys.setenv(GA_MP_SECRET="MY_SECRET")

# your GA4 settings
my_measurement_id <- "G-43MDXK6CLZ"

# create a connection object
my_connection <- ga_mp_connection(my_measurement_id)

# a random clientId
a_client_id <- ga_mp_cid()

event <- ga_mp_event("an_event")

# send to debug endpoint first to validate the hit
ga_mp_send(event, a_client_id, my_connection, debug_call = TRUE)

another <- ga_mp_event("another_event")

# send a real hit - batched together
ga_mp_send(list(event, another), 
           a_client_id, my_measurement_id)

# you can see sent events in the real-time reports to check its working
my_property_id <- 206670707
ga_data(my_property_id, 
        dimensions = "eventName", 
        metrics = "eventCount", 
        dimensionFilter = ga_data_filter(
           eventName == c("an_event","another_event")),
        realtime = TRUE)

Configuration of GA4

Once the API secret is created you can put it in an .Renviron file for the GA_MP_SECRET so you do not need to supply it in the R code for security.

You also need a clientId, which is the ID associated with the user creating the hits. Be aware of privacy laws when creating this - for example ePrivacy in the EU dictates you must get consent to associate an ID with a user. If the clientId is the same as other streams into your GA4 account (e.g. website, mobile apps), the hits you send will be associated with the same user.

Once you have those then you need to decide what event you want to send in. To see event parameters in your GA4 reports, create custom fields in your GA4 account first, after which you can see them in your reports 24hrs after you send them - - dimension name will be the label in the reports, event parameter will be the parameter you have sent in with the event.

Creating the connection object

The connection is configured with the API secret and your measurementId. Ideally the API secret should be set in your .Renviron file via environment argument MP_SECRET

# preferably set this in .Renviron
Sys.setenv(MP_SECRET="MY_SECRET")

# your GA4 settings
my_measurement_id <- "G-1234"

my_connection <- ga_mp_connection(my_measurement_id)

By default it will send the hits to https://www.google-analytics.com/mp/collect. To send to the debug URL https://www.google-analytics.com/debug/mp/collect" specify debug_call=TRUE in the ga_mp_send() function.

If you have a custom endpoint (say you are running GTM Server Side) then you can override this URL. In that case you will also want to see your hits appear in the GTM Server Side debugger, which requires a X-Gtm-Server-Preview header added to the request. When in your debugger Web UI’s Preview mode, select from the top right menu “send requests manually” to find your header value and put into the connection like below:

my_custom_connection <- ga_mp_connection(
    my_measurement_id,
    endpoint = "https://gtm.example.com",
    preview_header = "ZW52LTV8OWdPOExNWFkYjA0Njk4NmQ="
)

Sending the GA4 hit via R

You construct an event via ga_mp_event() - a simple one may be ga_mp_event("an_event")

ga_mp_event("an_event")
## 
## ==GA4 MP Event
## {
##   "name": "an_event"
## }

Refer to the Events documentation on what you can send in.

Once you have an event you can test it will show up when you send via ga_mp_send() by setting debug_call=TRUE

my_connection <- ga_mp_connection("G-1234")
a_client_id <- ga_mp_cid()

event <- ga_mp_event("an_event")
ga_mp_send(event, a_client_id, my_connection, debug_call = TRUE)
##  2022-01-13 13:01:31 > MP Request: https://www.google-analytics.com/debug/mp/collect?measurement_id=G-1234&api_secret=MY_SECRET 
##  {
##   "client_id": "96343037.1642078892",
##   "non_personalized_ads": true,
##   "events": [
##     {
##       "name": "an_event"
##     }
##   ]
## }
##  2022-01-13 13:01:31 > Response:  200
##  2022-01-13 13:01:31 > No validation messages found
## [1] TRUE

You can send many events in on send:

another <- ga_mp_event("another_event")
ga_mp_send(list(event, another), 
           a_client_id, 
           my_connection, 
           debug_call = TRUE)
##  2022-01-13 13:01:31 > MP Request: https://www.google-analytics.com/debug/mp/collect?measurement_id=G-1234&api_secret=MY_SECRET 
##  {
##   "client_id": "96343037.1642078892",
##   "non_personalized_ads": true,
##   "events": [
##     {
##       "name": "an_event"
##     },
##     {
##       "name": "another_event"
##     }
##   ]
## }
##  2022-01-13 13:01:32 > Response:  200
##  2022-01-13 13:01:32 > No validation messages found
## [1] TRUE

Events are flexible with the number of parameters you send with the event name - this allows for rich data streams. The data parameters can be added via the params argument and accepts (nested) named R lists:

ga_mp_event("event_with_params", params = list(my_param = "hello", my_param2 = "mum"))
## 
## ==GA4 MP Event
## {
##   "name": "event_with_params",
##   "params": {
##     "my_param": "hello",
##     "my_param2": "mum"
##   }
## }

If you are making product item style hits, the helper function ga_mp_event_item() helps you construct them:

# one item
ga_mp_event_item(item_name = "jeggings", 
                 price = 8.88, 
                 item_variant = "Black")
## ==GA4 MP Event Item
## {
##   "item_name": "jeggings",
##   "item_variant": "Black",
##   "price": 8.88
## }
# many items in a list
items <- list(
  ga_mp_event_item(item_id = "SKU_12345", 
                   price = 9.99, 
                   item_brand = "Gucci"), 
  ga_mp_event_item(item_name = "jeggings", 
                   price = 8.88, 
                   item_variant = "Black"))
                   
# construct an event with its own fields
ga_mp_event("add_payment_info", 
            params = list(coupon = "SUMMER_FUN", 
                          payment_type = "Credit Card", 
                          value = 7.77, 
                          currency = "USD"), 
            items = items)
## 
## ==GA4 MP Event
## {
##   "name": "add_payment_info",
##   "params": {
##     "coupon": "SUMMER_FUN",
##     "payment_type": "Credit Card",
##     "value": 7.77,
##     "currency": "USD",
##     "items": [
##       {
##         "item_id": "SKU_12345",
##         "item_brand": "Gucci",
##         "price": 9.99
##       },
##       {
##         "item_name": "jeggings",
##         "item_variant": "Black",
##         "price": 8.88
##       }
##     ]
##   }
## }

googleAnalyticsR package tracking with ga_trackme

googleAnalyticsR uses the above to provide opt-in tracking of what systems its running on.

ga_trackme() is send on package load if consent is given in the form of a file saved on the user’s computer (much like a website cookie).

You can see what is sent with the debug_call=TRUE flag - the below is sent when this RMarkdown file renders:

ga_trackme_event(debug_call = TRUE, say_hello = "Documentation is a feature")
## Warning in file(con, "r"): cannot open file '/builder/home/.config/
## googleAnalyticsR/optin-tracking': No such file or directory
##  2022-01-13 13:01:32 > MP Request: https://www.google-analytics.com/debug/mp/collect?measurement_id=G-43MDXK6CLZ&api_secret=_hS_7VJARhqbCq9mF3oiNg 
##  {
##   "client_id": "12345678.987654",
##   "non_personalized_ads": true,
##   "events": [
##     {
##       "name": "r_package_loaded",
##       "params": {
##         "r_version": "R version 4.1.2 (2021-11-01)",
##         "r_platform": "x86_64-pc-linux-gnu (64-bit)",
##         "r_locale": "LC_CTYPE=en_US.UTF-8;LC_NUMERIC=C;LC_TIME=en_US.UTF-8;LC_COLLATE=en_US.UTF-8;LC_MONETARY=en_US.UTF-8;LC_MESSAGES=en_US.UTF-8;LC_PAPER=en_US.UTF-8;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=en_US.UTF-8;LC_IDENTIFICATION=C",
##         "r_system": "Ubuntu 20.04.3 LTS",
##         "say_hello": "Documentation is a feature",
##         "package": "googleAnalyticsR 1.0.1"
##       }
##     }
##   ]
## }
##  2022-01-13 13:01:32 > Response:  200
##  2022-01-13 13:01:32 > No validation messages found
## [1] TRUE

Send your own messages if you like! I will read them in the GA4 interface.