Shopify + Facebook Product Feed: How to build your own Product Catalog

If you want to setup Facebook Dynamic Product Remarketing ads for your Shopify store, you will need to upload your store’s product catalog to Facebook. Below is a walk through of how to generate your Facebook product feed.

This is an advanced topic and so I will assume you have a basic understanding of HTML/Liquid, the Shopify Store Admin and Facebook Business Manager.

Step 1: Install the Shopify Google Shopping App

Installing this free app will allow you to configure various properties on your products such as Age Group, Gender, and Product Category.

Step 2: Create a Custom Collection Template for your Feed

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

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

In your Shopify admin, go to:

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

Paste the following code into your new template and click Save. (This code is also available on Github here: Shopify Facebook Product Feed Template)

{% layout none %}<?xml version="1.0"?>
<rss xmlns:g="" version="2.0">
{% paginate collection.products by 1000 %}
{% assign CountryCode = 'US' %}
{% if shop.currency == 'CAD' %}{% assign CountryCode = 'CA' %}{% endif %}
{% assign Color = "" %}
{% assign Size = "" %}
<title>{{ }} {{ collection.title | replace: '&', '&amp;' }}</title>
	<link>{{ shop.url }}</link>
<description>{{ collection.description | strip_html }}</description>
{% for product in collection.products %}
{% assign GoogleProductCategory = %}
{% assign Gender = %}
{% assign AgeGroup = %}

{% comment %}Output the top level product and then all variants{% endcomment%}
<title>{{ product.title | strip_html | strip_newlines | replace: '&', '&amp;' }}</title>
	<link>{{ shop.url }}{{ product.url }}</link>
<description>{{ product.title | strip_html | strip_newlines | replace: '&', '&amp;' }} {{ product.description | replace: '</', ' </' | strip_html | strip_newlines | replace: '&', '&amp;' }}</description>
<g:google_product_category>{{ GoogleProductCategory | replace: '&', '&amp;' }}</g:google_product_category>
<g:item_group_id>shopify_{{ CountryCode }}_{{ }}</g:item_group_id>
<g:id>{{ }}</g:id>
<g:price>{{ product.price | money_without_currency }} {{ shop.currency }}</g:price>
<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>

{% 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 %}
<title>{{ product.title | strip_html | strip_newlines | replace: '&', '&amp;' }}</title>
	<link>{{ shop.url }}{{ variant.url }}</link>
<description>{{ product.title | strip_html | strip_newlines | replace: '&', '&amp;' }} {{ variant.title | strip_html | strip_newlines | replace: '&', '&amp;' }} {{ product.description | replace: '</', ' </' | strip_html | strip_newlines | replace: '&', '&amp;' }}</description>
<g:google_product_category>{{ GoogleProductCategory | replace: '&', '&amp;' }}</g:google_product_category>
<g:item_group_id>shopify_{{ CountryCode }}_{{ }}</g:item_group_id>
<g:id>{{ }}</g:id>
<g:price>{{ variant.price | money_without_currency }} {{ shop.currency }}</g:price>
<g:availability>{% if variant.available %}in stock{% else %}out of stock{% endif %}</g:availability>
<g:image_link>http:{{ variant.image.src | product_img_url: 'grande' }}</g:image_link>
<g:gtin>{{ variant.barcode }}</g:gtin>
<g:brand>{{ product.vendor }}</g:brand>
<g:mpn>{{ variant.sku }}</g:mpn>
<g:product_type>{{ product.type }}</g:product_type>
<g:age_group>{{ AgeGroup }}</g:age_group>
{% unless Color == "" %}<g:color>{{ Color }}</g:color>{% endunless %}
{% unless Size == "" %}<g:size>{{ Size | remove: 'Mens ' | remove: 'Ladies ' | remove: 'Child ' | remove: 'Infant ' | replace: '&', '&amp;' }}</g:size><g:size_system>US</g:size_system>{% endunless %}
<g:gender>{{ Gender }}</g:gender>

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

or Download from Github:  Shopify Facebook Product Feed Template

Step 3: Assign products to your Facebook Feed Collection.

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

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

  • Go to Products > Collections
  • Click the Add Collection button
  • Enter a name for your new collection. I recommend Facebook Product Feed.
  • Add products you want published as part of this collection feed. (either manually or via dynamic rules)
  • IMPORTANT: Select your custom collection template: collection.fb-product-feed.liquid at the bottom of the right hand column.
  • Click Save.

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

Make sure you copy the url of this collection, as you will need to submit this url to Facebook Business Manager.

Step 4: Add your Product Feed to Facebook Business Manager

Make sure you have your Facebook Collection url open from step 3 above.

  • Log into Facebook Business Manager
  • Go to Assets > Product Catalogs > Product Feeds and click the +Add Product Feed button to create a new feed.
  • Enter a descriptive name for your feed. eg: Shopify Facebook Feed and your store’s currency.
  • Choose Schedule Recurring Uploads
  • Enter your feed collection url in the Feed URL field, and leave the username & password fields blank. Choose a time for your daily upload to occur (early morning is usually a good time)
  • Click Create Feed and wait for the feed to be fetched and processed.
  • Fix errors: If there are errors, go back and fix any errors (either in your product data because of missing meta fields, or you may need to edit the template for custom functionality), re-fetch, and keep doing so until the file is error free.

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

Step 5: Exclude your Facebook Feed Collection from Showing up on your Store Pages

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

{% unless collection.title contains "facebook" %}

... your collection code ...

{% endunless %}


You are now ready to setup Dynamic Product Remarketing on Facebook. (But that will be for another post)

Related Posts




  1. Holy ****ing ****!!! You finally got this to work for me! I’ve been trying to get a shopify collection feed to spit out a product feed readable by Facebook for about 3 weeks. Your solution has lead me to believe that we may have stripped essential code from our standard collection template file. The one caveat I’ve found is that you have to make the collection visible on online store in order for it the link to be readable by Facebook’s Product Feed input. This means people could hit that collection page and see a giant block of text. Maybe we can find a work around but again, THANK YOU kind sir. You are a saint.



  2. I get an error in FB product catalogue when adding the feed, FB says” “Invalid character detected at line 1347 column 15. Please make sure that the XML element is well formed.” Double checked everyhitng



    1. Hi Simon,

      There is likely an invalid (for XML) character in one of your product’s title or descriptions. eg: “&” is invalid in XML so needs to be encoded as “&amp;”. The best way to trouble shoot this is to open up your feed in your browser and view-source. Then go to the error line (1357, column 15 in your case) and see what character appears there. The proper solution is to probably wrap the TITLE and DESCRIPTION in the feeds with <![CDATA[ … ]]>. I will add this to the todo list.




      1. It turned out to be a product type that contained & so i just updated the template to {{ product.type | replace: ‘&’, ‘&’ }} and all good for now.


      2. Hi Alex,

        Any updates on this? I also have the same issue regarding an invalid character on line 5504 which turns out to be “Accessories & Misc” which I’ assuming is the & sign.

        Is there a workaround?




      3. Figured out the solution to the issue with invalid characters by adding ” | replace: ‘&’, ‘&’ }} ” to the rest!
        Encountered something else strange though, added page 2 as a separate feed but it says

        ‘This product already exists in the catalog:
        Another product with the same id was already uploaded to this catalog in Shopify Facebook Feed 0 – 500.”

        Shouldn’t ?page=1 load products 0-500 and ?page=2 load products 501-1000? If so how come there’s a product that exists in both feeds and how do I remove it from one of them to avoid the error?

        Thanks 😀


  3. Great Post Alex!
    I get this error when I try to fetch the data:
    Unexpected Content At the End of the Document (1 product affected)
    No content can be inserted after the end of document element on line 2 column 1.



  4. Hi Alex, I am wanting the product descriptions for my products on Google Shopping and my Facebook Feed to be different from those on my Shopify store (basically optimised for these channels). Most of the apps I have come across ( the free Google Shopping Shopify app for instance) just pull the product descriptions directly from your Shopify store and don’t give you the option to edit/change them. Can you recommend a solution that would allow me to edit the product descriptions so they differ from those on my Shopify store?



    1. Hi Scott,

      One option is to use the Shopify SEO Title and SEO Description for your feed’s description. But if you’re already using this for your actual SEO description, then that’s not an option.

      The other option would be to use a custom Shopify MetaField to store a third description for each product. There are various metafield editor apps available, as well as a Chrome extension that will allow you to edit them easily. That’s probably your best bet.

      Hope that helps.



  5. Hi Alex,

    Thanks for getting back to me. I really appreciate it. This stuff is definitely testing my technical know how! I had been looking at various product feed management services as a solution the downside being that they are all quite expensive and could possibly be overkill?

    Following on from your reply I also had a look and found a variety of apps (both free and paid) in the Shopify app store that seems to offer pretty powerful options when it comes to adding metafields. For instance…

    So with the metafields option am I correct in thinking that I could create additional metafields in order to create an alternate title and alternate product description and somehow configure it so that those fields did not display on my actual Shopify store but were used by the product feed instead of the standard title and product feed. I am not sure that the standard Google Shopping Shopify app that most stores use allows for this type of functionality?

    Would really appreciate your advice on this one.



    1. Hi Scott,

      A tool I would recommend trying first is the ShopifyFD Chrome Plugin:

      By default, MetaFields DO NOT show up on your store unless you explicitly want them to. So you would create 2 new metafields (You need to specify a “namespace” for them — like a top level category — I recommend you use the “google” namespace to keep things consistent with how the Google Shopping app work). Assuming you call the metafields google.title and google.description, you would just update the script code to use and (instead of product.title and product.description).

      A bigger question for you to ask yourself: Is the extra effort and overhead worth the extra sales you are expecting to generate? Going this route means you will theoretically have to manage 3 sets of Titles and Descriptions (The public description, the SEO description, and then your custom Shopping descriptions). I’m not sure what your assumptions are about increased sales due to these optimizations, but I would crunch some numbers first to see if your sales volume supports the overhead of optimizing the descriptions. (Eg: Assume a 10% increase in sales — what does that come to over the period of a year).

      Additionally, in my experience, product description does very little to influence Google Shopping sales (Titles **DO** have a big influence, but not descriptions): — and so I would consider just using the SEO description for this. (..and focus heavily on negative keywords in your PLA campaigns).

      Hope some of this helps!



    2. Hi Scott,

      I just realized that we are talking about the Facebook shopping feed and not the Google shopping feed. My previous comment was related to Google Shopping and not facebook (although all the metafields comments are still correct).

      RE: Facebook – I assume you want to display the description in the product ads, and so need to make it shorter, etc… That makes sense. But I would again make the calculation of how much conversion lift you are expecting from a slightly better description, and if that’s worth the additional short & long term administrative overhead.



  6. Hey Alex, thanks so much for this tutorial. Works great. I have one quick question: any way to only index the product without variants (or single variant)? The feed is treating each size of T-Shirt as an additional variant and thus there are multiple of the same photo in my product feed. I’m not sure if this will pose an issue, but I’m curious if when people are navigating my site then go to Facebook if they’ll see a remarketing carousel with the same product in all image placements (hope this makes sense). The best solution I can think of is to only index the product w/o variants. This will also reduce the size of the feed, which would be beneficial. Thanks!



    1. Hi Adam,

      Sorry for the late reply. If you delete lines 37-65 (for variant in product.variants…) then you will only upload the main product. However, you **may** miss out of dynamic remarketing, as Shopify matches based on the product’s variant. (But give it a shot?)





  7. Hi Alex,
    first of all, thanks so much for this tutorial. Since 3 months i have used your method to menage dynamic facebook campaign and about 2 days ago the feed stopped to work correctly, returning this error: “impossible analyze row 2 column 1”. Any idea about what cloud have caused this problem ?




Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s