Product XML feed
You can create an that contains the details of products in your ShopWired account by using the Product feed app.
The product feed has its own URL that you can provide to any third-party service that needs access to your product information (such as a comparison shopping engine).
The product feed feature has access to:
- The product object
- The product review object
- The product questions/answers object
- The product reward points object
It can therefore output all information about a product, including custom fields. Product feeds are created using and therefore support logic code, allowing you to apply conditional statements, loops, and other Twig functionalities to customise the output of your feed.
To create a feed, install the product XML feed app:
- Navigate to Apps
- Locate the Product feed app
- Select
install this app
- Navigate to Feeds > Product feed
- Enter the code to generate the feed into the Feed code setting
Product feed URL and password protection
Product feed URL and password protection
To access the URL for your feed, navigate to Feeds > Feed URLs, locate the Product feed section and copy the URL displayed.
To password protect your feed:
- Navigate to Feeds > Feed URLs
- Locate the Product feed section
- Select the Password protect this feed setting
- Use the username and password shown on the Feed URLs page in the Feed protection section to access the feed
Coding tips
Coding tips
- Consult the documentation for the product object for a list of the variables available
- Remember to close any
{% if %}
statements or{% for %}
cycles with{% endif %}
and{% endfor %}
- Any Twig errors in the code will result in an error generating the feed
- When configuring the code for your feed, if the code has an error the feed will not regenerate and changes will not be reflected
- or an external AI such as ChatGPT for assistance in creating the code
- Use custom fields to modify output and include custom data in your feed
Google Shopping product feed example
Google Shopping product feed example
Here is example code that can be used to generate a product XML feed in Google Shopping's XML format
{% spaceless %}
<?xml version="1.0"?>
<rss xmlns:g="http://base.google.com/ns/1.0" xmlns:c="http://base.google.com/cns/1.0" version="2.0">
<channel>
<title>{{ business.name }}</title>
<link>{{ business.full_url }}</link>
<description>{{ business.description }}</description>
{% for product in products %}
{% if not product.custom_fields.google_feed_exclude %}
{% if product.photos|length > 0 %}
{% set description = product.description|striptags %}
{% if product.variations|length and settings.separate_variations %}
{% for variation in product.variations %}
{% set weight = variation.weight > 0 ? variation.weight : product.weight %}
{% set price_excluding_tax = variation.price_excluding_tax > 0 ? variation.price_excluding_tax : product.price_excluding_tax %}
{% set price_including_tax = variation.price_including_tax > 0 ? variation.price_including_tax : product.price_including_tax %}
{% set sale_price_excluding_tax = variation.sale_price_excluding_tax > 0 ? variation.sale_price_excluding_tax : product.sale_price_excluding_tax %}
{% set sale_price_including_tax = variation.sale_price_including_tax > 0 ? variation.sale_price_including_tax : product.sale_price_including_tax %}
{% set price = settings.include_tax ? price_including_tax : price_excluding_tax %}
{% set sale_price = settings.include_tax ? sale_price_including_tax : sale_price_excluding_tax %}
{% set in_stock = variation.in_stock %}
<item>
<link>{{ variation.url }}</link>
{% set variationTitle = product.title ~ ' - ' ~ variation.values|join(' ') %}
<title>{{ (variationTitle|length > 150 ? (variationTitle|slice(0, 150) ~ '...') : variationTitle)|title }}</title>
<g:is_bundle>{{ product.google.is_bundle ? 'TRUE' : 'FALSE' }}</g:is_bundle>
<g:condition>{{ product.google.condition ? (product.google.condition == 2 ? 'refurbished' : 'used') : 'new' }}</g:condition>
<g:availability>{{ product.archived or ((not in_stock) and (not product.purchasable_when_out_of_stock)) ? 'out of stock' : 'in stock' }}</g:availability>
<g:price>{{ price|number_format(2, '.', '') }} {{ business.currency }}</g:price>
<g:id>{{ variation.sku ?: variation.id }}</g:id>
{% if not settings.no_shipping %}
{% if product.delivery_cost > 0 %}
<g:shipping>
<g:country>GB</g:country>
<g:service>Delivery</g:service>
<g:price>{{ (settings.include_tax ? product.delivery_cost_including_tax : product.delivery_cost_excluding_tax)|number_format(2, '.', '') }}</g:price>
</g:shipping>
{% elseif product.free_delivery %}
<g:shipping>
<g:country>GB</g:country>
<g:service>Free Delivery</g:service>
<g:price>0</g:price>
</g:shipping>
{% else %}
{% set shipping_rate = business.best_shipping_rate(sale_price > 0 ? sale_price : price, weight) %}
{% if shipping_rate %}
<g:shipping>
<g:country>GB</g:country>
<g:service>{{ shipping_rate.title }}</g:service>
<g:price>{{ (settings.include_tax ? shipping_rate.price_including_tax : shipping_rate.price_excluding_tax)|number_format(2, '.', '') }} {{ business.currency }}</g:price>
</g:shipping>
{% endif %}
{% endif %}
{% endif %}
{% if sale_price > 0 %}<g:sale_price>{{ sale_price|number_format(2, '.', '') }} {{ business.currency }}</g:sale_price>{% endif %}
{% if product.sale and product.sale.to > 0 %}
<g:sale_price_effective_date>{{ product.sale.from|date('c') }}/{{ product.sale.to|date('c') }}</g:sale_price_effective_date>
{% endif %}
{% if product.description %}
<description>{{ description|length > 5000 ? (description|slice(0, 5000) ~ '...') : description }}</description>
{% endif %}
{% for photo in product.photos %}
{% set photo_url = photo.url('', true) %}
{% if loop.index0 == 0 %}
<g:image_link>{{ photo_url }}</g:image_link>
{% else %}
<g:additional_image_link>{{ photo_url }}</g:additional_image_link>
{% endif %}
{% endfor %}
{% if product.brand_title|length > 0 %}
<g:brand>{{ product.brand_title|length > 70 ? (product.brand_title|slice(0, 70) ~ '...') : product.brand_title }}</g:brand>
{% endif %}
{% if product.google.no_identifier %}
<g:identifier_exists>FALSE</g:identifier_exists>
{% else %}
{% set gtin = variation.gtin ? variation.gtin : product.gtin %}
<g:identifier_exists>{{ gtin|length > 0 or product.mpn|length > 0 ? 'TRUE' : 'FALSE' }}</g:identifier_exists>
{% if gtin|length > 0 %}
<g:gtin>{{ gtin|length > 50 ? (gtin|slice(0, 50) ~ '...') : gtin }}</g:gtin>
{% endif %}
{% set mpn = variation.mpn ? variation.mpn : product.mpn %}
{% if mpn|length > 0 %}
<g:mpn>{{ mpn|length > 70 ? (mpn|slice(0, 70) ~ '...') : mpn }}</g:mpn>
{% endif %}
{% endif %}
{% if weight > 0 %}
<g:shipping_weight>{{ weight }} {{ business.weight_unit }}</g:shipping_weight>
{% endif %}
{% if product.google.category_id > 0 %}
<g:google_product_category>{{ product.google.category_id }}</g:google_product_category>
{% endif %}
{% for name, value in product.google.attributes %}
{% if name == 'color' %}
<g:color>{{ product.variations|length ? variation.google_color|default(value) : value }}</g:color>
{% elseif name == 'size' %}
<g:size>{{ product.variations|length ? variation.google_size|default(value) : value }}</g:size>
{% else %}
{{ ('<g:' ~ name ~ '>')|raw }}{{ value }}{{ ('</g:' ~ name ~ '>')|raw }}
{% endif %}
{% endfor %}
<g:item_group_id>{{ product.sku ?: product.id }}</g:item_group_id>
{#% for breadcrumbs in product.breadcrumbs|slice(0, 3) %}
<g:product_type>{% for breadcrumb in breadcrumbs %}{{ loop.first ? '' : ' > ' }}{{ breadcrumb.name }}{% endfor %}</g:product_type>
{% endfor %#}
{% if product.custom_fields.custom_label_0 %}
<g:custom_label_0>{{ product.custom_fields.custom_label_0 }}</g:custom_label_0>
{% endif %}
{% if product.custom_fields.custom_label_1 %}
<g:custom_label_1>{{ product.custom_fields.custom_label_1 }}</g:custom_label_1>
{% endif %}
{% if product.custom_fields.custom_label_2 %}
<g:custom_label_2>{{ product.custom_fields.custom_label_2 }}</g:custom_label_2>
{% endif %}
{% if product.custom_fields.custom_label_3 %}
<g:custom_label_3>{{ product.custom_fields.custom_label_3 }}</g:custom_label_3>
{% endif %}
{% if product.custom_fields.custom_label_4 %}
<g:custom_label_4>{{ product.custom_fields.custom_label_4 }}</g:custom_label_4>
{% endif %}
</item>
{% endfor %}
{% else %}
{% set in_stock = product.in_stock %}
{% set price = settings.include_tax ? price_including_tax : price_excluding_tax %}
{% set sale_price = settings.include_tax ? sale_price_including_tax : sale_price_excluding_tax %}
<item>
<link>{{ business.full_url }}{{ product.slug }}</link>
<title>{{ (product.title|length > 150 ? (product.title|slice(0, 150) ~ '...') : product.title)|title }}</title>
<g:is_bundle>{{ product.google.is_bundle ? 'TRUE' : 'FALSE' }}</g:is_bundle>
<g:condition>{{ product.google.condition ? (product.google.condition == 2 ? 'refurbished' : 'used') : 'new' }}</g:condition>
<g:availability>{{ product.archived or ((not in_stock) and (not product.purchasable_when_out_of_stock)) ? 'out of stock' : 'in stock' }}</g:availability>
<g:price>{{ price|number_format(2, '.', '') }} {{ business.currency }}</g:price>
<g:id>{{ product.sku ?: product.id }}</g:id>
{% if not settings.no_shipping %}
{% if product.delivery_cost > 0 %}
<g:shipping>
<g:country>GB</g:country>
<g:service>Delivery</g:service>
<g:price>{{ (settings.include_tax ? product.delivery_cost_including_tax : product.delivery_cost_excluding_tax)|number_format(2, '.', '') }}</g:price>
</g:shipping>
{% elseif product.free_delivery %}
<g:shipping>
<g:country>GB</g:country>
<g:service>Free Delivery</g:service>
<g:price>0</g:price>
</g:shipping>
{% else %}
{% set shipping_rate = business.best_shipping_rate(sale_price > 0 ? sale_price : price, weight) %}
{% if shipping_rate %}
<g:shipping>
<g:country>GB</g:country>
<g:service>{{ shipping_rate.title }}</g:service>
<g:price>{{ (settings.include_tax ? shipping_rate.price_including_tax : shipping_rate.price_excluding_tax)|number_format(2, '.', '') }} {{ business.currency }}</g:price>
</g:shipping>
{% endif %}
{% endif %}
{% endif %}
{% if sale_price > 0 %}
<g:sale_price>{{ sale_price|number_format(2, '.', '') }} {{ business.currency }}</g:sale_price>
{% endif %}
{% if product.sale and product.sale.to > 0 %}
<g:sale_price_effective_date>{{ product.sale.from|date('c') }}/{{ product.sale.to|date('c') }}</g:sale_price_effective_date>
{% endif %}
{% if product.description %}
<description>{{ description|length > 5000 ? (description|slice(0, 5000) ~ '...') : description }}</description>
{% endif %}
{% for photo in product.photos %}
{% set photo_url = photo.url('', true) %}
{% if loop.index0 == 0 %}
<g:image_link>{{ photo_url }}</g:image_link>
{% else %}
<g:additional_image_link>{{ photo_url }}</g:additional_image_link>
{% endif %}
{% endfor %}
{% if product.brand_title|length > 0 %}
<g:brand>{{ product.brand_title|length > 70 ? (product.brand_title|slice(0, 70) ~ '...') : product.brand_title }}</g:brand>
{% endif %}
{% if product.google.no_identifier %}
<g:identifier_exists>FALSE</g:identifier_exists>
{% else %}
<g:identifier_exists>{{ product.gtin|length > 0 or product.mpn|length > 0 ? 'TRUE' : 'FALSE' }}</g:identifier_exists>
{% if product.gtin|length > 0 %}
<g:gtin>{{ product.gtin|length > 50 ? (product.gtin|slice(0, 50) ~ '...') : product.gtin }}</g:gtin>
{% endif %}
{% if product.mpn|length > 0 %}
<g:mpn>{{ product.mpn|length > 70 ? (product.mpn|slice(0, 70) ~ '...') : product.mpn }}</g:mpn>
{% endif %}
{% endif %}
{% if product.weight > 0 %}
<g:shipping_weight>{{ product.weight }} {{ business.weight_unit }}</g:shipping_weight>
{% endif %}
{% if product.google.category_id > 0 %}
<g:google_product_category>{{ product.google.category_id }}</g:google_product_category>
{% endif %}
{% for name, value in product.google.attributes %}
{% if name == 'color' %}
<g:color>{{ value }}</g:color>
{% elseif name == 'size' %}
<g:size>{{ value }}</g:size>
{% else %}
{{ ('<g:' ~ name ~ '>')|raw }}{{ value }}{{ ('</g:' ~ name ~ '>')|raw }}
{% endif %}
{% endfor %}
{#% for breadcrumbs in product.breadcrumbs|slice(0, 3) %}
<g:product_type>{% for breadcrumb in breadcrumbs %}{{ loop.first ? '' : ' > ' }}{{ breadcrumb.name }}{% endfor %}</g:product_type>
{% endfor %#}
{% if product.custom_fields.custom_label_0 %}
<g:custom_label_0>{{ product.custom_fields.custom_label_0 }}</g:custom_label_0>
{% endif %}
{% if product.custom_fields.custom_label_1 %}
<g:custom_label_1>{{ product.custom_fields.custom_label_1 }}</g:custom_label_1>
{% endif %}
{% if product.custom_fields.custom_label_2 %}
<g:custom_label_2>{{ product.custom_fields.custom_label_2 }}</g:custom_label_2>
{% endif %}
{% if product.custom_fields.custom_label_3 %}
<g:custom_label_3>{{ product.custom_fields.custom_label_3 }}</g:custom_label_3>
{% endif %}
{% if product.custom_fields.custom_label_4 %}
<g:custom_label_4>{{ product.custom_fields.custom_label_4 }}</g:custom_label_4>
{% endif %}
</item>
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
</channel>
</rss>
{% endspaceless %}
Google Shopping Local Inventory Feed example
Google Shopping Local Inventory Feed example
Here is example code that can be used to generate a product XML feed in Google Shopping's Local Inventory Feed XML format
{% spaceless %}
<?xml version="1.0"?>
<rss xmlns:g="http://base.google.com/ns/1.0" xmlns:c="http://base.google.com/cns/1.0" version="2.0">
<channel>
{% for product in products if not product.archived %}
{% if product.variations|length %}
{% for variation in product.variations %}
<item>
<g:store_code>[[ store ID]]</g:store_code>
<g:id>{{ variation.sku ?: variation.id }}</g:id>
<g:availability>{{ product.in_stock ? 'in stock' : 'out of stock' }}</g:availability>
<g:price>{{ product.price }}</g:price>
{% if product.sale_price %}
<g:sale_price>{{ product.sale_price }}</g:sale_price>
{% endif %}
<g:quantity>{{ product.stock }}</g:quantity>
</item>
{% endfor %}
{% else %}
<item>
<g:store_code>[[ store ID ]]</g:store_code>
<g:id>{{ product.sku ?: product.id }}</g:id>
<g:availability>{{ product.in_stock ? 'in stock' : 'out of stock' }}</g:availability>
<g:price>{{ product.price }}</g:price>
{% if product.sale_price %}
<g:sale_price>{{ product.sale_price }}</g:sale_price>
{% endif %}
<g:quantity>{{ product.stock }}</g:quantity>
</item>
{% endif %}
{% endfor %}
</channel>
</rss>
{% endspaceless %}
Google Shopping product reviews and ratings example
Google Shopping product reviews and ratings example
Here is example code that can be used to generate a product XML feed in Google Shopping's product reviews and ratings format
{% spaceless %}
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.google.com/shopping/reviews/schema/product/2.3/product_reviews.xsd">
<version>2.3</version>
<publisher>
<name>{{ business.name }}</name>
<favicon>{{ business.full_url }}/favicon.ico</favicon>
</publisher>
<reviews>
{% for product in products if not product.archived %}
{% if product.reviews|length %}
{% for review in product.reviews %}
<review>
<review_id>{{ product.id }}_{{ loop.index }}</review_id>
<reviewer>
<name>{{ review.name }}</name>
</reviewer>
<review_timestamp>{{ review.date|date("Y-m-d\\TH:i:s\\Z") }}</review_timestamp>
<content>{{ review.content }}</content>
<ratings>
<overall min="1" max="5">{{ review.rating }}</overall>
</ratings>
<review_url type="group">{{ product.url }}</review_url>
<products>
<product>
<product_ids>
{% if product.gtin %}
<gtins>
<gtin>{{ product.gtin }}</gtin>
</gtins>
{% endif %}
{% if product.mpn %}
<mpns>
<mpn>{{ product.mpn }}</mpn>
</mpns>
{% endif %}
{% if product.sku %}
<skus>
<sku>{{ product.sku }}</sku>
</skus>
{% endif %}
{% if product.brand %}
<brands>
<brand>{{ product.brand.name }}</brand>
</brands>
{% endif %}
</product_ids>
<product_name>{{ product.title }}</product_name>
<product_url>{{ product.url }}</product_url>
</product>
</products>
</review>
{% endfor %}
{% endif %}
{% endfor %}
</reviews>
</feed>
{% endspaceless %}