Feed Import
Feed import is a recommended primary path to a complete catalog: it covers every product in your feed, with zero shopper-browser cost.
Feed import lets you point A vs B at a URL where your store already publishes a product feed, then choose how often to fetch it. A vs B polls the URL on your chosen schedule, parses any products that changed, and updates the Live Catalog, no manual uploads, no code changes each time your catalog changes.
Supported formats
| Format | Status |
|---|---|
| Google Merchant Center XML | Supported |
| CSV / TSV | Supported |
Both formats use the same field meanings: they are just different file layouts. The Google Merchant Center XML format (RSS 2.0 with the g: namespace) is the same file you already send to Google Shopping. CSV (or tab-separated TSV) is the right pick if your store or agency works in spreadsheets and exports a column-based file instead.
Because the two formats share the exact same mapping rules, the same products produce the same catalog entries whether you feed us XML or CSV.
Configuring a feed
On the Product catalog page, open Connect your store, then the Product feed card, then + Add product feed. You will be asked for:
| Setting | Notes |
|---|---|
| Feed URL | Must be HTTPS. An http:// address is refused with "Feed URL must start with https://". A vs B fetches this URL on your chosen cadence. The URL must be publicly accessible or accessible from Cloudflare's network. |
| Format | Select Google Merchant Center XML or CSV / TSV to match your file. |
| Poll cadence | How often A vs B should check for changes: Hourly, Every 6 hours, or Daily. |
| Default currency | Optional 3-letter code (for example USD). Leave it blank to use your project's default currency. |
Select Register feed to save. This does not fetch the feed straight away: the source starts out Never synced and picks up on the next hourly sweep (see below). For an immediate first import, open the source's row in Catalog source health and select Sync now.
- Paste your feed's HTTPS URL here.
- Pick the format that matches your file.
- Choose how often A vs B should check the feed for changes.
Poll cadence
A vs B runs a feed sweep every hour. On each sweep, it checks which feed rows are due based on their configured cadence:
- Hourly: fetched on every sweep
- Every 6 hours: fetched when at least 6 hours have passed since the last successful fetch
- Daily: fetched when at least 24 hours have passed
Choose a cadence that matches how often your inventory or pricing actually changes. Hourly is appropriate for stores with frequent price changes or stock fluctuations. Daily is usually fine for stores with stable catalogs.
How changes are detected
A vs B does not parse and compare every product on every poll. Before doing any work, it computes a hash of the feed body and compares it to the hash from the previous fetch. If the feed has not changed, the sweep skips parsing and moves on: your catalog is already up to date.
When the hash differs, A vs B parses the full feed and upserts any products through the freshest-wins merge. Only the fields present in the feed are written; fields the feed does not include are left as they are from any higher-priority source.
Google Merchant Center XML field mapping
A vs B maps the standard g: namespace fields to the Live Catalog:
| Feed field | Catalog field | Notes |
|---|---|---|
g:id | sku | The stable product identifier. For grouped items, g:item_group_id becomes the parent SKU. |
title | title | Standard RSS <title> element. |
description | description | Standard RSS <description> element. |
link | href | Product page URL. |
g:image_link | image | Main product image. |
g:brand | brand | Brand or manufacturer. |
g:product_type | category | Product type string. |
g:google_product_category | categories | Added as the first entry in the categories list. |
g:price | price or compareAtPrice | When g:sale_price is also present, g:price becomes the compare-at (was) price. |
g:sale_price | price | Wins over g:price as the live selling price when present. |
g:availability | availability | in_stock, out_of_stock, preorder. backorder maps to preorder. |
g:custom_label_0 through g:custom_label_4 | customFields | Stored as custom_label_0 etc. in the product's custom fields. |
Price strings use the format "19.99 USD" (amount, space, ISO currency code). A vs B reads the currency code from the string and converts to minor units using the correct exponent for that currency.
Variant grouping
Items sharing the same g:item_group_id are collapsed into a single parent product with variants:
- The parent
skuis theg:item_group_id. - Each item in the group becomes a variant with
variantSku= its owng:id. - Variant-level options (
g:color,g:size,g:material) are captured as the variant's option map. - The parent's price is the minimum variant price across the group.
- If any variant is
in_stock, the parent isin_stock.
Standalone items (no g:item_group_id) are treated as individual products.
CSV / TSV field mapping
A CSV feed is a plain table: the first row is the column headers, and every row after that is one product (or one variant). A vs B accepts the same column names Google uses for its CSV product feed, so any file that works for Google Shopping works here too.
| Column | Catalog field | Notes |
|---|---|---|
id | sku | Required. For grouped items, item group id becomes the parent SKU. |
item group id | parent sku | Optional. Rows sharing this value collapse into one product with variants. |
title | title | |
description | description | |
link | href | Product page URL. |
image link | image | Main product image. |
brand | brand | |
product type | category | |
google product category | categories | Added as the first category entry. |
price | price or compareAtPrice | When sale price is also present, this becomes the compare-at (was) price. |
sale price | price | Wins as the live selling price when present. |
availability | availability | in_stock, out_of_stock, preorder. backorder maps to preorder. |
color, size, material | variant options | |
custom label 0 … custom label 4 | customFields |
A few things to know:
- Comma or tab. A vs B detects automatically whether your file uses commas (CSV) or tabs (TSV): both work.
- Header names are forgiving. Capitalisation, spaces vs underscores, and an optional
g:prefix are all accepted, soImage Link,image_link, andg:image_linkall mean the same thing. - Quoted values. A value containing a comma, quote, or line break must be wrapped in double quotes, with any inner quotes doubled (
"a ""quoted"" word"): the standard spreadsheet CSV rules. - Prices use the same
"19.99 USD"format as the XML feed (amount, space, ISO currency code). - Rows without an
idare skipped. A file with noidcolumn at all is rejected with a clear error.
Matching your own column names
A vs B already reads through capitalisation, spaces-vs-underscores, and an optional g: prefix, so Image Link, image_link, and g:image_link all mean the same thing to it.
For a column whose name means something different, like Product Name or Cost, tell A vs B what it is with Column mapping on the add-feed form:
- Set the format to CSV / TSV. The mapping section appears.
- Type your column header on the left, exactly as it appears in your file.
- Pick the A vs B field it means on the right.
- Add a row for each column you need to translate, then register the feed.
You only need a row for headers that do not already use a standard name. Everything else is read automatically.
A few things to know:
- Editing a feed keeps its mapping. Re-opening the form on a registered feed shows the mapping that is live, so changing the poll cadence never quietly drops it.
- Two rows naming the same column: the last one wins, and the form tells you when that happens.
- XML feeds have nothing to map. Google Merchant Center XML uses Google's own field names, so the mapping section is hidden for that format, and a mapping is not saved with an XML feed.
Feed source health
The Product catalog page's Catalog source health table shows a row for each configured feed:
- Last synced: when the last successful parse ran
- Products seen: how many product records were in the most recent feed
- Status: Never synced (registered but no sweep has run yet), OK, Syncing…, or Error
- The status badge: Never synced, OK, Syncing…, or Error.
- How many products the last successful poll saw.
An Error status means the last fetch or parse failed. The error message appears in the row. Common causes:
- The feed URL returned a non-200 response (check that the URL is correct and publicly accessible)
- The XML or CSV was malformed
- The selected format doesn't match the file (for example, the format is set to CSV but the file is actually XML)
- A CSV file has no
idcolumn (map your product-id column toidunder Column mapping, or rename it in your export)
A vs B continues retrying on the configured cadence regardless of errors.
Priority in the freshest-wins merge
Feed writes are tagged source feed. In the merge order:
live_event > shopify > push_api ≈ feed > uploadA feed write always wins over an uploaded dataset baseline. A Shopify webhook update always wins over a feed write, no matter which one is newer. A feed write and a Push API write are equally trusted: whichever one lands most recently wins. For price and stock specifically, the most recent write wins regardless of source, including a real-time browser price observation, so a fresh on-page price can outrank even a Shopify update for that one field.
If a product is missing from a complete, successful poll of your feed, A vs B removes it from the Live Catalog, as long as your feed is still the source that owns that product (a higher-priority source like Shopify keeps its own products even if your feed stops listing them). A vs B never removes anything on a product's first-ever poll, since there is nothing yet to compare against, and it refuses to remove more than half your catalog in a single poll: a feed that suddenly returns almost nothing is more likely broken than genuinely emptied, so A vs B flags it as an error instead of deleting in bulk.