Shopify, Google Shopping and Product Variants: How to Fix your Feed

The Google Shopping Feed app by Shopify is a great way to publish your products to Google Merchant center. However, if you are a clothing retailer with multiple variants of the same product in different colors the Shopify app will only publish your first variant.


While we wait for a fix to be delivered, here is an alternative way to submit your own product feeds to Google Shopping, including all your variants:

  1. Install and upload your feed using the Shopify Google Shopping App
  2. Create a custom collection template that outputs your products as 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, as we will 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
  • In the dotdotdotmenu and choose 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 = "" %}
<title>{{ }} {{ collection.title | strip_html | strip_newlines | replace: '&', '&amp;' | replace }}</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 %}

<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:price>{{ variant.price | money_without_currency }} {{ shop.currency }}</g:price>
<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>

{% endfor %}
{% endfor %}
{% endpaginate %}

or Download from Github: Shopify xml Variant Shopping 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

Shopify Build a Business

Hack The Shopify Build-a-Business Competition

How to get a leg up on the competition and increase your odds of winning.

Every year Shopify has a competition where the top selling new stores win valuable time with some big name mentors. This year’s contest is the biggest yet: 5 winners will be flown by private jet to Richard Branson’s private retreat, Necker Island, to be mentored by Richard Branson, Seth Godin, Tim Ferriss, Daymond John, and Marie Forleo.

If you are thinking of entering the contest here are some “hacks” to help get a leg up on the competition and increase your odds of winning.

Concentrate your Sales into your Top Two Months

The Shopify Build-a-Business competition takes your top 2 months of sales to determine the winner.  If you can manipulate your sales in such as way to concentrate them into a 2 month window you significantly increase your odds of winning. With the build-a-business contest, it doesn’t matter how many bad months you have as long as you have at least two dynamite months.

You need to be very strategic when picking the two months you want to put your focus on. Pick the two months where you expect your natural organic sales volume to be peaking.


In 2011 Coffee Joulies released an innovative and unique product that sold out almost immediately.  As they were waiting for new stock to arrive they continued to take “pre-orders”. Once their product came back in stock, all those accumulated pre-orders were triggered into a single month giving them a huge single month sales volume, much higher than any other competing store, and sealing their win.

If you have a product that is unique and in demand, you can “fake” an out of stock situation, collect all the unfulfilled demand, and then bring the item back into stock at a strategic time and trigger all those sales in a single month.

Use Promotions

Let’s say that you pick December to be one of your key months. You will want to offer a promotion in November that will encourage customers to delay their purchases to December 1st, and then another promotion at the end of December to encourage customers to make a purchase before the end of the month.

Delay Purchases to the Following Month

Advertise a sale starting on the 1st of the next month — During the last few days of November start advertising that “Starting Dec 1st – All items on sale for 1 day only!”.

Encourage Purchases Before End of Month

An example of a promotion encouraging customers to buy before the end of the month would be something along the lines of a “Free Shipping until the end of the month”.

Target Special Dates

If there are any special dates in your target month such as Mother’s Day, Father’s Day, Black Friday, Boxing Day, etc… Make sure to have strong promotions running around those dates to further boost your sales for the month.

Seasonal products

The Build a Business competition usually runs from September to March. If you are selling a summer seasonal product such as Gas Grills or BBQs, then you are probably out of luck. You will be hitting your sales peaks long after the contest is over (and before the next one begins).

On the other hand if you are selling a Winter seasonal product then you are in good shape as your sales will be peaking at the right time. In 2013 won the 3rd annual competition due in big part to the nature of one of their products: The Canada Goose Arctic Parka (known as “The warmest jacket on earth”). While most merchants hit their peak with the Christmas rush, Canadian Icons continued to peak through January and February due to a cold Canadian winter. Those strong January and February months were key in their win.

Sacrifice Profits for  Sales

Know your Profit Margins and Leverage Google AdWords

You can use Google AdWords to “buy” extra sales. if you are willing to spend your entire profit margin to get a sale, then  you should be able to outbid other advertisers and get a majority of the sales. This isn’t a great ongoing option to keep your business viable, but something to consider if you are trying to boost sales in a couple specific months.  In particular, this works well if:

  1. You are selling a brand or product that has a pre-existing high search volume/demand on Google and;
  2. Your product has high margins or high lifetime customer value and;
  3. You have unlimited stock of this item.

Sacrificing your profits for higher sales volume may not be the best strategy if you are trying to run a profitable business. However if you think it’s the difference between making it to Necker Island or not, then it might be worth it!

Optimize your Order Size

Split orders into $1000 chunks

This year’s Build-a-Business competition will only count the first $1000 of any order. If you think your average order size may be above the $1000 mark consider encouraging customers to split purchases into multiple orders. This way their entire purchase counts towards your Build-a-Business “score” instead of just the first $1000.

Google Display Network & Extended Keyword Match


  • Keyword Targeting on the Display network does not behave the same way as on  the Search network. Because of “Extended Keyword Match” your ads WILL display on pages that DO NOT contain your keywords. In particular for low volume keywords. Even when using phrase or exact match.
  • Pausing / Un-Pausing an AdWords Campaign triggers a Reset or Recalibration which may cause the campaign to behave differently than it did before the pause.

Targeting our Brand Name:
Are we Getting what we Paid for?

We recently noticed something strange with one of our AdWords Display campaigns. The Ad Group was exclusively using Keyword targeting on a phrase matched keyword: Our Brand name. Although it was a low volume Ad Group, the goal was to display our ads on any pages with a mention of our Brand.

Is Pausing and Unpausing Campaigns Safe?

Pausing a Campaign or Ad Group feels pretty safe — after all you are not actually changing anything, just taking a little break. This was our attitude when we decided to pause our campaign for a few hours one day. However, when we unpaused a few hours later the campaign volume suddenly shot up 100x (as did our costs). Nothing had changed with how the campaign was setup. The only factor at play was the pausing and then unpausing of the campaign.


Impressions and Ad Spend over the last 2 weeks of September. The large hump started when we paused and then un-paused our campaigns.

Extended Keyword Match?
But we’re “Phrase” Matching!

When we took a closer look at what was going on we quickly realized that our ads were showing on sites that clearly DID NOT have our Brand name mentioned. We first double checked that we had the setting “Let AdWords automatically find new customers” turned off, and it was. We then dug deeper and discovered that almost 100% of our ad impressions were triggered due to “Extended Keyword Match”.

Extended keyword match is when the placement was relevant to the keywords you chose and other factors, including pages a person seeing your ad has recently browsed.

You can see what percentage of your impressions are coming from this “Extended Keyword Match” by going to: Display > Placements > Segment > Targeting Mode


Almost 100% of our impressions were due to “Extended Keyword Match”

We did NOT generate any conversions from these extended keyword matches. The goal of this Campaign was to display an ad on pages that mentioned our Brand. Instead we were having ads displayed due to “other factors including pages a person seeing your ad has recently browsed” — this clearly was not aligned with our goals.

Happily, Google was in agreement and quickly issued a full credit for the extra amount spent (which may hint that this is a bug or unexplored edge case of some sort).  On a side note, the AdWords inbound customer service was wonderful, friendly, and exceptional — one of the best customer service experiences I’ve had in a long time.


  • Be careful when pausing / unpausing campaigns. This causes some sort of “reset” of statistics in the AdWords backend and causes the campaign start re-calibrating itself. Monitor the campaign for a few days after the unpause to ensure if is behaving as expected.
  • Avoid exclusively using keyword match types in display campaigns. In particular for keywords with very low search volume. Having a campaign targeting your Brand name does not appear to be a viable option as of this writing.
  • Monitor what percentage of impressions are coming from Extended Keyword Match. Consider excluding those placements that are delivering a relatively high volume of “Extended Keyword Match” impressions.

More Reading…

Shopify Cancelled Orders and Google Analytics

By default, when you cancel an order in Shopify, that transaction remains as positive revenue in your Google Analytics.

To “cancel” the transaction in Google Analytics you have to send a negated version of the transaction. To do this in Shopify you have to create a Webhook on Order Cancelled that hits a script (located on the same root domain as your store) that will call server side Google Analytics e-commerce code to negate the transaction.

Webhook Endpoint Dependencies


Place the following code into a file that will act as your Order Cancelled Webhook endpoint (ie:

Make sure you:

  • Update the script to use your GA Account Id and Root Domain.
  • Change the path of autoload.php to point at your php-ga library

Begin Code

use UnitedPrototype\GoogleAnalytics;
require_once '../includes/autoload.php'; // Update to point at your php-ga install

$GA_AccountId = 'UA-********-1'; // Update with your GA account
$GA_domain = ''; // Update with your root domain
$webhookContent = '';
// Read the webhook content
$webhook = fopen('php://input' , 'rb');
while (!feof($webhook)) {
  $webhookContent .= fread($webhook, 4096);

if (!empty($webhookContent)) {
  // Convert the webhook content into an array
  $shopifyOrder = json_decode($webhookContent, true);

  $tracker = new GoogleAnalytics\Tracker($GA_AccountId, $GA_domain);

  $visitor = new GoogleAnalytics\Visitor();

  $session = new GoogleAnalytics\Session();

  $page = new GoogleAnalytics\Page($_SERVER['REQUEST_URI']);
  $page->setTitle('Order Cancelled');

  $tracker->trackPageview($page, $session, $visitor);

  $transaction = new GoogleAnalytics\Transaction();

  foreach ( $shopifyOrder['line_items'] as $product ) {
    $item = new GoogleAnalytics\Item();

  $tracker->trackTransaction($transaction, $session, $visitor);

Setup the Webhook in Shopify

In your Admin dashboard go to:

  • Settings > Notifications > Webhooks (at the bottom)
  • Create a Webhook
  • Event: Order Cancellation // Format: JSON // URL: The full url of your php file


  • Google Analytics: Got to the Real Time > Content report
  • Shopify: Click “Send test notification” link beside your webhook.
  • Google Analytics: You should see a page request popup with your script name
  • Google Analytics: Wait a few hours and then (for transactions to register) and then go to Conversions > Ecommerce > Product Performance and you should see a sledge-hammer and wire-cutter products (the Shopify sample data) along with negative quantities and value.

Further Reading & Resources

The Value of View Through Conversions

If you are running a Display or Retargeting campaign you have probably been exposed to View Through Conversions (VTC): View Through Conversions are conversions where a customer saw — but did not click — a display ad on the display network before completing a conversionThere is a lot of debate on if you should count View Through Conversions or ignore them. How can you be sure they legitimately assisted in the conversion?

A/B Testing vs a Blank Ad

Display Network A/B Test

  1. Create 2 different but equal non-overlapping campaigns
    (If you use AdRoll your rep will be happy to set this up for you)
  2. Campaign A will serve your “normal” ad
    Campaign B will serve a Public Service Announcement ad (in our case it was an ad for the SPCA)
  3. Run both campaigns for a few weeks
  4. Do the math:

Valid VTC Formula

Real Life Example

Campaign A
[Default Ad]
Campaign B
[Blank Ad]
Impressions 99,467 97,412
VTCs 329 261

Valid VTCs = (329 – 261) / 329 = 20%

This means that only 1 in 5 View Throughs actually assisted with conversions — the other 80% had no effect and should be ignored. These results are specific to — your results will vary depending on your business and ad creative.

Do you Really Need a Quantity Box?

Product Quantity Dropdown ListThe other day I was browsing an e-commerce clothing store and the Quantity box suddenly seemed odd to me:  “Who would buy MORE THAN ONE of this shoe – in the EXACT SAME STYLE AND SIZE??”

I wondered how many people EVER buy more than 1 IDENTICAL piece of clothing? (We’re talking same SIZE, STYLE, COLOR, etc…).  I asked my good friends over at Gongshow Gear (makers of awesome hockey inspired clothing) if I could have a peak at their Analytics data, and here are the results:

  • On average, only 1 out of 50 people will buy more than 1 of the same item
  • For non-clothing items that come in a single size & color multiple qty purchases increase (to between 12% and 40% — but still a minority of your customers)

Do we need the Quantity field?

For products where 98% of customers buy a single item I would recommend removing the Quantity field.  If someone really wants to buy 2 of something they can just add the product to the shopping cart twice or change the quantity directly in the shopping cart.

Note: The Quantity field is useful for items where you EXPECT the customer to purchase more than 1 – make sure you dig into your analytics before making your final decision.

Most usability studies show that reducing the number of form elements usually improves usability and so conversion rates should improve by removing the quantity field.  Zappos seems to agree as they have removed the Quantity field from all their product pages.

Does it really matter?

Probably not.  The quantity field usually defaults to 1 item so you never have to touch it anyway.  I have a hard time believing that the quantity field will make a measurable difference to conversion rates for most sites.    If there is a difference, it is probably very very small.  Then again I assume that Zappos measures everything they do on their site and THEY removed the Quantity box.

If anyone decides to run an A/B test to get stats on this, I would love to hear your results.

Google Analytics Custom Events to Simplify Navigation

Below, is a screen shot of a quiz from our online ATV safety course.

ATV Quiz

The navigation is relatively simple:

  • NEXT and PREVIOUS buttons
  • Right hand menu to navigate to any question
  • FINISH QUIZ button that becomes visible once you have answered all the questions

Questions About Usability:

  • Does anyone actually use the PREVIOUS button?
  • Does anyone actually use the side menu to Navigate?

To answer these questions you can setup a series of Google Analytics Custom Events on each button/nav click.

onclick=”_gaq.push([‘_trackEvent’, ‘TDE Navigation’, ‘Next’, ‘Quiz’]);”

WARNING! Google Analytics has a data limit of 500 hits per session (this includes page views, events, and other types of data). Tracking EVERYTHING with custom events could cause you to reach those limits.

Looking at Google Analytics

Total Events

Total events will give an indication of the relative usage of each navigation element

Unique Events

Unique events will give us an idea of navigation usage PER SESSION.  This answers the question “How many users click on the navigation element at least once during a visit”.

The verdict

  • The previous button only receives 1.3% of the total navigation clicks.
  • About 1 in 6 users do click previous at some point in their quiz.

RECOMMENDATION: Previous button should be converted to a simple text link

  • The side menu (Jump) only receives 0.2% of total navigation clicks.
  • Only 3.5% of users ever used the side menu (about 1 in 30 users).

RECOMMENDATION: Remove the side navigation menu all together
(or make it a slide in/out style navigation element)

Next Steps

Consider auto-advancing to the next question when an answer choice is clicked

Not surprisingly, almost 92% of total navigation clicks go to the NEXT button and another 6.5% of total clicks are on the finish button.  So over 98% of the time the user simply wants to advance to the next logical step.

Does this mean that we should we just auto-advance to the next question when the user selects the answer choice? How often does a user change his answer choice before clicking the next button?  The answer to this last question will be saved for another post (after we collect more data)

I hope you found this post useful.  If you know of simpler methods to gain this sort of insight, please post them in the comments below.