Analytics Shopify

Fix Product Performance Reports in Google Analytics with Shopify

By default, Shopify sends transactions to Google Analytics with a unique product title for each product variant. This causes the Product Performance Report to be split on the variant level instead of at the product level as it was intended.

This is how Shopify data shows up by default in the Product Performance Report. Notice that the various “Trillium Parka” variants are ungrouped because of the different size and color information in the product name. This makes it difficult to see “Revenue by Product” for all “Trillium Parkas”.

If, for example. you are selling winter boots, and someone buys a size 10 in Black, Shopify will send the product name as “Winter Boots – 10 / Black” instead of just “Winter Boots“. This is a bug as as the variant details are already included in the Google Analytics “Product Variant” column.

The Solution: GA Custom Data Import

The solution to this problem is to overwrite the Shopify data using the Google Analytics Custom Data Import tool.

1: Export your Product Data

First we need to export all our product data – we can accomplish this by creating a custom Collection Template that generates a CSV report instead of the standard HTML.

a. Create a new Collection Template

Call the new collection template csv-ga-product-feed and paste the following code:

{% layout none %}{% paginate collection.products by 1000 %}ga:productSku,ga:productName,ga:productVariant{% for product in collection.products %}{% for variant in product.variants %}
{{ variant.sku }},{{ product.title | replace: ',','' | remove: '"' | remove: "'" | strip_html | strip }},{{variant.title | replace: ',','' | remove: '"' | remove: "'" | strip_html | strip }}{% endfor %}{% endfor %}{% endpaginate %}

(also available on GitHub here)

b. Create a new Collection based on your csv-ga-product-feed Template

Select the products you want to include in this feed (probably all your products). These will be the products whose values will be overwritten in Google Analytics. Call your collection “Google Analytics Product Data Import” or something similar and save it.

c. Download your Product Feed

  • View your new collection in your store (eg:
  • View source in your browser and save as HTML
  • Rename the file with a CSV extension (eg: google-analytics-product-data-import.csv)

2: Setup and Import the data into Google Analytics

WARNING! You can really mess up your Google Analytics data if things go wrong. I highly recommend that you duplicate or backup your Google Analytics view and do a trial run before working with your live data. Once you upload this new data and overwrite there is no UNDO!

a. Setup the Data Feed

  • Go to Google Analytics > Admin > Account > Property > Data Import
  • Click the red “+ NEW DATA SET” button
  • Select “Product Data”
  • Give your Data Import a name: “Product Name Override”
  • Select the Google Analytics Views you want this import to affect
  • Setup your Data Set Schema: Product SKU is the mandatory key, but select Product and Product Variant as the additional fields.
  • Overwrite Hit Data: Choose Yes (but read my warning above)
  • Click Save and Done

b. Upload your data feed

  • Click on “manage uploads” beside your new Data Feed definition
  • Click the blue UPLOAD button
  • Choose your CSV file and click UPLOAD again
  • And now wait for the upload an update to be complete

3: Verify your new data

The data upload will only affect data from this date forward. So your old data will not be fixed. But your future data will be nice and clean… Until you add new products to your store, in which case you will have to repeat this process.

You will need to wait at least a day before you start seeing the new data coming in. If you add new product SKUs to your store, you will also need to regenerate and reupload a new file in order for the new product data to be fixed.

eCommerce Google Ads Shopify Shopping

DIY Shopify xml Product Feed for Google Shopping

WARNING: This post is OUTDATED and the code is no longer maintained. Although the code should still work, please use at your own risk. I personally now use the Google Sales Channel App in combination with Merchant Center feed rules to manage my Shopping Campaigns.

The custom XML Product Feed Code is still available here: Shopify XML Product Feed Template

Here is how to build your own Shopify XML product feed to submit to Google Shopping or other platforms.

  1. Install and upload your feed using the Shopify Google Shopping App (This will setup some meta fields that are used in the script)
  2. Create a custom collection template that outputs xml
  3. Assign the products you want to publish to your new xml collection template.
  4. Submit your new xml collection url to Google Merchant Center.

Step 1: Install Shopify’s Google Shopping App

Shopify’s app is an excellent tool for managing your Google Shopping product attributes, including custom labels, product categories, etc… The custom feed we are about to build will enhance Shopify’s base feed with extra variant information and customized titles. But the base app is still needed to make use of the app’s metafields.

Step 2: Create the XML Collection Template

This step involves getting your hands dirty with code. If you are not a developer and have a low appetite for risk, consider asking a coder to do this step for you.

What we are doing here is creating a template, that when assigned to a collection, will cause that collection to be displayed as XML instead of HTML.

In your Shopify admin, go to:

  • Online Store > Themes > Edit HTML / CSS
  • Under Templates, choose Add a new Template
  • Choose collection from the drop down and name your template xml-product-feed

Paste the following code into your new template and click Save. (It is probably safer to copy and paste this code from github, as that will always be the most up to date version: Shopify xml Variant Shopping Feed)

IMPORTANT: Do not add any extra spaces before or after this code. Your very first line should be start with {% layout none %} and your very last line should be {% endpaginate %}

{% layout none %}<?xml version="1.0"?> <rss xmlns:g="" version="2.0"> {% paginate collection.products by 1000 %} {%- assign useSEOtitle = false -%} {%- assign useSEOdescription = false -%} {%- assign CountryCode = 'US' -%} {%- if shop.currency == 'CAD' -%}{%- assign CountryCode = 'CA' -%}{%- endif -%} {%- assign Color = "" -%} {%- assign Size = "" -%} <channel> <title>{{ }} {{ collection.title | strip_html | strip_newlines | replace: '&', '&amp;' }}</title> <link>{{ shop.url }}</link> <description>{{ collection.description | strip_html | strip_newlines | replace: '&', '&amp;' }}</description> {% for product in collection.products %}  {%- assign GoogleProductCategory = -%} {%- assign Gender = -%} {%- assign AgeGroup = -%}          {% for variant in product.variants %}     {%- assign Color = "" -%}     {%- assign Size = "" -%}     {%- for option in product.options -%}    {%- if option == 'Color' -%}{%- capture Color -%}{{ variant.options[forloop.index0] }}{%- endcapture -%}    {%- elsif option == 'Size' -%}{%- capture Size -%}{{ variant.options[forloop.index0]  }}{%- endcapture -%}    {%- endif -%}    {%- endfor -%}          {%- capture productTitle -%}{{ product.vendor }} {{ product.title }}{%- endcapture -%}     {%- unless productTitle contains Color -%}{%- capture productTitle -%}{{ productTitle }} {{ Color }}{%- endcapture -%}{%- endunless -%}     {%- if useSEOtitle and > 0 -%}{%- assign productTitle = -%}{%- endif -%}     {%- assign productDescription = product.description -%}     {%- if useSEOdescription and > 0 -%}{%- assign productDescription = -%}{%- endif -%}     {%- assign OnSale = false -%}     {%- assign Price = variant.price -%}     {%- if variant.compare_at_price > variant.price -%}       {%- assign OnSale = true -%}       {%- assign Price = variant.compare_at_price -%}       {%- assign SalePrice = variant.price -%}     {%- endif -%} <item> <title>{{ productTitle | strip_html | replace: '&', '&amp;' }}</title> <link>{{ shop.url }}{{ variant.url }}</link> <description>{{ productDescription | strip_html | strip_newlines | replace: '&', '&amp;' }}</description> <g:google_product_category>{{ GoogleProductCategory | replace: '&', '&amp;'  }}</g:google_product_category> <g:item_group_id>shopify_{{ CountryCode }}_{{ }}</g:item_group_id> <g:id>shopify_{{ CountryCode }}_{{ }}_{{ }}</g:id> <g:condition>new</g:condition> <g:price>{{ Price | money_without_currency }} {{ shop.currency }}</g:price> {%- if OnSale -%} <g:sale_price>{{ SalePrice | money_without_currency }} {{ shop.currency }}</g:sale_price> {%- endif -%} <g:availability>{% if variant.available %}in stock{% else %}out of stock{% endif %}</g:availability> <g:image_link>http:{% if variant.image.src %}{{ variant.image.src | product_img_url: 'grande' }}{% else %}{{ product.featured_image.src | product_img_url: 'grande' }}{% endif %}</g:image_link> <g:gtin>{{ variant.barcode }}</g:gtin> <g:brand>{{ product.vendor }}</g:brand> <g:mpn>{{ variant.sku }}</g:mpn> <g:product_type>{{ product.type }}</g:product_type> <g:age_group>{{ AgeGroup }}</g:age_group> {% unless Color == "" %}<g:color>{{ Color | strip_html | strip_newlines | replace: '&', '&amp;' }}</g:color>{% endunless %} {%- unless Size == "" -%} <g:size>{{ Size | strip_html | strip_newlines | replace: '&', '&amp;' }}</g:size> <g:size_system>US</g:size_system> {%- endunless -%} <g:gender>{{ Gender }}</g:gender> <g:custom_label_0>{{ }}</g:custom_label_0> <g:custom_label_1>{{ }}</g:custom_label_1> <g:custom_label_2>{{ }}</g:custom_label_2> <g:custom_label_3>{{ }}</g:custom_label_3> <g:custom_label_4>{{ }}</g:custom_label_4> <g:shipping_weight>{{ variant.weight | weight_with_unit }}</g:shipping_weight> </item> {% endfor %} {% endfor %} </channel> </rss> {% endpaginate %}

or Download from Github: Shopify xml Product Feed

Step 3: Assign products to your xml Collection.

You should now have a new XML Template created. This is just the template that defines how your collection will be displayed on your site. We now need to assign products to this template so we can submit the feed to google.

This involves creating a new product collection, and choosing the xml-product-feed as the template to use:

  • Go to Products > Collections
  • Click the Add Collection button
  • Enter a name for your new collection. I recommend XML Google Shopping Feed – All if you want a single feed for all your products or XML Google Shopping Feed – Shoes if you want this feed to contain your shoe products (or other product type).
  • Add products you want published as part of this collection feed. (either manually or via dynamic rules)
  • Click Save.

Preview the collection to make sure it works. You should just see a bunch of unformatted text on the screen. If you do a “view source” in your browser, you should now see the formatted XML.

Make sure you copy the url of this collection, as you will need to submit this url to Google Merchant center.

Step 4: Submit your feed to Google Merchant

Make sure you have your xml collection url open from step 3 above.

  • Log into Google Merchant Center or create an account if you do not have one.
  • Go to Feeds and click the +Feed button to create a new feed.
  • Mode: Choose Standard or Test (recommended that you choose Test until you are sure the feed is correct)
  • Feed type: Products
  • Select your target country & language. If you have multiple target countries, you need to submit a separate feed for each.
  • Enter a descriptive name for your feed. eg: Shopify XML Feed – Shoes
  • Choose Scheduled Fetches as your input method (recommend daily)
  • Enter your collection url in the File URL field, and the collection’s filename in the name field (eg: name=google-shopping-feed-shoes and url=
  • Save and click the Fetch Now button to download the feed. Wait a few minutes for the feed to complete.
  • Fix errors: If there are errors, Google Merchant Center will tell you in a few minutes. Go back and fix any errors, re-fetch, and keep doing so until the file is error free.

If you have more than 1000 variants, you will need to submit multiple feeds with the ?page=x querystring appended as so:


If you receive any errors, the best way to troubleshoot them is to open the the collection / feed url in your browser and do a view source. Then navigate to the line & character of the error to investigate further

Common Errors

  • Improperly formatted XML Error on Line 1: This is usually because your template has a blank line as it’s first line. Delete the blank line and make sure your template begins with {% layout none %} … exactly as in the code.
  • Improperly formatted XML Error on Line 1: The other cause of this error is that you forgot to choose the XML template for your Shopping Feed Url, and you are actually outputting HTML. Go to your collection in the Shopify Admin console and set the template correctly (at the bottom of the right hand column) 
  • Errors at Lines XXX: The xml file does not support & and > characters. These must be replaced by encoded versions &amp ; &gt ; or the feed will report errors.

(optional) Step 5: Exclude your feed collection from showing up on your store pages

If your store is setup to programatically display all your collections, then you will want to make sure your xml feed doesn’t show up. The exact way to do this will change depending on your theme, but generally you will want to have an “unless” statement within the loop that displays your collections:

{% unless collection.title contains "xml" %}
... your collection code ...
{% endunless %}


If you have complex rules or customization that you want to apply to your feed, you have several options:

  • Use the Google Merchant Center “Feed Rules” tab to transform various values based on various conditions. This is very powerful!
  • Modify your collection template and add conditional statements based on product titles, tags, metafields, etc…
  • Create multiple collection templates with hard coded values
  • Create custom Feed Rules in Google Merchant Center
  • A combination of the above.

If you have more than 1000 variants, you will need to submit multiple feeds with the ?page=x querystring appended as so:

Related Posts & Resources