Shopify metafields to product feed attributes: the mapping that actually works
June 19, 2026
Piping Shopify metafields into material, color, size, age_group, and GTIN feed fields cleanly, including the traps that break the mapping.
Shopify’s built-in product model runs out of fields fast. It has a title, a description, a couple of option axes, a barcode, and not much else that maps to what Google and Meta want. There is no native home for material, pattern, age_group, gender, or a reliable gtin. Metafields are where that structured data lives, and piping them into feed attributes cleanly is one of the highest-leverage things you can do for feed quality. It is also where a lot of mappings quietly go wrong.
Why metafields, and where they live
A metafield is a typed, namespaced piece of extra data attached to a product or a variant. You define, say, custom.material and fill it with cotton, and now every product carries a structured material value instead of you trying to scrape it out of the description. The two things to keep straight from the start are level and type. Level is whether the metafield sits on the product or on the individual variant. Type is what kind of value it holds, single-line text, a list, a number, a reference to a metaobject. Both decide how you have to read it in the transform.
The level trap: product vs variant
Feed attributes are per-variant, because a feed row is a variant. So an attribute that legitimately differs by variant, size, color, gtin, has to come from variant-level data, while an attribute that is the same across the whole product, brand, material on a single-fabric item, can come from a product-level metafield and be copied down to every row. Put a per-variant fact in a product-level metafield and you lose the variation; try to read a product-level metafield off a variant and it is simply not there. Decide the level to match how the attribute actually varies.
The type trap: text, lists, and metaobjects
A metafield’s type changes the shape of what you read. A single-line text metafield gives you a string. A list.single_line_textgives you an array, and if you drop that straight into a feed field you get a stringified list where you wanted one value, or a joined blob where the channel wanted the first item. A metaobject reference gives you a handle you then have to resolve to the actual value. The mapping has to know each source metafield’s type and read it accordingly, rather than assuming everything is a plain string.
Normalize to the channel’s accepted values
This is the step people skip, and it is where good source data still produces disapprovals. Several feed attributes only accept values from a fixed list, and your metafield’s free text has to be normalized to match:
- age_group accepts a closed set:
newborn,infant,toddler,kids,adult. A metafield that saysChildrenorbabyhas to be mapped onto one of those. - gender accepts
male,female,unisex.Men,Mens, andMall need to resolve tomale. - size / size_system / size_type want consistent values, and Google reads size best when the size system is declared rather than guessed.
- color should be a real color word, not a marketing name;
Midnight Dreamneeds to becomeBlue(you can keep the fancy name too, primary color first).
The normalization is a lookup from your values to the channel’s accepted values, maintained in one place. Without it, a perfectly populated metafield still yields an invalid attribute.
GTIN from a metafield instead of barcode
The barcode field is the usual GTIN source, but if your store already misuses it for internal codes, a dedicated GTIN metafield is cleaner. Either way the rule from the identifier side still applies: validate the value (length, numeric, check digit) before sending it as gtin, and fall back to identifier_exists=no when there genuinely is no identifier. A metafield does not make a bad GTIN good; it just gives you a tidier place to keep the good one.
The mapping that holds up
The version that survives a growing catalog is a declared mapping, not code scattered through an export script: a table that says this metafield, at this level, of this type, maps to this feed attribute, normalized through this lookup. New attribute? Add a row. Channel changes its accepted values? Edit the lookup. When the mapping is data rather than buried logic, you can see the whole thing at a glance and reason about why a field came out the way it did. That is the difference between a mapping you trust and one you re-debug every time Merchant Center complains.
The plug
SnowPipe reads product- and variant-level Shopify metafields, handles the list and metaobject types, normalizes values to each channel’s accepted set, and validates identifiers on the way through, all from a mapping you can see rather than code you have to trace. It is at pipe.snowforge.dev, with a free tier for smaller stores.