Product variants in feeds
Product variants in feeds: group_id, variant_dict and listing_has_variations
How OpenAI's feed spec represents products that come in sizes and colours, with a worked example and the mistakes that break variant rows.
Checked against the sources on 7 October 20266 min read
A linen apron in three colours is one product to you and three things to a feed. Each colour has its own stock, maybe its own price, and its own picture. The spec handles this by giving each one a row and tying the rows together. Three fields do that.
The three variant fields
group_idthe ID shared by every variant of one product. It must be different from any item_id.listing_has_variationstrue on every variant row.variant_dictan object that says which option this row is, such as {"color":"Sage","size":"M"}.
The spec says all three are required if a product has variants. Each variant row also keeps its own item_id, price, availability, url and image_url.
A worked example
Here are two colours of one apron. The data is made up.
{"item_id":"APRON-SAGE","group_id":"APRON","listing_has_variations":true,"variant_dict":{"Colour":"Sage"},"title":"Linen apron, Sage","url":"https://example-shop.com/products/linen-apron?variant=1","image_url":"https://example-shop.com/img/apron-sage.jpg","availability":"in_stock","price":"34.00 GBP","brand":"Example Shop","seller_name":"Example Shop Ltd","description":"A hard-wearing linen apron."}
{"item_id":"APRON-CLAY","group_id":"APRON","listing_has_variations":true,"variant_dict":{"Colour":"Clay"},"title":"Linen apron, Clay","url":"https://example-shop.com/products/linen-apron?variant=2","image_url":"https://example-shop.com/img/apron-clay.jpg","availability":"out_of_stock","price":"34.00 GBP","brand":"Example Shop","seller_name":"Example Shop Ltd","description":"A hard-wearing linen apron."}Both rows carry group_id APRON, which is not the same as either item_id. Each has its own URL and image, and the clay one is out of stock while the sage one is not. That difference is the reason variants are separate rows.
How shops usually store variants
- Shopify: a product with several variants, each with up to three options. SKUHelm uses the product ID as group_id and the variant ID as item_id, and builds variant_dict from your option names. A product with only one variant called Default Title is treated as a plain product.
- WooCommerce: a variable product with variations. The list API does not give variation prices, so each variation is fetched on its own. The group is the parent's SKU or ID, and variant_dict comes from the variation's attributes.
- Spreadsheets: usually one row per variant with a shared handle or parent column. You map that column to group ID and map the option name and value columns.
Mistakes that break variant rows
- group_id equal to an item_id. The spec says they have to be different, so give the group a distinct ID.
- listing_has_variations missing on some rows. It has to be true on every variant row, not just the first.
- An empty variant_dict. If you say a row is a variant, say which option it is.
- The same URL for every variant. Each row keeps its own url. Link to the page that opens on that variant where your platform supports it.
- One image for every colour. The spec expects colour to match the image. If the apron is clay, show clay.
- A single-variant product marked as having variations. If there is only one version, leave the variant fields out.
- Variant titles that are all identical. Put the option in the title so a buyer can tell rows apart.
Do I need all the combinations?
Include the ones you actually sell. If you offer a mug in three sizes and four colours but only make seven of the twelve, send seven rows. A row for a combination that does not exist invites an order you cannot fill.
For ones you sell but have run out of, send the row with out_of_stock rather than dropping it. That keeps the item ID stable, so the product comes back when you restock.
How big can this get?
Variants multiply fast. A hundred products with six variants each is 600 rows. Free SKUHelm accounts publish 25 items, and that counts rows, not products. Pro allows 10,000.
A checking routine for variants
- Pick a product with several variants and find all its rows.
- Check they share one group_id and that it matches none of their item_ids.
- Check listing_has_variations is true on every row.
- Read each variant_dict. Could a buyer tell the rows apart from it alone?
- Open each url. Does it land on the right variant?
- Compare each price and availability with the shop.
Doing this for three or four products usually shows whether the pattern is right across the catalogue, because the same code or the same export produces every row.
Check how your variants will look as feed rows before you publish.
Check my variantsQuestions
Is group_id the same as the parent product ID?
It is the shared ID for the group of variants. Many shops use the parent product's ID, as long as it differs from every item_id in the feed.
Do variants count as separate items?
Yes. Each variant is a row with its own item_id, so each counts towards your plan's item limit.
What goes in variant_dict if I only have one option?
A single pair, such as {"Size":"M"}. The spec's example has two, but any number of options that identify the row is fine.
Can price differ between variants?
Yes. Price, availability, url and image can all differ per row.