It’s been a while since I’ve last written a bona fide Google Tag Manager trick, so here comes. This was inspired by Bart Gibby’s question in Measure Slack.

The purpose is to fetch the latest currency exchange rates from the exchangeratesapi.io service, cache them using sessionStorage, and push the results into dataLayer. From dataLayer, they can then be utilized in Custom JavaScript variables and custom variable templates to perform client-side conversions. I’ll show you how!

Tip 103: Get latest currency exchange rates in GTM

The API request itself is run in a Custom HTML tag (as custom templates do not support arbitrary HTTP requests yet), so go ahead and create one. In the tag, add the following code:

<script>
  (function() {
    // Set expiration to stored cache. Default 24 hours.
    var cacheExpiresHours = 24;
    // Set currency to get rates against
    var base = 'EUR';
    
    // Do not edit below
    var rates = JSON.parse(window.sessionStorage.getItem('currencyRates') || '{}');
    var now = new Date().getTime();
    var xhr;
    if (!rates.timestamp || rates.timestamp + cacheExpiresHours * 60 * 60 * 1000 <= now) {
      xhr = new XMLHttpRequest();
      xhr.open('GET', 'https://api.exchangeratesapi.io/latest?base=' + base);
      xhr.onreadystatechange = function() {
        if(xhr.readyState === XMLHttpRequest.DONE) {
          rates = JSON.parse(xhr.responseText).rates;
          rates.timestamp = now;
          sessionStorage.setItem('currencyRates', JSON.stringify(rates));
          window.dataLayer.push({
		    event: 'exchangeRates',
		    rates: rates,
            ratesType: 'fresh'
          });
        }
      };
      xhr.send();
    } else {
      window.dataLayer.push({
      	event: 'exchangeRates',
        rates: rates,
        ratesType: 'cached'
      });
    }
  })();
</script>

Modify the cacheExpiresHours to determine how long the currency rates should be cached in the browser for. If you don’t want to cache them, set it to 0. This would fetch the latest rates with every page load.

I would recommend using a cache. The API is a free service, and it’s free for as long as it’s not abused. It makes no sense to continually fetch the latest rates, so set the cache to something you can live with.

You’ll also need to set a base currency against which the rates are calculated. In the example, I’m using 'EUR' as I want the rates to be calculated against euros.

The rest of the code follows this pattern:

  1. If the currency rates are found in browser storage, and the cache expiration duration has not been met yet, then fetch the rates from storage and push them into dataLayer.

  2. If the rates are not found in browser storage, or if the cache expiration duration has been met, fetch the latest rates using an HTTP request to https://api.exchangeratesapi.io/latest, using the base currency rate you provided in the script configuration. Push the results into dataLayer. Cache the results in browser storage.

I also included a key to reflect whether or not the results were cached (ratesType: 'fresh/cached'), which you can use to determine the reliability of the results. This is what the dataLayer would look like:

{
  event: "exchangeRates",
  rates: {
    AUD: 1.6467,
    BGN: 1.9558,
    BRL: 4.4099,
    CAD: 1.4785
    CHF: 1.0919
    CNY: 7.8521
    CZK: 25.727
    DKK: 7.4644
    GBP: 0.9183,
    ...
    timestamp: 1565172369394
  },
  ratesType: 'fresh'
}

Set this tag to fire on the All Pages trigger. That way it will fire as soon as the GTM container has loaded.

Now that the data is in dataLayer, you can create some variables that will let you use the results.

To start off, a Data Layer variable for rates will fetch the corresponding object from GTM’s data model.

Now, to perform a conversion, you need the source variable (the value you want to convert) and this Data Layer variable. For example, if your source value is in Australian dollars, you can convert that to euros with a Custom JavaScript variable that has the following code:

function() {
  // Update these if necessary
  var convertFrom = 'AUD';
  var sourceVariable = {{Source value in AUD}};
  var rates = {{DLV - rates}};
  
  // Don't change anything below
  if (!rates || !rates[convertFrom] || isNaN(sourceVariable)) {
    return sourceVariable;
  }
  return sourceVariable / rates[convertFrom];
}

This variable pulls in the source value and the rates object. In case the rates object doesn’t exist, OR it doesn’t contain the currency symbol you want to convert from, OR the source variable does not return a number, the source variable is returned untouched.

Otherwise, the variable divides the source variable with the exchange rate to give you the source variable value in the base currency you configured in the Custom HTML tag.

You can also create a custom variable template where the user indicates which source variable to use and what the currency symbol would be. The template would then return the result of the conversion.

You can download the template I created here.

NOTE: The currency conversion will only work if the source variable is available when then conversion is made AND if the exchange rates have been successfully fetched. The request for the exchange rates is asynchronous, so you need to be careful not to fall into a race condition. You can use the exchangeRates event name in a Custom Event trigger if you want to fire a tag only after the exchange rates have been successfully retrieved.

I hope this tip proves useful to someone, or at the very least gives you ideas for how to pull in data from public APIs, through GTM, into the user’s web browser.