Categories
Digital Marketing Google Ads Shopify Shopping

Enhance Shopify’s Google Shopping feed

The following script will allow you to enhance Shopify’s Google Shopping app feed to include:

  • Sale Price annotations
  • Additional Images by Variant: More control over which product images get associated with which variant.
  • Exclude Out of Stock items from Google Programs. If you are paying to advertise, make sure you are only paying to display products you can sell.
  • Exclude Low Stock variants from Google Programs. Exclude color ways that have low size options. Show other color options or products instead.
Sale annotation in Google Shopping
SALE labels and price markdown annotations usually only appear if you provide Google Shopping with both your regular price (compare_at_price) and your sale price.

To add the above functionality, you must create and upload a supplemental data feed to Google Merchant Center.

Step 1
Create a Supplemental Data Feed in Shopify

The first step is to create a data feed in Shopify. We can accomplish this by creating a custom Shopify Collection Template that will output XML data instead of HTML:

1. Create a new Collection Template called collection.google-update.liquid with the following code:

{% layout none %}<?xml version="1.0"?>
<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">

{% comment %}
Google Shopping / Merchant Center + Shopify Product Update Feed by Alex Czartoryski
https://business.czarto.com/2020/08/12/shopify-sale-price-google-shopping/

This version: Oct 14, 2020
The latest version of this script available here:
https://github.com/Czarto/ShopifyScripts/blob/master/Templates/collection.google-feed-update.liquid
{% endcomment %}

{% comment %} Settings {% endcomment %}
{%- assign exclude_unavailable_variants = true -%}
{%- assign exclude_variant_colors_with_limited_availability = true -%}
{%- assign ignore_x_smallest_sizes = 1 -%}
{%- assign ignore_x_largest_sizes = 1 -%}
{%- assign minimum_percentage_availability = 50 -%}
{%- assign filter_variantImages_byColor = true -%}

{%- assign CountryCode = 'US' -%}
{%- if shop.currency == 'CAD' -%}{%- assign CountryCode = 'CA' -%}{%- endif -%}

<channel>
<title>{{ shop.name }} {{ collection.title | strip_html | strip_newlines | replace: '&', '&amp;' }}</title>
<link>{{ shop.url }}</link>
<description>{{ collection.description | strip_html | strip_newlines | replace: '&', '&amp;' }}</description>

{%- paginate collection.products by 1000 -%}
{%- for product in collection.products -%}

{%- comment -%} Get color option {%- endcomment -%}
{%- for option in product.options -%}
{%- if option == 'Color' -%}{% capture option_color %}option{{ forloop.index }}{% endcapture %}{%- endif -%}
{%- endfor -%}

{%- comment -%} Make a list of Colors to exclude {%- endcomment -%}
{%- assign colors_to_exclude = "" -%}
{%- if exclude_variant_colors_with_limited_availability -%}
{%- for color in product.options_by_name['Color'].values -%}
{%- assign variants = product.variants | where: option_color, color -%}
{%- assign variants_to_process_count = variants.size | minus:ignore_x_smallest_sizes | minus:ignore_x_largest_sizes -%}
{%- assign available_count = 0 -%}
{%- assign total_processed_count = 0 -%}
{%- for variant in variants offset:ignore_x_smallest_sizes limit:variants_to_process_count -%}
{%- assign total_processed_count = total_processed_count | plus:1 -%}
{%- if variant.available -%}{%- assign available_count = available_count | plus:1 -%}{%- endif -%}
{%- endfor -%}
{%- if total_processed_count == 0 -%}
{%- continue -%}
{%- endif -%}
{%- assign percentage_availability = available_count | times: 100.0 | divided_by: total_processed_count | round -%}
{%- if percentage_availability < minimum_percentage_availability -%}
{% capture colors_to_exclude %}{{colors_to_exclude}}#{{ color }}{%endcapture%}
{%- endif -%}
{%- endfor -%}
{%- assign colors_to_exclude = colors_to_exclude | split: "#" -%}
{%- endif -%}

{%- for variant in product.variants -%}
<item>
<g:item_group_id>shopify_{{ CountryCode }}_{{ product.id }}</g:item_group_id>
<g:id>shopify_{{ CountryCode }}_{{ product.id }}_{{ variant.id }}</g:id>
<g:mpn>{{ variant.sku }}</g:mpn>

{%- comment -%} Sale Price {%- endcomment -%}
{%- if variant.compare_at_price > variant.price -%}
{%- assign variant_onSale = true -%}
{%- assign variant_price = variant.compare_at_price -%}
{%- assign variant_salePrice = variant.price -%}
<g:price>{{ variant_price | money_without_currency }} {{ shop.currency }}</g:price>
<g:sale_price>{{ variant_salePrice | money_without_currency }} {{ shop.currency }}</g:sale_price>
{% endif %}

{%- comment -%} Additional Images by Color {%- endcomment -%}
{%- assign additional_images = product.images -%}
{%- for option in product.options -%}
{%- if option == 'Color' -%}{% capture variant_color %}{{ variant.options[forloop.index0] }}{% endcapture %}{%- endif -%}
{%- endfor -%}
{% if filter_variantImages_byColor %}{% assign additional_images = product.images | where: "alt", variant_color | sort: 'attached_to_variant' | reverse%}{% endif %}
{% if additional_images.size > 1 %}{%- for image in additional_images offset:1 limit:10 -%}
<g:additional_image_link>https:{{ image.src | product_img_url: 'master' }}</g:additional_image_link>
{% endfor %}{% endif %}

{%- comment -%} Exclude Out of Stock Variants {%- endcomment -%}
{% if exclude_unavailable_variants and variant.available == false %}
<g:excluded_destination>Display ads</g:excluded_destination>
<g:excluded_destination>Shopping ads</g:excluded_destination>
<g:excluded_destination>Shopping Actions</g:excluded_destination>
{% elsif exclude_variant_colors_with_limited_availability and colors_to_exclude contains variant_color %}
<g:excluded_destination>Display ads</g:excluded_destination>
<g:excluded_destination>Shopping ads</g:excluded_destination>
<g:excluded_destination>Shopping Actions</g:excluded_destination>
{% endif %}
</item>

{% endfor %}
{% endfor %}
{% endpaginate %}
</channel>
</rss>

Available on github here

2. Create a new collection called “google-update” and choose google-update as your collection template.

3. Preview the collection and copy the url. Your url should look something like this: yourstoredomain.com/collections/google-update

Step 2
Configure your Feed

There are a few options you can configure in your feed, all located towards the top of your template file in the “configuration” section.

Sale Priceautomatic
Exclude Out of Stock Variants
exclude_unavailable_variants

true
Exclude Limited Stock Color Variants
exclude_variant_colors_with_limited_availability
ignore_x_smallest_sizes
ignore_x_largest_sizes
minimum_percentage_availability

false
1
1
50
Filter Variant Images by Color + Alt Text Matching
filter_variantImages_byColor

false

Sale Price

This is automatically included. If your products have a current price that is less than the compare_at_price then the appropriate sale price data will be automatically updated in the feed.

Exclude Out of Stock Variants

exclude_unavailable_variants = true

By default, any variants that are out of stock will be excluded from Google Shopping, Google Shopping Actions, and Dynamic Remarketing. Change the value of exclude_unavailable_variants = false if you want to disable this behaviour.

Exclude Limited Stock Color Variants

exclude_variant_colors_with_limited_availability = true

This setting is intended for apparel products, where you may have many colors of a product, but limited sizes available in a specific product. Setting this value to true will cause the script to attempt to exclude the colors with low availability (so that alternative colors or products can show instead).

There are a few additional configuration items for this setting that you can change:

minimum_percentage_availability
default = 50
Minimum % of sizes available before all variants of this color are excluded.
ignore_x_smallest_sizes
default = 1
Ignore the x smallest sizes in the % available calculation
ignore_x_largest_sizes
default = 1
Ignore the x largest sizes in the % available calculation

Filter Variant Images by Color
and Alt Text Matching

filter_variantImages_byColor = true

Setting this value to true will assign additional images to the current variant where the image’s Alt Text matches the variant’s color. Note that the primary variant’s image will always be included in the feed regardless of Alt text.

Step 3
Add a Supplemental Data Feed in Google Merchant Center

1. Open Merchant Center and go to
Products > Feeds > Supplemental Feeds > Add Supplemental Feed

NameFeed Update Script
Feed TypeScheduled Fetch
File Namegoogle-update
File Urlyourstoredomain.com/collections/google-update

Leave everything else as default values and click Continue

2. Make sure there’s a checkmark beside Content API and click Create Feed

3. You should now see your newly created feed in the Supplemental Feeds section. Click on your feed’s name and then click on Fetch Now to update your product data now.

Testing

It may take up to 30 minutes for your main feed to be updated. It is a good idea to review your products and feed to ensure that everything is coming through as expected, and tweak as required.

If everything looks good, your new sale pricing, variant images, and program availability should now be updated once per day.

Related Reading

Categories
Attribution Digital Marketing eCommerce Google Ads Shopify

Optimizing Shopify’s Google Ads Conversion Tracking

Here are some essential changes to make to your Google Ads conversion tracking after you connect Shopify’s Google Shopping Channel.

STEP 0
Install the Google Shopping Channel

This guide assumes that you have the Google Shopping Channel installed on your Shopify store. If this is not the case, then:

  1. Install the Google Shopping Channel
  2. Connect your Google Account (must be an account that has access to both Google Merchant Center and Google Ads)
  3. Connect your Google Ads Account in the “Smart Shopping campaign” section

STEP 1
Remove Old Conversion Tracking Code

By connecting Shopify to Google Ads via the Google Shopping Channel, Shopify will begin sending conversion data to your Google Ads account. If you were already tracking conversions in Google Ads, then you need to make sure you are not duplicating your conversion data:

  • If you previously had a Google Ads conversion tracking script installed in your checkout, then remove that code.
  • If you were importing conversions from Google Analytics, stop importing those conversions.

STEP 2
Fix Conversion Categories

Log into Google Ads and then Go to
Tools & Settings > Measurement > Conversions

You should see a bunch of new conversion actions created by Shopify. You will also see a warning that your conversion categories are out of date, and that you should update 4 of your conversions actions.

Click Update Now and update the settings to the following:

STEP 3
Conversion Windows and Attribution Models

Although Shopify created multiple conversion actions, you only need to worry about the Google Shopping App Purchase conversion event.

Click on the Google Shopping App Purchase conversion action and update the following settings:

Old ValueNew Value
CountEvery ConversionOne
View-through Conversion Window30 days1 day
Attribution ModelLast clickLinear

One Conversion per click

If you count “Every Conversion” you increases the risk of double attribution across your various channels.

For example: A user clicks on your Google ad and makes a purchase. It makes sense to count and attribute this conversion to Google Ads. If this same user later receives your newsletter, and purchases again, then you probably want to attribute that sale to the Newsletter and NOT to Google. Setting conversion count to “One” instead of “Every” ensures that only the first sale gets attributed to google, and not the second.

Counting “Every” conversion increases the risk of double attribution

Click-Through Conversions

Arguably, you should set your Click-Through Conversion window to 30 days. 90 days seems extremely long, and could increases your chances of double attribution across multiple sales channels.

However, if you are using Smart Campaigns or Smart Bidding, the bidding algorithm can only take into account conversions that have occurred within the specified conversion window. So theoretically, the longer your conversion window, the more data for Smart Bidding to optimize with. So the 90 day click-conversion window can probably stay.

View-Through Conversions

Set the View-Through Conversion window to only 1 day. A longer View-Through conversion window is dangerous, and will lead to over attributing sales to Display Remarketing, YouTube, and Smart Shopping. This will invariably cause you to overspend on those campaigns.

A View-Through conversion window greater than 1 day is dangerous, and will lead to over attributing sales to undeserving channels.

Which Attribution Model to use?

Linear, Time Decay, Position Based, or Data Driven are all better than First or Last click, as they allow you to attribute sales across a wider range of ad interactions, which allows you to spend more evenly across your entire funnel.

I personally usually choose Linear or Position Based, as I like pushing more attribution (and therefore spend) into the top part of the funnel.

Further Reading

Categories
Digital Marketing Google Ads Shopify Shopping

Add Sale Price to Shopify’s Google Shopping feed

As of Aug 12, 2020 Shopify’s Google Shopping app does NOT send compare_at_price to Google Merchant Center. This means that you are unlikely to receive the SALE annotation in your Google Shopping ads when your products are on sale:

Sale annotation in Google Shopping
SALE labels and price markdown annotations usually only appear if you provide Google Shopping with both your regular price (compare_at_price) and your sale price.

To fix this, you must create and upload a supplemental data feed to Google Merchant Center.

Create a Price Feed in Shopify

The first step is to create a data feed in Shopify containing your products sale and regular prices. We can accomplish this by creating a custom Shopify Collection Template that will output XML data instead of HTML:

1. Create a new Collection Template called collection.xml-sale-pricing.liquid with the following code:

{% layout none %}<?xml version="1.0"?>
<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">
{% paginate collection.products by 1000 %}
{%- assign CountryCode = 'US' -%}
{%- if shop.currency == 'CAD' -%}{%- assign CountryCode = 'CA' -%}{%- endif -%}
<channel>
<title>{{ shop.name }} {{ 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 %} 
  {% for variant in product.variants %}
    {%- if variant.compare_at_price > variant.price -%}
      {%- assign OnSale = true -%}
      {%- assign Price = variant.compare_at_price -%}
      {%- assign SalePrice = variant.price -%}
        <item>
            <g:item_group_id>shopify_{{ CountryCode }}_{{ product.id }}</g:item_group_id>
            <g:id>shopify_{{ CountryCode }}_{{ product.id }}_{{ variant.id }}</g:id>
            <g:price>{{ Price | money_without_currency }} {{ shop.currency }}</g:price>
            <g:sale_price>{{ SalePrice | money_without_currency }} {{ shop.currency }}</g:sale_price>
        </item>
    {%- endif -%}
{% endfor %}
{% endfor %}
</channel>
</rss>
{% endpaginate %}

Also available on github

2. Create a new collection called “google-feed-sale-price” and choose xml-pricing-feed as your collection template.

3. Preview the collection and copy the url. Your url should look something like this: yourstoredomain.com/collections/google-feed-sale-price

Add a Supplemental Data Feed in Google Merchant Center

1. Open Merchant Center and go to
Products > Feeds > Supplemental Feeds > Add Supplemental Feed

  • Name: Sale Pricing Update
  • Feed Type: Scheduled Fetch
  • File Name: google-feed-sale-price
  • File Url: yourstoredomain.com/collections/google-feed-sale-price

Leave everything else as default values and click Continue

2. Make sure there’s a checkmark beside Content API and click Create Feed

3. You should now see your newly created feed in the Supplemental Feeds section. Click on your feed’s name and then click on Fetch Now to update pricing data immediately.

Done

It may take up to 30 minutes for your main feed to be updated. Any new sale pricing will now be uploaded once per day.

Related Reading

Categories
Digital Marketing Google Ads

Essential Google Ads Remarketing Audiences

Below is a list of the Essential Google Audiences the every e-commerce site should create in their Google Ads Account. Even if you don’t plan on using them right away, creating them now will ensure that they can grow so they are ready to be used in the future.

Google’s Built-in Audiences

Before we create our own audiences, we first need to install the Google Ads Dynamic Remarketing Code. This triggers Google to automatically create some built-in Remarketing Audiences:

Audience NameDurationDescription
Shopping cart abandoners30 daysPeople who added products to the shopping cart in the past 30 days but did not complete the purchase
Product Viewers30 daysPeople who viewed specific product pages on your site in the past 30 days but did not create a shopping cart
Past buyer30 daysPeople who purchased products from you in the past 30 days
All visitors30 daysPeople who visited pages that contain your remarketing tags in the past 30 days
All converters180 daysPeople who converted on your site in the last 180 days. Based on your conversion tracking tag. This is not necessarily people who have purchased from you, but anyone who has triggered a “conversion”. (eg: Phone call from an ad)
General Visitors30 daysPeople who visited your website in the past 30 days but did not view any specific products

Additional Audiences

The additional audiences to create follow the same pattern used in Google’s built-in audiences, but with expanded membership durations. The main focus is on Shopping Cart Abandoners, Product Viewers, and Past Buyers.

Shopping Cart Abandoners

Google will have already created a “Shopping cart abandoners” audience with 30 day time window. We will create the following additional audiences for 7, 14, 90, and 180 day durations:

Audience Name:Shopping cart abandoners: xd
“x” will be the duration.
Eg: Shopping cart abandoners: 7d
List Members:Visitors of a page who did not visit another page
Visited page:URL contains cart
Unvisited pageURL contains thank_you
Membership Duration:7, 14, 90, 180 days
* 30 day duration is already created by Google

Past Buyers

Google will have already created a “Past buyers” audience with 30 day time window. We will create these additional audiences for 14, 90, 180, 365, and 520 day durations:

Audience NamePast buyers xd
“x” will be the duration.
Eg: Past buyers: 14d
List MembersVisitors of a page with specific tags
TagsPurchase
Membership duration14, 90, 180, 365, 520 days

Product Viewers

Google will have already created a “Product viewers” audience with a 30 day duration. We will create additional audiences with 14, 90, 180, 365, and 520 day durations.

Audience Name:Product viewers: xd
“x” will be the duration.
Eg: Product Viewers: 14d
List Members:Visitors of a page who did not visit another page
Visited page:URL contains product
Unvisited pageURL contains cart
Membership Duration:14, 90, 180, 365, 520 days
* 30 day duration is already created by Google

General Notes

  • The Membership Durations are somewhat arbitrary. You can get more or less granular, and set your own intervals. It is however best to start with something simple and add granularity later on.
  • A Remarketing Audience needs a minimum of 1,000 members to be eligible to serve. When creating your audience durations, consider how much time it will take to reach 1,000 memebers. eg: How long will it take before your site generates 1,000 abandoned carts, or 1,000 purchases? That will probably be the shortest duration with which you should start.
  • You should generally add all these lists as “Observations” to all your campaigns.

More Reading…

Categories
Attribution Digital Marketing Google Ads

YouTube Video Campaigns are Over-Reporting Conversions

YouTube Video Campaigns count Conversions very differently from other Campaign types

YouTube Video Campaigns count view-conversions as click-conversions, and include them in the “Conversions” columns of Google Ads.

If you are running a ROI focused YouTube Campaign that is targeting a lower funnel Remarketing Audience, the revenue and conversions reported by Google Ads can be over-inflated by 1000% or more vs what Google Analytics will report.

When running a YouTube Campaigns, always:

  1. Be suspicious and skeptical of the conversion data
  2. Adjust your performance targets. Conversions will be overinflated by a factor of up to 1000%, so adjust your performance accordingly.
  3. Use Google Analytics as your measure of YouTube Campaign performance
  4. Avoid Targeting lower funnel Remarketing Lists with YouTube. Save these lists for campaigns that offer true click-based conversion tracking.
  5. Explicitly exclude low funnel Remarketing Lists from your targeting. This will ensure the attribution poaching is limited.

YouTube Conversion Tracking Explained

Standard Campaign Behaviour

For most campaign types, a conversion is only recorded in the “Conversions” column when someone clicks on your ad and then proceeds to make a purchase. This is called a Click-Through Conversion. If there are no clicks on your ad, no conversion is recorded in the Conversions column.

This is the normal expected behaviour.

YouTube Campaign Behaviour

For YouTube campaigns, clicks behave normally: If someone clicks on your ad and then makes a purchase, that purchase is recorded as a conversion as expected. So far so good.

However, if someone DOES NOT CLICK on your ad, but watches your entire ad, and then later purchases, that purchase will be recorded as a conversion and attributed to YouTube.

As per Google:

A ‘view’ is counted when someone watches 30 seconds (or the whole ad if it’s shorter than 30 seconds) or clicks on a part of the ad. A ‘view’ that leads to a conversion is counted in the ‘Conversions’ column. 

Google Ads Help: Understand your conversion tracking data
https://support.google.com/google-ads/answer/6270625

Essentially, these are View-Through Conversions masquerading as Click-Through Conversions. The ad was never clicked, yet a conversion was recorded anyway. Considering that many ads are only 5 seconds long, most ad views are likely being treated as clicks.

This is NOT EXPECTED behaviour!

Attribution Poaching

Attribution Poaching is when one Channel tries to take credit for a sale that was either going to happen anyway, or for a sale that was actually caused by another Channel.

Consider this scenario

You create a YouTube campaign targeting your Shopping Cart Abandoners with a 5 second video ad.

One of your potential customers visits your site, and ads a product to their Shopping Cart. By adding to the Shopping Cart, they are now in your Shopping Cart Abandoners audience, and will be actively targeted by your YouTube Campaign.

They continue to browse your site, but they are interrupted by an e-mail or text from a friend with a link to a funny YouTube video. They click on the link and watch the video. While watching the video, they are forced to watch your 5s video ad (which they ignore). Since they watched y0ur entire ad, Google considers that a “click”.

They then eventually return to your site, to complete the purchase. Or maybe they complete the purchase 3 days later after receiving your Cart Abandonment e-mail.

In either case, your YouTube Campaign will claim credit for the Purchase, and will count it as a Conversion even thought that customer never clicked on your ad.

Why is Google Doing This?

A video view is much more like an impression than like a click, so why are these conversions being lumped in with click-through conversions?

The simple cynical answer is that Goolge makes more more money this way. By counting view-through conversions, the YouTube campaigns will appear to perform much better than they actually do with just click-through conversions (as much as 1000% better). If advertisers think that YouTube is performing 10X better, then they will allocated 10X more budget. The end result is Google makes 10X more money from YouTube.

A “Video View” is more like an Impressions than a Click

Video-View-Conversions should clearly be lumped into the View-Through Conversion column. If Google wants to explicitly report on Video-View-Conversions separately, then they should create another column type. Don’t lump them in with click-through conversions.

Unexpected Behaviour

The problem with all this is that suddenly the “Conversions” column behaves differently in one campaign type vs another. Suddenly the clean click-through conversion data is being polluted with View-Through conversion data. This makes YouTube campaigns appear to perform much better than they should. Which will lead you to incorrectly increase spend.

To make the matter worse…

  1. Many people have long click-through conversion windows. Often 30 days or even longer. This essentially allows a YouTube to claim credit for a conversion that happens 30 days after a video view.
  2. Many video ads are short – only 5s long. Most of these short ads are probably viewed in their entirety, meaning that they are all being counted as clicks. Often the user is forced to watch the entire 5s ad – again a click. This leads to more incorrectly attributed click-conversions.
  3. Often people target video ads using remarketing lists. Often the lowest hanging fruits for video campaigns are Remarketing Audiences. This shows ads to people who know your brand, who have recently visited your site, who may be subscribed to your newsletter, and who may in fact be currently actively shopping on your site. They are all likely to buy from you regardless of the the video ad, but the video ad will take credit for all their purchases.

This could also affect your Display and Discovery Campaigns

This issue could also affect your regular display campaigns and possibly your Discovery campaigns if they are serving ads on the YouTube network.

My recommendation for Display Campaigns is to try and exclude all YouTube placements: Go to: Campaign Settings > Additional Settings > Content Exclusions and select all of the following for exclusion:

  • Live streaming YouTube video
  • Embedded video
  • In-video

Notes:

  • This should only be an issue if you use Google Ads Native Conversion tracking. If you import your conversions directly from Google Analytics, then this should not be an issue (as Analytics only attributes conversions to the last click)
  • If you are running  a pure brand awareness campaign, this is probably less of a concern for you.
  • If you are an Ad Agency getting paid as a % of ad spend, then YouTube campaigns can make you a lot of money.

More Reading

Categories
Digital Marketing Google Ads Shopify

Google Ads Dynamic Remarketing for Shopify

Last Updated: September 25, 2020

STEP 1
Activate Dynamic Remarketing in Google

  1. Go to Google Ads > Tools & Settings > Shared Library > Audience Manager > Audience Sources
  2. Click Set up tag in the “Google Ads tag” card
  3. Remarketing: Choose “Collect data on specific actions…
  4. Business Type: Choose “Retail
  5. Retail Parameters: Select All Parameters
  6. Click Save and continue.
  7. Click on Install Tag Yourself
  8. In the first code box, look for the number at the end of the first line of code and write it down or copy it. This is your Google Conversion Id — you will need it in the next step.
<!-- Global site tag (gtag.js) - Google Ads: 123456789 -->
  1. Click Continue and then Done

STEP 2
Install the Remarketing Code in Theme.liquid

In your store’s admin section go to:

  • Online Store > Themes > Actions > Edit Code
  • Expand the Snippets section and choose Add new snippet
  • Call the snippet “adwords-remarketing
  • Paste the code below into the snippet
  • Enter your google_conversion_id that you obtained in Step 1.8 above.
{% comment %}
Google Ads Dynamic Remarketing Script by Alex Czartoryski
https://business.czarto.com/2017/02/07/shopify-dynamic-remarketing-setup/

This version: Sept 30, 2020
The latest version of this script available here:
https://github.com/Czarto/ShopifyScripts/blob/master/snippets/adwords-remarketing.liquid
{% endcomment %}

{% comment %}Set to false if GTAG is already loaded on the page. Leave to true if unsure.{%endcomment%}
{% assign load_gtag = true %}

{% comment %} Enter your google conversion id below {% endcomment %}
{% assign google_conversion_id = 123456789 %}

{% assign shopify_store_country  = 'US' %}
{% if shop.currency == 'CAD' %}
{% assign shopify_store_country  = 'CA' %}
{% elsif shop.currency == 'AUD' %}
{% assign shopify_store_country  = 'AU' %}
{% endif %}

{%if load_gtag %}
<!-- Global site tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=AW-{{ google_conversion_id }}"></script>
{% endif %}
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'AW-{{ google_conversion_id }}');
</script>

{% assign google_event = false %}
{% assign google_items = false %}
{% assign google_value = false %}
{% if template contains 'cart' %}
	{% assign google_event = 'add_to_cart' %}
	{% capture google_items %}{% for item in cart.items %}{'id':'shopify_{{ shopify_store_country  }}_{{ item.product.id }}_{{ item.variant.id }}','google_business_vertical': 'retail'}{% unless forloop.last %}, {% endunless %}{% endfor %}{% endcapture %}
	{% assign google_value = cart.total_price %}
{% elsif template contains 'collection' %}
	{% assign google_event = 'view_item_list' %}
	{% capture google_items %}{% for item in collection.products limit:5 %}{'id':'shopify_{{ shopify_store_country  }}_{{ item.id }}_{{ item.variants.first.id }}','google_business_vertical': 'retail'}{% unless forloop.last %}, {% endunless %}{% endfor %}{% endcapture %}
{% elsif template contains 'product' %}
	{% assign google_event = 'view_item' %}
	{% capture google_items %}{'id':'shopify_{{ shopify_store_country  }}_{{ product.id }}_{{ product.selected_or_first_available_variant.id }}','google_business_vertical': 'retail'}{% endcapture %}
	{% assign google_value = product.selected_or_first_available_variant.price %}
{% elsif template contains 'search' %}
	{% assign google_event = 'view_search_results' %}
	{% capture google_items %}{% for item in search.results limit:5 %}{'id':'shopify_{{ shopify_store_country  }}_{{ item.id }}_{{ item.variants.first.id }}','google_business_vertical': 'retail'}{% unless forloop.last %}, {% endunless %}{% endfor %}{% endcapture %}
{% endif %}

{% if google_event %}
<script>
	gtag('event', '{{ google_event }}', {
	  'send_to': 'AW-{{ google_conversion_id }}',
	  {% if google_value %}'value': '{{ google_value | divided_by: 100.0 }}',{% endif %}
	  'items': [{{ google_items }}]
	});
</script>
{% endif %}

The latest version of this code is available on Github

Add snippet to your Theme file

Open up Layout > theme.liquid and add the following line of code before the closing </head> tag:

{% include 'adwords-remarketing' %}

STEP 3
Install Remarketing in the Checkout Scripts

  • In the very bottom left hand corner the Shopify Admin choose Settings and then Checkout
  • Scroll down to the Additional Scripts section.
  • Copy and paste the code below into the “Additional Scripts” field and update google_conversion_id with your value from step 1.8 as before.
{% comment %}
Google Ads Dynamic Remarkting Script by Alex Czartoryski https://business.czarto.com/

This version: Sep 30, 2020
The latest version of this script available here:
https://github.com/Czarto/ShopifyScripts/blob/master/settings/checkout/adwords-remarketing.liquid
{% endcomment %}

{% comment %}Set to false if GTAG is already loaded on the page. Leave to true if unsure.{%endcomment%}
{% assign load_gtag = true %}

{% if first_time_accessed %}
{% comment %} Enter your account specific values below {% endcomment %}
{% assign google_conversion_id = "123456789" %}

{% assign shopify_store_country  = 'US' %}
{% if shop.currency == 'CAD' %}
{% assign shopify_store_country  = 'CA' %}
{% elsif shop.currency == 'AUD' %}
{% assign shopify_store_country  = 'AU' %}
{% endif %}


{%if load_gtag %}
<!-- Global site tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=AW-{{ google_conversion_id }}"></script>
{% endif %}
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'AW-{{ google_conversion_id }}');
</script>

<!-- Event snippet for Web Order conversion page -->
<script>
    // Google Ads Remarketing
    gtag('event', 'purchase', {
	  'send_to': 'AW-{{ google_conversion_id }}',
	  'value': '{{ total_price | divided_by: 100.0 }}',
	  'items': [{% for item in order.line_items %}{'id':'shopify_{{ shopify_store_country }}_{{ item.product.id }}_{{ item.variant.id }}','google_business_vertical': 'retail'}{% unless forloop.last %}, {% endunless %}{% endfor %}]
	});
</script>

{% endif %}

Download the lastest version from Github

STEP 4
Verification

Once you’ve installed all your code, it’s time to run through your main pages (collection, product, cart, and purchase pages) with Google Tag Assistant installed to make sure there are no errors.

Next Steps
Configure your Remarketing Audiences

Now that your store is collecting dynamic remarketing data, the next step is to properly organize and segment your visitors into Purchasers, Cart Abandoners and Product Viewers. This is covered in the next post about Google Ads Remarketing Audiences.

Additional Reading…

Categories
Digital Marketing Google Ads

Google Ads Remarketing Campaign Structure

Remarketing Theory

  1. The deeper a user is in your sales funnel, the more likely he is to buy. A shopping cart abandoner is more likely to buy than a product browser who is more likely to buy than someone who briefly visited your homepage.
  2. The more recent the user’s visit, the more likely he is to buy. A user who visited your site yesterday is more likely to buy than a user who visited your site 10 days ago.
  3. The more likely a user is to buy, the more you want to bid on that user.

Audiences

Please read the post Essential Google Ads Remarketing Audiences and follow the instructions to create your remarketing audiences.

Campaign Setup

Create one campaign per major market you are targeting, and give them a descriptive name:

  • USA: Display Remarketing
  • Canada: Display Remarketing

Generally, but not always, you will want a separate campaign for every unique currency and language you are targeting.

Core Ad Groups

Creating only these two ad groups will generate 90% of your display remarketing conversions . I highly recommend you start with only these two. Only add more groups (described later) if the extra 10% in remarketing sales will justify the extra administrative complexity.

1. Cart Abandoners

This ad group will target cart abandoners: Visitors who added a product to their cart but never purchased. Dynamic Product ads perform particularly well with cart abandonment, as your visitors are shown the exact products that they added to their cart.

Audiences:
  • Cart Abandoners – 1 day
  • Cart Abandoners – 4 day

2. Product Viewers

This ad group will target users who visited a product details page but who never added a product to their cart.

Audiences:
  • Product Viewers – 7 day
  • Product Viewers – 28 day

Optional Ad Groups

Here are additional remarketing ad groups and audiences. In my experience, most conversions come from the two core ad groups identified above (Cart Abandoners and Product Viewers) but you may have a different experience depending on your business.

Just Purchased

This will show ads to people who have just purchased. This is a good place to possibly push micro conversions such as joining a loyalty program, joining a community, etc… Or perhaps up-selling them another related product?

Audience
  • Purchase 7 days

Past Customers

This is similar to the “Just Purchased” ad group, but with a longer time window. This will show ads to people that have previously purchased a product 30 or more days ago. This is a good place to advertise new product launches or promotions.

Audience
  • Purchase 30 days
  • Purchase 90 days
  • Purchase 360 days

Never Purchased

This is the opposite of the “Past Customers” group. These are people who visited your site in the past year, but have never purchased. This is also a good place to advertise new product launches or promotions.

Audience
  • Site Visitors 30 days
  • Site Visitors 90 days
  • Site Visitors 360 days
Exclude Audience
  • Purchase 360 days

New Customers, Brand Unaware

This isn’t really remarketing, but simple display advertising: These are people who have never visited your site. Your remarketing audiences are EXCLUDED so that you are only targeting people who are unaware of your product or brand. You are arguably willing to pay more for each customer as they are new customers, but this will be your worst performing segment. You can waste a lot of money here, so be careful.

Audience
  • Combination of keyword, interest, topic, and placement targeting relevant to your product and brand.
Exclude Audience:
  • Site Visitors 360 days

Other Tips

Exclude “low quality” visitors

You will have to create these audience groups within Google Analytics, and then exclude them from all your remarketing campaigns:

  • Exclude people who have bounced
  • Exclude people who have spent less than ten seconds on the site

Exclude Mobile App Placements

Exclude placements where users are unlikely to interact with your ad, or where they may accidentally click your ad, such as in mobile apps and games.

To exclude mobile apps, go to your ad group and then select:

  • Placements > Exclusions tab > Exclude placements
  • App Categories > Expand All App Categories, and exclude all app categories individually
  • Repeat for all your display ad groups

Exclude YouTube Placements

YouTube tracks View Through Conversions as if they were Click Through Conversions. This leads to attribution poaching, and makes your display campaigns appear to perform much better than they actually do. This ultimately causes you to increase bids and budgets, and overspend.

To exclude YouTube placements, go to: Campaign Settings > Additional Settings > Content Exclusions and select all of the following for exclusion:

  • Live streaming YouTube video
  • Embedded video
  • In-video

Frequency Capping

Most resources online will tell you to limit your frequency cap to about 20 times per user per month, but I suspect this is wrong. That means a user will be exposed to your ad once every other day or so, with no guarantee that he actually noticed it.

I normally set the frequency capping to 20 times per user PER DAY. If you think of how many web pages you visit in a day, 20 ads will be gone fairly quickly and you will probably miss most of them. Add in that for cart abandonment ads you really want to hit the abandoners hard within the first 24 hours, I think the 20 impressions per day is the safer bet.

But the true answer will vary based on your business, product, and customers.

A word about View Through Conversions

View Through Conversions are conversions where a display ad appeared on the screen, was NOT clicked, but the user ended up purchasing on your site sometime later. In general I recommend that everyone IGNORE View Through Conversions, in particular in remarketing campaigns.

What usually happens, is that an ad is displayed on screen, the visitor may not even see it, but clicks instead on a cart-abandonment e-mail and makes the purchase. AdWords will credit that conversion to the view through.

The one exception is for “brand unaware” customers. These are customers that have never visited your website before. If such a customer sees you ad, and purchases, then the odds are better that it was a result of your ad.

In an ideal world, there would be a simple way to test the value of your view-through-conversions, as they are different for every segment, and every business.

Other Resources

Categories
Analytics Digital Marketing Google Ads

Why doesn’t my Ad Spend Scale?

Consider this Scenario:

You spend $1,000 on a new Ad Campaign that generates $10,000 in revenue. “That’s a great ROI” you tell yourself, “Let’s double the spend!“.

When you double the budget to $2,000, your Campaign only generates $12,500 in total sales and not the $20,000 you were expecting. Why?

SpendSalesCost of SalesROAS
Ad Campaign #1$1,000$10,00010%1000%
Ad Campaign #2$2,000$12,50016%625%
Increasing spend by $1,000 only resulted in $2,500 in additional revenue: An incremental cost of sales of 40% and an incremental ROAS of 250%

Why doesn’t it scale?

By scale I mean that your ROI should be linear: If the first $1,000 generates $10,000 in sales, then the next $1,000 should also generate $10,000 in sales.

The issue is that rarely is your campaign performance evenly distributed. If your drill down deeper into your initial $1000 Campaign, you might see the spend broken down into something like this:

SpendSales%COSROAS
Branded$200$8,0002.5%4000%
Unbranded$800$2,00040%250%
$1000 Campaign$1,000$10,00010%1000%
Majority of the sales are being generated by a small subset of the overall campaign. A classic 80/20 scenario. The performance of the Branded ad set is subsidizing the cost of the unbranded ad set.

The performance of the Branded subset is subsidizing the cost of the Unbranded subset. 80% of your sales are coming from only 20% of the spend, while 80% of your spend is going towards an underperforming segment.

When we try to double the budget to $2,000, here is how the budget gets allocated:

SpendSalesCOSROAS
Branded$200$8,0002.5%4000%
Unbranded$1,800$4,50040%250%
$2,000 Campaign$2,000$12,50016%625%
When budget is doubled, most of the spend goes towards the underperforming segment, resulting in disappointing incremental sales.

With this new data in mind, we should probably:

  1. Decrease budget on the Unbranded segment
  2. Increase budget on the Branded segment

However, it is probably the case that your performing segment is already receiving 100% reach/impressions. So spending more is usually not possible. (In particular for Google Search Ads targeting your branded term: How much you can spend is a function of how many people are searching for your brand. Once you reach everyone, spending more can’t get you more people).

If you want to increase your spend, the only place to do so is in the underperforming unbranded campaign. But at least you’ll have a better expectation of the results.

Summary

  • Avoid making budget decisions on aggregate data. Always try to segment and dig a little deeper.
  • Don’t let underperforming segments ride the coat tails of your top performers. Look for 80/20 campaign and ad group performance and analyze those individually.
  • For Google Search Ads, always separate your Branded search terms and Unbranded search terms into separate campaigns.
Categories
Digital Marketing Google Ads Shopify Shopping

Add Microdata to your Shopify Product Pages (with variant support)

If you are using Google Merchant Center to with Shopify then you have likely ran into an issue where Merchant Center will give you a warning that there is insufficient match of micro-data information and that automatic item updates are no longer being performed.

merchant-center-warning
Insufficient micro-data warnings in Google Merchant Center

This is generally due to incorrect or incomplete micro-data on your product page.

Product.liquid

All the required edits should be limited to your Product.liquid file. You need to define a Product itemscope which will have properties such as Product Url, Product Image, Product Title, and Product Description.

Nested within the Product will be an Offer itemscope that will contain the product variant’s price, currency, condition, and availability.

What complicates things is that most Shopify Themes will have at least a partial implementation of micro-data, and are perhaps only missing a few items, or perhaps don’t fully support variants.

A good idea would be to first run your product page through Google’s Structured Data Testing Tool to see which tags already exist.

Product ItemScope

1. Open your product.liquid file and add the Product itemscope property to the outer most div. The first line of your product.liquid should look something like this: 

<div itemscope itemtype="http://schema.org/Product">

2. Directly below this line, you should add the product variant’s url and image markup as so:

<meta itemprop="url" content="{{ shop.url }}{{ product.selected_or_first_available_variant.url }}" />
<meta itemprop="image" content="https:{{ product.selected_or_first_available_variant.image.src | product_img_url: 'grande' }}" />

3. Find where your product’s title and description are displayed, and add itemprop=name and itemprop=description attributes as shown below:

<!-- Product Title -->
<h1 itemprop="name">{{ product.title }}</h1>
<!-- Product description -->
<div class="product-description" itemprop="description">
{{ product.description }}
</div>

Offer ItemScope

4. Now you need to find the place in your product.liquid file where you display your price. Find a wrapping div tag and add the Offers itemscope attributes to the tag. The Offers itemscope MUST nested within the Product itemscope div tag.

<div itemprop="offers" itemscope itemtype="http://schema.org/Offer">

5. Immediately after this line you can add the product variant’s price, currency, condition, and availability microdata as so:

<meta itemprop="priceCurrency" content="{{ shop.currency }}" />
<meta itemprop="price" content="{{ product.selected_or_first_available_variant.price | money_without_currency | remove: ',' }}" />
<meta itemprop="itemCondition" itemtype="http://schema.org/OfferItemCondition" content="http://schema.org/NewCondition"/>
{% if product.selected_or_first_available_variant.available %}
  <link itemprop="availability" href="http://schema.org/InStock" />
{% else %}
  <link itemprop="availability" href="http://schema.org/OutOfStock" />
{% endif %}

Putting it all together

Once done, your product.liquid should have roughly the following structure.

<!-- BEGIN Product itemscope -->
<div itemscope itemtype="http://schema.org/Product">
  <meta itemprop="url" content="{{ shop.url }}{{ product.selected_or_first_available_variant.url }}" />
  <meta itemprop="image" content="https:{{ product.selected_or_first_available_variant.image.src | product_img_url: 'grande' }}" />
 
  <!-- Product Title & Description -->
  <h1 itemprop="name">{{ product.title }}</h1>
  <div class="product-description" itemprop="description">
  {{ product.description }}</div>
  <!-- BEGIN Offer itemscope -->
  <div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
    <meta itemprop="priceCurrency" content="{{ shop.currency }}" />
    <meta itemprop="price" content="{{ product.selected_or_first_available_variant.price | money_without_currency | remove: ',' }}" />
    <meta itemprop="itemCondition" itemtype="http://schema.org/OfferItemCondition" content="http://schema.org/NewCondition"/>
    {% if product.selected_or_first_available_variant.available %}
      <link itemprop="availability" href="http://schema.org/InStock" />
    {% else %}
      <link itemprop="availability" href="http://schema.org/OutOfStock" />
    {% endif %}
  </div>
</div>

Final Steps

  1. Save and test your new product page in Google’s Structured Data Testing Tool, and fix all errors if any.
  2. Wait and check back periodically in Google Merchant center to ensure no new errors were introduced. It will take a few weeks before the “unable to update” warning disappears.

Related Reading

Categories
Attribution Digital Marketing Facebook Ads Google Ads

The Value of View Through Conversions

My personal experience with View Through Conversions (VTCs) is that they provide very little real world lift in sales, in particular when dealing with lower funnel remarketing campaigns. The value increases if you are advertising to a brand-unaware audience, but the value is likely still very small.

But what is the actual value of a View Through Conversion vs a Click Through Conversion? You can theoretically calculate this by running a placebo A/B test of your actual ad vs a Public Service Announcement ad.

Below is such an A/B test that was run on the AdRoll network back in 2013:

Placebo A/B Tests to Measure View Through Conversions

Display Network A/B Test

You will likely need assistance from your Ad Platform or agency to properly run this sort of test, and to properly segregate the A and B groups.

Setup a Placebo A/B Test

  1. Create 2 separate (but equal) non-overlapping campaigns
  2. Campaign A will serve your “normal” ad
    Campaign B will serve a Public Service Announcement ad
  3. Run both campaigns for a few weeks
  4. Calculate VTC “lift” as follows:
Valid VTC Formula

Real Life Example

Campaign A
(Real Ad)
Campaign B
(PSA Ad)
Impressions99,46797,412
VTCs329261
Data from a real world A/B test performed in 2013 on the AdRoll network

VTC “Lift” = (329 – 261) / 329 = 20%

In this real life example, the irrelevant PSA Ads still managed to generate 80% of the View Through Conversion volume that the real ad generated. To be more clear, 261 people saw an ad to adopt a cat, and later went to BOATERexam.com to purchase a boating license.

So 80% of VTCs can be given a value of zero. Of the remaining 20%, more analysis is needed to figure out exactly how they influenced sales. The Real Ad did seem to generate more VTCs than the PSA ad, but the real question is those VTCs result in extra incremental sales or are they simply tracking sales generated by another channel such as e-mail? I don’t have a scientific answer to this, but anecdotally, I think the answer is that the VTCs did NOT result in incremental sales.

NOTE: A few years later we ran a similar Placebo A/B test on Facebook with the help of SocialCode. That test showed ZERO lift from VTCs, and the PSA Ads actually outperformed the real ads from a VTC standpoint!

So how should you value View Through Conversions?

Here are my recommendations:

  • Give View Through Conversions a value of ZERO. Unless you can prove otherwise via an A/B test, assume VTCs are not providing any lift to your campaigns. This is especially true for re-marketing campaigns as the visitors have previously visited your site, and may be actively engaged in checkout when the ad is shown.
  • Run your own Placebo A/B test. If someone insists on using VTCs in performance metrics, then you should insist on running an A/B test to calculate the true value. You should run at least two tests: One for remarketing audiences and another for brand-unaware audiences.
  • Be cautious and skeptical of anyone pushing the value of VTCs especially if they are an ad agency or ad platform that will benefit from including the extra VTCs in their performance metrics.