Categories
Digital Marketing Facebook Ads Shopify

Fix Missing Fields in Shopify’s Facebook Product Feed

Last Update: March 29, 2021

If you use the Facebook Channel on your Shopify store, you will notice that some product attributes are missing in the product catalog:

  • Google Product Category fixed!
  • SEO Description
  • Gender
  • Material
  • Additional Images

Below is the solution to add these missing details. This will give you finer control over your Facebook Product Sets and will let you create richer dynamic ads with multiple product images.

Pre-requisites

Before we start, make sure all of the following are done:

  1. Install the Google Sales Channel
    The script leverages some of the metafields that are created on by the Google Sales Channel
  2. Install the Facebook Sales Channel
  3. Setup Empty Field Rules
    This is not actually a pre-requisite, but it’s a good backup in case things break down with your feed. This sets some default values for fields such as condition and availability.
  1. Go to Facebook Business Manager and then
    Commerce Manager > Catalog > Data Sources
  2. Click on your data feed and go to Settings
  3. Scroll down to Data Feed Rules and click on Add Rules > Set Default Values
  4. Create default values for age_group, gender, availability, condition, material, and any other fields that make sense for your business.

Step 1
Create the Facebook Product Feed Template

To create the Facebook product update feed, we will use a “hack” to transform a standard Shopify Collection page into and XML data feed:

  • In the Shopify admin, go to
    Online Store > Themes > Action > Edit Code
  • Under Templates, choose Add a new Template
  • Choose collection from the drop down and name your template facebook-feed-template

Paste the code below into your newly created template file and click Save.

{% 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 useSEOdescription = true -%}
{%- assign additionalImagesForVariants = false -%}
{%- assign includeOutOfStock = false -%}
{%- assign filterVariantImagesByColor = false -%}
{% comment %}
This template is used to add additional information to the Facebook product catalog
Documentation: https://business.czarto.com/2019/12/11/update-your-shopify-facebook-product-feed-with-missing-attributes/

<comment:title>{{ product.title}} {{variant.title}}</comment:title>
{% endcomment %}
<channel>
<title>{{ shop.name }} {{ collection.title | replace: '&', '&amp;' }}</title>
<link>{{ shop.url }}</link>
<description>{{ collection.description | strip_html }}</description>
{% for product in collection.products %}
{%- assign Gender = product.metafields.mm-google-shopping.gender -%}
{%- assign AgeGroup = product.metafields.mm-google-shopping.age_group -%}
{%- assign Material = product.metafields.mm-google-shopping.material  -%}
{%- assign Color = "" -%}

{%- if product.variants.size > 0 -%}
{%- for variant in product.variants -%}
{%- if includeOutOfStock or variant.available -%}
{%- for option in product.options -%}
{%- if option == 'Color' -%}{% capture Color %}{{ variant.options[forloop.index0] }}{% endcapture %}{%- endif -%}
{%- endfor -%}

{% assign additional_images = product.images %}
{% if filterVariantImagesByColor %}{% assign additional_images = product.images | where: "alt", Color | sort: 'attached_to_variant' | reverse%}{% endif %}

<item>
<g:id>{{ variant.id }}</g:id>
<g:brand>{{ product.vendor }}</g:brand>
{% if useSEOdescription and product.metafields.global.description_tag.size > 0 %}<g:description>{{ product.metafields.global.description_tag | strip_html | strip_newlines | replace: '&', '&amp;' }}</g:description>{% endif %}
<g:product_type>{{ product.type | replace: '&', '&amp;' }}</g:product_type>
<g:mpn>{{ variant.sku }}</g:mpn>
<g:item_group_id>{{ product.id }}</g:item_group_id>
<g:content_id>{{ variant.id }}</g:content_id>
<g:material>{{ Material }}</g:material>
<g:gender>{{ Gender }}</g:gender>
<g:age_group>{{ AgeGroup }}</g:age_group>
{% if additionalImagesForVariants %}
{% 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 %}
{% endif %}
</item>
{%- endif -%}
{% endfor %}
{%- else -%}

<item>
<g:id>{{ product.id }}</g:id>   
<g:brand>{{ product.vendor }}</g:brand>
{% if useSEOdescription and product.metafields.global.description_tag.size > 0 %}<description>{{ product.metafields.global.description_tag | strip_html | strip_newlines | replace: '&', '&amp;' }}</description>{% endif %} 
<g:product_type>{{ product.type | replace: '&', '&amp;' }}</g:product_type>
<g:item_group_id>{{ product.id }}</g:item_group_id>
<g:material>{{ Material }}</g:material>
<g:gender>{{ Gender }}</g:gender>
<g:age_group>{{ AgeGroup }}</g:age_group>
{% if product.images.size > 1 %}{%- for image in product.images offset:1 limit:10 -%}
<g:additional_image_link>https:{{ image.src | product_img_url: 'master' }}</g:additional_image_link>
{% endfor %}{% endif %}
</item>

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

or download from Github

Although the script above will work as is, there are three items that you can configure:

useSEOdescription = true
Setting this to true will use your product’s SEO description. Setting to false will not upload any description (default Shopify feed will be used)

additionalImagesForVariants = false
Setting this to true will upload all your product’s additional images. If you use variants, all your variants will have the identical additional images (their primary image will be as-configured in Shopify)

filterVariantImagesByColor = false
Setting this to true will only upload additional images for a variant IF the ALT text of the images exactly match that variant’s color attribute.

Step 2
Select the Products to Send to Facebook

Now select which products will be included in your feed.

  • In Shopify Admin, go to
    Products > Collections > Create Collection
  • Enter a Title: “Facebook”
  • Add Products to the collection (either manually or using conditions). If you want to include all your products, then add a rule similar to “Inventory Stock is greater than 0”.
  • IMPORTANT!
    Assign the Facebook Feed Template to this collection.
    In the bottom right column of the page, you should see a section called Theme templates. Choose collection.facebook-feed-template otherwise none of this will work.
  • At the bottom of the page, click on Edit Website SEO and enter “facebook” as your collection url handle. (optional – but helps with remembering your feed url)
  • Save and Preview the collection. You should see unformatted text on the screen. This is your Facebook feed. Do a “View Source” in your browser to preview the XML data.
  • Copy the url of this page. It should look similar to https://www.yourstore.com/collections/facebook

Step 3
Upload your Product Feed to Facebook

  • Go to your Facebook Business Manager and go to Commerce Manager
  • Expand Catalogs in the left hand menu and select Data Sources
  • Click on your data source name and then on Settings
  • Scroll to the bottom of the screen and click on Update Schedule and choose Hourly
  • Paste in the feed url generated in Step 2 and click Save
  • Click Request Update Now
  • Wait for Facebook to finish fetching your feed

Done

Your products should now have the missing information added. You will probably want to repeat STEP 3 anytime you modify or add new products to your shop.

It’s just a matter of time before Shopify starts uploading the full data specs to Facebook, but until that time, this is the workaround!

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
Digital Marketing Facebook Ads

Adjusting Facebook Ad Budgets towards a target ROAS Goal

Here is a simple formula to adjusting your daily Facebook Ad budget towards a target ROAS based on past performance.

NumberOfDaysThe time period for which you want to base performance.
AmountSpentHow much did you spend during the time period.
ActualROASThe ROAS performance of your ads during the time period.
TargetROASThe ROAS target you are trying to achieve.

How I use this

  1. I maintain an evergreen campaign structure so that I can easily measure past performance.
  2. My performance target is generally an ROAS of 4.5
  3. My lookback time period is generally 7 days

Once a week (or sometimes daily if we are in a period of high activity or if we have launched a new promotion), first thing in the morning, I look at the past 7 days and run the above formula for each of my campaigns.

  • If my target ROAS is very close to my actual ROAS, then I don’t bother with the calculation, nor with adjusting budgets.
  • If the results call for a decrease, then I decrease the budgets.
  • If the results call for an increase, then I increase budgets, but usually by no more than 10%.

Related Reading

Categories
Attribution Digital Marketing

Attribution Poaching and Remaketing Fraud

Common Deception Tactics by Remarketing Platforms

View Through Conversions

  • A View Through Conversion is when a user sees your ad, DOES NOT CLICK on the ad, and later makes a purchase.
  • A Click Through Conversion is when a user sees your ad, CLICKS on the ad, and later makes a purchase.

For a sales driven remarketing campaign you should exclusively focus on Click-Through Conversions and ignore View-Through Conversions. The incremental sales generated by an ad display in a remarketing campaign is almost zero.

Remember that we are working with a Remarketing campaign. We are targeting customers that are aware of your brand. Customers who have already visited your site. In many cases, customers that have added a product to their cart and are very close to purchase.

Consider this scenario: A long time customer visits your site, quickly finds the product he is looking for and starts the checkout process. In the middle of checkout, he receives an e-mail from a friend. He opens the e-mail and clicks on a link. That link takes him to a news site to read an article. Since he is in your remarketing audience, that news site shows him an ad and records a “View”. The customer finishes reading the article and returns to your site to complete his purchase. Your remarketing campaign has now taken credit for that sale because an ad was displayed to this customer.

If your customer visits any webpage during the purchase process, or if they have another browser tab open, they will likely be exposed to an ad view. That ad view will get View-Through credit for that conversion.

The number of View Through Conversions recorded in a remarketing campaign can be more than ten times that of Click Through Conversions. It is no wonder that remarketing platforms and ad agencies are eager to include View Through Conversions in ROI calculations. If you include View Through Conversions you will see fantastic performance results. This will cause you to double or triple your budget. It’s easy money for them.

Here is what a fictional remarketing campaign with a typical distribution of view vs click conversions might look like:

 Ad SpendClick ConversionsClick SalesView ConversionsView Sales
 $12,00050 $10,000 400 $80,000

If we use Click Conversions to calculate performance, we lose $0.17 for every $1.00 in ad spend. We have a negative ROI. The campaign is not profitable. We need to DECREASE our bids and budget.

 Ad SpendConversionsSalesCPAROASROI
 $12,00050 $10,000 $240 0.83-16%

If we add View-Through Conversions to our calculation, we come to a totally different conclusion: The campaign is now performing fantastically well. We profit $6.50 for every $1.00 in ad spend. That’s a 650% ROI! The campaign is super profitable. We should INCREASE our bids and budgets. This is exactly what your remarketing platform wants you to do, but is exactly the OPPOSITE of what you should do.

 Ad SpendConversionsSalesCPAROASROI
 $12,000450 $90,000 $26.60 7.5650%

The (unfortunate) reality is that the 400 view-through sales above were not a result of your remarketing campaign. The ad display is simply poaching attribution from your other channels.

Note: View Conversion metrics are valuable and useful in many scenarios. However, in the context of a sales driven remarketing campaign they should be excluded.

Cookie Bombing and Fake Impressions

Compounding the problem of View Through Conversions are fraudulent ad impressions and cookie bombing.

Over 50% of Display Ads never actually appear on the user’s screen. They are fraudulently served in a hidden iframe, or are stacked underneath other ads, or are compressed into a 1×1 pixel image, etc… These hidden fraudulent ad impressions still record a “View” in the user’s remarketing cookie. When that user eventually purchases on your site, that invisible fraudulent ad gets credit for the purchase.

If a particular ad network’s website can generate many conversions for advertisers, then those advertisers will want to spend more with that website. Fraudulent sites engage in “cookie bombing” to maximize the number of conversions they can “capture”. They serve as many ads to as many unique users as possible, so that as many users as possible have their cookie. The more users have their cookies, the more conversions will be credited to their sites, and the more ad budget money they will be paid.

To be clear, these sites are not actually serving any visible ads to any users. They are generating fraudulent ad impressions to set cookies on as many users as possible in order to “poach” conversion attribution (and budget) from your other channels. If a user with their cookie ever makes a purchase in the future, that site will get a view-attribution for that purchase, causing more ad budget money to flow to them.

All these hidden impressions distributed to as many users as possible cause View Through Conversion metrics to skyrocket. As mentioned before, View Through Conversions reported by an ad platform can easily be more than 10 times the number of Click Through Conversion reported. It is no surprise that ad platforms want you to believe in and count View Through Conversions.

New Attribution Models: View Throughs Disguised as Click Throughs

The simple way for an ad platform to fool you into counting View Through Conversions is to simply have a single “Conversions” column and lump all conversions in there. The platforms are however getting more sophisticated at transforming view conversions into click conversions, using new attribution models as a guise:

  • YouTube TrueView Conversion is any conversion that occurs after the user has watched 30 seconds of a video. Even if no click happened. TrueView conversions get counted in the Conversion column in AdWords along with the regular Click Through conversions. This makes it seem like an actual click happened, when none did. What makes this even more confusing and misleading, is that YouTube will still report View Conversions in a separate column. Since there are two separate columns, one tracking View Throughs explicitly, you will assume that the Conversions column must only contain clicks. This is not the case, and if not careful, you will significantly overspend on your YouTube remarketing campaign.
  • Steelhouse’s “Verified Visit” model counts any View-Through visit to your site within 1 hour of seeing an ad as a “Verified Visit”. Any conversion that happens within 30 days of this “Verified Visit” is counted as a Click Conversion (even though, no click ever occurred). Steelhouse will skirt the View Through question by saying that they only count conversions from “Verified Visits”. This will mislead you to think that they only count Click Through Conversions. But that is incorrect. The stats they report definitely include View conversions, which inflate your ROI, and cause you to overspend.

I spent months believing that SteelHouse’s numbers were based solely on clicks and post-click conversions. SteelHouse’s conduct made it appear to perform better than it actually was performing. Toms made spending allocation decisions and marketing vendor choices based on SteelHouse’s inflated performance.

Anna Hordov
former Online Demand Generation Manager, Toms

How to protect yourself from View Through Conversion “fraud”?

  • Insist that your platform report on Click Through and View Through Conversions in two separate columns. It is OK to include View Conversions in a report if they are in a separate column. It is NOT OK to combine View Conversions with Click Conversions in the same column.
  • Validate all data with your site analytics. Your analytics platform should report the actual accurate click through conversion data. Always compare the reports your remarketing platform generates with your own analytics data.
  • Don’t run Conversion focused YouTube remarketing campaigns. There is currently no way to opt-out of the TrueView model, therefor your conversion column will be overstated. Read more about TrueView…
  • Don’t use Steelhouse. Their Verified Visit model is designed to fool you. They also use their tracking pixel to inject data into your analytics. Scary stuff. Avoid.
  • Don’t use Criteo. They won’t tell you where your ads are displayed and their network has much more fraudulent sites participating in cookie bombing than anyone else. Avoid.

Fraudulent Automated Clicks

Fake clicks are an evolution of Cookie Bombing, except in this case clicks are auto generated onto the invisible ads, sending invisible visits to your site, and claiming Click credit for your purchases.

How Fake Clicks Work

  1. A legitimate customer visits your site but does not purchase. He is now placed into your remarketing list.
  2. This customer now visits another site on the internet. That site serves up an invisible ad and automatically “clicks” on that ad.
  3. That click causes an invisible visit to your website, by that customer. That customer’s remarketing cookie records a click and a site visit.
  4. Your legitimate customer now has a remarketing cookie set that says he saw an ad, clicked on that ad, and visited your website from that ad.
  5. When that customer comes back to your site to make a purchases, that ad will take credit for the conversion.

There are two issues here: The first is that you’ve paid for a fraudulent click. The second more important issue is that a conversion has been attributed to that fraudulent click. As a result, your remarketing platform appears to be performing better, meaning you will likely increase your budget in order to buy more of those fraudulent clicks.

Obfuscation and Lack of transparency

Anytime the remarketing network is hiding data from you, that’s an opportunity for them to fool you. For example, Criteo refuses to be transparent with the list of publisher sites where you ad appears and where your clicks and conversions come from.

3.6% of Criteo’s ‘users’ generate 25% of its clicks. Such behavior by real human users is highly unlikely. This behavior is indicative of adware, bots, click farms, or other code created by Criteo or its affiliates to generate clicks and drive up Criteo’s click-count numbers.

Steelhouse

How to protect yourself?

  • Insist on detailed and transparent reports on where your ads were served, and where the clicks came from. If certain sites seem to have unusually high click and conversion rates, dig deeper into those sites to see if they are legitimate or not.
  • Avoid using platforms that lack transparency. Don’t use Criteo for this reason, as they refuse to provide transparent reports of where your ads are served, preventing you from doing a proper audit.

Hijacking Visits and Overwriting Analytics

When you install 3rd party javascript pixels on your site, you give the owner of that script a lot of power. In particular that script can overwrite referral data, popup hidden pages, and generate clicks to your site. It can even directly write to your analytics platform!

You may think that your analytics data is safe, but think again. When you install any 3rd party javascript on your site, you also expose your analytics account for injection.

This is super alarming and you should be very concerned about this capability. Any Advertising Platform that has any javascript installed on your site can be overwriting and modifying your analytics data. They can override referral data for direct and organic visits to make it look like they are coming from their network. They can highjack e-commerce transactions and attribute them to themselves. They can make it seem like they are out-performing all your other marketing channels.

SteelHouse used “malicious code to make it appear as though an internet user clicked on a SteelHouse-placed advertisement, even though no such click occurred.”

Leah Bliss
former Global Retargeting Manager, VistaPrint

Steelhouse, under their Verified Visit model, uses their javascript pixel to generate clicks on ads, visits to your site, and to attribute conversions to themselves directly in your analytics.

How this works:

  1. A legitimate customer visits your site but does not purchase. He is now placed into your remarketing list.
  2. This customer now visits another site and is shown an ad. The customer DOES NOT click on the ad, but the remarketing cookie records a View.
  3. An hour later, the customer revisits your site from an organic source. The pixel script on your site detects that the customer has a recent ad view recorded.
  4. The pixel script loads an invisible iFrame, in the iFrame it loads the original ad and generates an automatic click and visit to your site.
  5. Your legitimate customer now has a remarketing cookie set that says he saw an ad, clicked on that ad, and visited your website from that ad. Your analytics data also says that this customer came from an ad click, and not from the original organic source.
  6. When that customer comes back to your site to make a purchases, that ad will take credit for the conversion.

Worst still, when you look in your analytics, it will appear that your Remarketing Platform is sending you lots of visitors and conversions, which will cause you to spend more with them. The reality is that these visitors are organic visits that were hijacked by the remarketing pixel.

“SteelHouse’s practice of inserting a code into an internet user’s browser to make a view of an advertisement appear indistinguishable from a click on Decker’s Adobe Analytics system is not, in any way, common or acceptable industry practice. Nor should it be because it is deceptive.”

Graham McCulloch
Director of eCommerce, Deckers

How to protect yourself?

  • Insist on an image only tracking pixel. Although this is rarely a viable option, try to avoid 3rd party javascript when possible.
  • Keep your analytics private and hidden. The less they know about your analytics platform and setup, the more difficult it will be for them to inject data. Avoid giving them any access, and ideally don’t even tell them what platform you use.
  • Get a written declaration from the Remarketing Platform that they will not inject data into your analytics, that they will not modify referral data, that they will not generate clicks, nor do anything that will simulate visits to your site or in your analytics.
  • Don’t use Steelhouse

More steps to protect yourself

Be very skeptical

Be very skeptical of all data provided by the 3rd party platform. By default assume they are lying to you and are trying to take your money. Always rely on your in-house analytics data first (although even this can be messed with via 3rd party pixels) and always try to deeply understand how their platform and algorithms work. Explicitly ask what they are doing about ad fraud.

Beware of secret sauces, black boxes, and magical artificial intelligence powered predictive self optimizing algorithms. If something looks too good to be true, then it probably is.

Never ask the barber if you need a Haircut…

The ultimate selfish goal of any Advertising Platform is to MAXIMIZE your ad spend. You are THEIR customer and they want YOUR money. They want you to allocate as much of your budget to them as possible. They are incentivized to fool you.

Therefore be very skeptical with the data they give you and the stories they tell you. I would explicitly AVOID using either Steelhouse or Criteo due to first hand bad experiences with both.

More Reading about this topic:

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

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 – 7 day
  • Cart Abandoners – 14 day
  • Cart Abandoners – 30 day
  • Cart Abandoners – 90 day
  • Cart Abandoners – 180 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 – 14 day
  • Product Viewers – 30 day
  • Product Viewers – 90 day
  • Product Viewers – 180 day
  • Product Viewers – 365 day
  • Product Viewers – 520 day

3. Past Buyers

This ad group will show ads to people who have previously purchased. This is a good place to push micro conversions such as joining a loyalty program, joining a community, new product launches or related product up-selling.

Audiences

  • Past Buyers 14 days
  • Past Buyers 30 days
  • Past Buyers 90 days
  • Past Buyers 365 days
  • Past Buyers 520 days

Other Tips

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

Exclude GMail Placements

Gmail “clicks” don’t necessarily result in a visit to your site, and usually only represent the expansion of your ad. This can lead to attribution poaching, in particular if you have a newsletter that you send our regularly.

To exclude gmail placements, go to Placements > Exclusions and exclude mail.google.com

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
Facebook Ads Shopify

DIY Facebook Product Feed for Shopify

WARNING! This code is no longer maintained. Although the code in this post should still work, please use at your own risk. I recommend using the Shopify Facebook Marketing App to sync your product catalog.

Below is a free customizable DIY solution to create a Facebook Product Feed in Shopify.

  1. This is an advanced topic and assumes you have the required understanding of HTML/XML/Liquid, the Shopify Store Admin and Facebook Business Manager.
  2. There are several existing paid apps that allow you to do this without coding (Flexify and DataFeedWatch) and a free app (Facebook Marketing App by Shopify).

1. Install the Google Shopping Channel

Install Shopify’s free Google Shopping app. This will allow you to configure product properties such as Age Group, Gender, and Product Category.

2. Create an XML Collection Template

Create a custom collection template that will output your products as XML instead of HTML.

  • In the Shopify admin, go to Online Store > Themes > Action > Edit Code
  • Under Templates, choose Add a new Template
  • Choose collection from the drop down and name your template fb-product-feed

Paste the code below into your new template and click Save. (Best to copy the code from this link: Shopify Facebook Product Feed Template)

{% 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 -%}
{%- assign PriceAdjustment = 1.0 -%}
{%- assign PriceAdjustmentEffectiveDate =  '20181226T080000-0500/20190102T235900-0800' -%}

<channel>
<title>{{ shop.name }} {{ collection.title | replace: '&', '&' }}</title>
<link>{{ shop.url }}</link>
<description>{{ collection.description | strip_html }}</description>
{%- for product in collection.products -%} 
  {%- assign GoogleProductCategory = product.metafields.mm-google-shopping.google_product_category -%}
  {%- assign Gender = product.metafields.mm-google-shopping.gender -%}
  {%- assign AgeGroup = product.metafields.mm-google-shopping.age_group -%}
  {%- assign Color = "" -%}
  {%- assign Size = "" -%}

  {%- if product.variants.size > 0 -%}
  {%- for variant in product.variants -%}
    {%- 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 -%}

    {% comment %} Calculate Sales vs Base Pricing {% endcomment %} 
    {%- if variant.compare_at_price == blank -%}
      {%- assign BasePrice = variant.price -%}
    {%- else -%}
      {%- assign BasePrice = variant.compare_at_price -%}
    {%- endif -%}
    {%- assign SalePrice = variant.price | times: PriceAdjustment -%}

<item>
<title>{{ product.title | strip_html | strip_newlines | replace: '&', '&' }}{% unless product.title contains Color %} {{ Color | replace: '&', '&' }}{% endunless %}</title>
<link>{{ shop.url }}{{ variant.url }}</link>
<description>{{ product.title | strip_html | strip_newlines | replace: '&', '&' }} {{ variant.title | strip_html | strip_newlines | replace: '&', '&' }} {{ product.description | replace: '</', ' </' | strip_html | strip_newlines | replace: '&', '&' }}</description>
<g:google_product_category>{{ GoogleProductCategory | replace: '&', '&'  }}</g:google_product_category>
<g:item_group_id>{{ product.id }}</g:item_group_id>
<g:id>{{ variant.id }}</g:id>
<g:condition>new</g:condition>
<g:price>{{ BasePrice | money_without_currency }} {{ shop.currency }}</g:price>
{%- if SalePrice < BasePrice -%}<g:sale_price>{{ SalePrice  | money_without_currency }} {{ shop.currency }}</g:sale_price>{%-  endif -%}
{%- if PriceAdjustment < 1 -%}<g:sale_price_effective_date>{{ PriceAdjustmentEffectiveDate }}</g:sale_price_effective_date>{%- 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 | replace: '&', '&' }}</g:product_type>
<g:age_group>{{ AgeGroup }}</g:age_group>
{% unless Color == "" %}<g:color>{{ Color | strip_html | strip_newlines | replace: '&', '&' }}</g:color>{% endunless %}
{% unless Size == "" %}<g:size>{{ Size | strip_html | strip_newlines | replace: '&', '&' }}</g:size><g:size_system>US</g:size_system>{% endunless %}
<g:gender>{{ Gender }}</g:gender>
<g:custom_label_0>{{ product.metafields.mm-google-shopping.custom_label_0 }}</g:custom_label_0>
<g:custom_label_1>{{ product.metafields.mm-google-shopping.custom_label_1 }}</g:custom_label_1>
<g:custom_label_2>{{ product.metafields.mm-google-shopping.custom_label_2 }}</g:custom_label_2>
<g:custom_label_3>{{ product.metafields.mm-google-shopping.custom_label_3 }}</g:custom_label_3>
<g:custom_label_4>{{ product.metafields.mm-google-shopping.custom_label_4 }}</g:custom_label_4>
<g:shipping_weight>{{ variant.weight | weight_with_unit }}</g:shipping_weight>
</item>

  {% endfor %}
  {% else %}

  {% comment %} Calculate Sales vs Base Pricing {% endcomment %} 
  {%- if product.compare_at_price_min == blank -%}
    {%- assign BasePrice = product.price -%}
  {%- else -%}
    {%- assign BasePrice = product.compare_at_price_min -%}
  {%- endif -%}
  {%- assign SalePrice = product.price | times: PriceAdjustment -%}

<item>
<title>{{ product.title | strip_html | strip_newlines | replace: '&', '&' }}</title>
<link>{{ shop.url }}{{ product.url }}</link>
<description>{{ product.title | strip_html | strip_newlines | replace: '&', '&' }} {{ product.description | replace: '</', ' </' | strip_html | strip_newlines | replace: '&', '&' }}</description>
<g:google_product_category>{{ GoogleProductCategory | replace: '&', '&'  }}</g:google_product_category>
<g:item_group_id>{{ product.id }}</g:item_group_id>
<g:id>{{ product.id }}</g:id>
<g:condition>new</g:condition>
<g:price>{{ BasePrice | money_without_currency }} {{ shop.currency }}</g:price>
{%- if SalePrice < BasePrice -%}<g:sale_price>{{ SalePrice  | money_without_currency }} {{ shop.currency }}</g:sale_price>{%-  endif -%}
{%- if PriceAdjustment < 1 -%}<g:sale_price_effective_date>{{ PriceAdjustmentEffectiveDate }}</g:sale_price_effective_date>{%- endif -%}
<g:availability>{% if product.available %}in stock{% else %}out of stock{% endif %}</g:availability>
<g:image_link>http:{{ product.featured_image.src | product_img_url: 'grande' }}</g:image_link>
<g:gtin>{{ product.barcode }}</g:gtin>
<g:brand>{{ product.vendor }}</g:brand>
<g:mpn>{{ product.sku }}</g:mpn>
<g:product_type>{{ product.type }}</g:product_type>
<g:age_group>{{ AgeGroup }}</g:age_group>
<g:gender>{{ Gender }}</g:gender>
<g:custom_label_0>{{ product.metafields.mm-google-shopping.custom_label_0 }}</g:custom_label_0>
<g:custom_label_1>{{ product.metafields.mm-google-shopping.custom_label_1 }}</g:custom_label_1>
<g:custom_label_2>{{ product.metafields.mm-google-shopping.custom_label_2 }}</g:custom_label_2>
<g:custom_label_3>{{ product.metafields.mm-google-shopping.custom_label_3 }}</g:custom_label_3>
<g:custom_label_4>{{ product.metafields.mm-google-shopping.custom_label_4 }}</g:custom_label_4>
<g:shipping_weight>{{ variant.weight | weight_with_unit }}</g:shipping_weight>
</item>
  {% endif %}
{% endfor %}
</channel>
</rss>
{% endpaginate %}

or Download from Github:  Shopify Facebook Product Feed Template

3. Assign products to your Feed

In Step 2 you created your feed template. Now you need to assign products to this feed:

  • In Shopify Admin, go to Products > Collections > Create Collection
  • Enter a Title: “Facebook Product Feed”
  • Add Products to the collection (either manually or using conditions)
  • IMPORTANT! Assign your feed TEMPLATE to this collection.
    In the bottom right column choose collection.fb-product-feed as the Theme Template.
  • Save and Preview the collection. You should see unformatted text on the screen. This is your Facebook feed.
  • Copy the url as you need it in the next step.

4. Upload your Feed to Facebook

  • In Facebook Business Manager go to Assets > Catalogs > Create Catalog.
  • Catalog Type: E-Commerce
  • Click Add ProductsUse Datafeed
  • Enter the feed collection url you copied in step 3 above. Leave the username & password blank. Choose a time for your daily upload to occur (early morning is usually a good time). Choose your currency.
  • Click Start Upload and wait for the feed to be fetched and processed.
  • Fix errors: If there are errors, go back, fix them, re-fetch, and keep doing so until the feed is error free. Sometimes it is necessary to delete and re-create your catalog in Facebook for some changes to appear.
  • If you have more than 1000 product variants, you will need to submit multiple feeds with a ?page=x querystring appended like so: http://mystore.myshopify.com/collections/facebook-product-feed?page=1 (This will send products 1-1000) and http://mystore.myshopify.com/collections/facebook-product-feed?page=2 (This will send products 1001-2000)

5. Prevent the Facebook Feed from Showing on your Store

Depending on how your store is setup, you may need to add some code to prevent your Facebook feed collection from showing up on your store. The exact way to do this may depend on your theme, but generally you will want to have an “unless” statement within the loop that displays your collections:

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

Done!

You are now ready to setup your Dynamic Product Remarketing campaigns!

Related Posts