Google Shopping feed vs OpenAI product feed: what transfers, what breaks

Most Google Shopping columns map to the OpenAI feed; seller_name, eligibility flags, target_countries and pre_order do not. Mapping table and fixes.

Published 11 min read

Most of a Google Shopping feed transfers to the OpenAI product feed: identifiers, titles, descriptions, prices, GTINs, brand and condition all have a direct counterpart. What breaks is the part Google never asked for: a seller_name on every row, lowercase string booleans for search, ads and checkout eligibility, target_countries, and the pre_order spelling. This guide maps the fields one by one.

Two specifications, one catalog

Google Merchant Center and OpenAI both want a flat list of products with a stable identifier, a price in a currency, an image and a landing page. The vocabulary differs. Google calls the identifier id and the landing page link; OpenAI calls them item_id and url. Google groups variants with item_group_id; OpenAI uses group_id. The values are usually the same data, so a mapping layer handles most of the work.

The differences that matter are not renames. They are fields OpenAI requires that have no source in a Google feed, values OpenAI spells differently, and defaults that behave differently when a column is missing. The rest of this article works through each of those.

OpenAI also accepts a second format that keeps the Google column names. The specification lists it separately and adds one condition: “Use this format only after OpenAI confirms it for your registered feed.” So there are really three paths: map your Google feed to the native OpenAI format, register a Google-compatible feed, or generate both from one catalog. The last option is what Convrail does, and the reasons become clear once the mapping is on the table.

Field-by-field mapping

The table below pairs each common Google attribute with its OpenAI counterpart. “Transfers” means the value can be copied as is. “Rename” means only the column name changes. “Convert” means the value itself needs work.

Google attributeOpenAI fieldStatusNotes
iditem_idRenameOpenAI: “Stable ID, unique per item or variant within your feed. Never reuse it for a different item.” Google caps id at 50 characters; OpenAI still accepts id and sku as legacy aliases.
item_group_idgroup_idRenameBoth are a shared parent identifier, distinct from each item identifier. OpenAI accepts item_group_id as an alias.
titletitleTransfersBoth allow 150 characters and want the variant named in the title.
descriptiondescriptionTransfers, check contentBoth allow 5,000 characters. OpenAI wants plain text; HTML left by a store editor should be stripped.
linkurlRenameBoth require an absolute HTTP or HTTPS URL. OpenAI: “Product detail page for the item, with the variant selected when possible.”
image_linkimage_urlRename, check formatGoogle accepts JPEG, WebP, PNG, GIF, BMP and TIFF. OpenAI wants a direct JPEG or PNG link; a .webp image that Google accepts will not do.
availabilityavailabilityConvertGoogle: in_stock, out_of_stock, preorder, backorder. OpenAI native: in_stock, out_of_stock, pre_order, backorder, unknown. The underscore in pre_order is the trap.
availability_dateavailability_dateTransfersISO 8601 on both sides.
pricepriceTransfersGoogle’s example is 15.00 USD; OpenAI’s is 79.99 USD. Same shape: decimal amount, space, uppercase ISO 4217 code.
sale_pricesale_priceTransfers, check valueOpenAI: “greater than zero, strictly less than price, and in the same currency.” A sale price equal to the price passes Google and fails OpenAI.
sale_price_effective_date(none in native format)ConvertOpenAI native wants the current price: “Submit the current price; update the feed when a sale starts or ends.” The Google-compatible format keeps the start/end pair.
gtingtinTransfers, check digitsOpenAI: “Exactly 8, 12, 13, or 14 digits, including a valid check digit.” Google allows up to 50 numeric characters, so a value Google tolerates can fail here.
mpnmpnTransfersBoth preserve punctuation and casing.
brandbrandTransfersBoth cap at 70 characters. OpenAI adds: “Use a real brand and seller name, not placeholders.”
conditionconditionTransfersSame three values: new, refurbished, used.
product_typeproduct_categoryRenameGoogle’s Home > Women > Dresses > Maxi Dresses is exactly the shape OpenAI wants: “Your category path, from broad to specific, separated by >.”
google_product_category(none)Drop or foldOpenAI has no field for Google’s taxonomy ID. Keep it in the Google projection only.
shippingshipping_price, shippingConvertOpenAI: “Nonnegative amount; zero means no charge. Omitted or empty means unknown, not free shipping.” A Google feed that relies on account-level shipping settings sends nothing here.
(none)seller_nameAddRequired by OpenAI on every row. Google identifies the seller through the Merchant Center account, not the feed.
(none)is_eligible_searchAdd or omit“Omitted or empty: true.” Set false to pull a product out of results.
(none)is_ads_eligibleAdd“Set true for products Ads should process. Omitted or empty: disabled unless a feed-level Ads default applies.”
(none)is_eligible_checkoutAdd, with policy URLs“Omitted, empty, or false: disabled. Search set to false overrides this flag.” When true, seller_privacy_policy and seller_tos become required.
(none)target_countriesAddUppercase ISO 3166-1 alpha-2 codes. “Omitted or empty does not mean worldwide.”
(none)variant_dictAdd for variants“Map option names to the selected values.” Google has separate color, size, material columns; OpenAI accepts those too and adds the dictionary.

Three rows in this table deserve a closer look, because they are where a working Google feed turns into silent rejections.

The availability spelling

The OpenAI specification is explicit about the Google-compatible format: availability “must be in_stock, out_of_stock, preorder, or backorder. Use preorder, not the OpenAI spelling pre_order; unknown is not accepted.” Read it twice. In the native OpenAI format you write pre_order; in the Google-compatible format you write preorder. Whichever format you registered, the other spelling is an invalid value for that row. And unknown, which the native format allows, is not valid in the Google-compatible one.

Booleans that Google never had

A Google Shopping feed has almost no boolean columns, so the exporters that build it have no convention for them. OpenAI does: “For boolean fields, use JSON true or false, or the lowercase strings true and false in delimited files.” A CSV cell containing TRUE, 1 or yes is a malformed value, and the file upload overview lists “Malformed field values” among the common ingestion failures.

Defaults that point the wrong way

Two OpenAI defaults are the opposite of what a merchant would guess. An omitted shipping_price means unknown, not free. An omitted target_countries does not mean worldwide. And an omitted is_ads_eligible means the product is not processed for ads unless a feed-level default exists. A Google feed carries none of these columns, so a straight re-upload lands in the least favorable interpretation of each.

OpenAI’s Google-compatible ingestion mode

The specification documents a format that keeps Google’s column names so a merchant can reuse an existing export. Its required columns are id, title, description, link, image_link, availability, price and brand. Variants use item_group_id, described as a “Shared parent ID, different from each id. Enables grouping.” availability_date is “Required for preorder and backorder. Use YYYY-MM-DD or ISO 8601 timestamp.” sale_price_effective_date keeps Google’s shape: “start/end with ISO 8601 dates or timestamps.”

Two things this mode does not remove. First, the seller identity and eligibility settings still have to exist somewhere; the format has no seller_name column, so they live in the feed registration on the OpenAI side rather than in the file. Second, the gate: “Use this format only after OpenAI confirms it for your registered feed.” A Google-format file dropped on an SFTP endpoint registered for the native format is a file full of outdated field names, which the file upload overview also lists as a common failure (“Outdated or non-spec field names”).

The practical consequence: the Google-compatible mode is a registration decision, not a file decision. Pick it deliberately, or map to the native format.

What breaks when you just re-upload a Google feed

Assume a merchant exports their Google Shopping CSV, renames nothing, and pushes it to an SFTP endpoint registered for the native OpenAI format. Here is what the specification says will happen, row by row.

Symptom in the Google exportEffect under the native OpenAI format
Columns named id, link, image_link, item_group_idid and item_group_id are accepted as legacy aliases. link and image_link are not listed as aliases, so url and image_url are missing: two required fields absent, row rejected.
No seller_name columnRequired field missing on every row. The whole file is rejected, not a few lines.
availability = preorderNot one of the five native values. Row rejected.
image_link pointing to a .webp fileOpenAI wants a direct JPEG or PNG. Row rejected.
sale_price equal to price during a promotion that endedFails “strictly less than price”. Row rejected.
gtin with a wrong check digit, tolerated by GoogleFails the check digit rule. Row rejected.
Description with <br> and <strong> tagsNot plain text. Row at risk.
No is_ads_eligible columnProducts are not processed for ads unless a feed-level default applies. The rows are accepted; the ads campaign has nothing to show.
No target_countries“Omitted or empty does not mean worldwide.” Rows accepted, visibility in your markets not guaranteed.
Empty shipping because shipping lives in Merchant Center settingsInterpreted as unknown, not free.

The uncomfortable part is what you see afterwards: nothing. The file upload overview describes how snapshots are processed and how long a record persists, but the specification documents no per-row error report for rejected rows. Products missing from a snapshot are retained “for up to 14 days”, then disappear. A merchant who re-uploads a Google feed therefore has two weeks of apparent normality before products that were never accepted stop existing in ChatGPT, and no message in between. The sibling article on products not showing in ChatGPT shopping covers the diagnosis path.

How to keep one catalog for both channels

The safe pattern is neither “re-upload Google” nor “maintain two feeds by hand”. It is one catalog with two projections, each validated against its own specification.

The native OpenAI projection

Convrail syncs your Shopify or WooCommerce catalog by webhook, debounced for 30 seconds so a bulk edit triggers one rebuild. Each product becomes a native OpenAI row: HTML is stripped from descriptions, the price is formatted as amount CURRENCY in the store currency, variants are grouped under a group_id distinct from item_id, and seller_name, target_countries and the eligibility flags come from your feed settings rather than from a column you have to invent. A GTIN with a wrong check digit is omitted rather than sent, so the row stays valid instead of being dropped for a typo in a barcode.

Every row is then validated: the nine required fields, the format of each conditional field present, placeholder values such as n/a or unknown where they are not allowed, sale_price against price, is_eligible_checkout against its policy URLs. Rejected rows never reach the file. They are written to the run journal with the item, the field and the rule, so the check the specification does not give you happens on your side before delivery.

The Google-compatible projection

The same catalog is also kept as a Google-compatible projection: id, link, image_link, item_group_id, preorder instead of pre_order, no unknown availability, product_type where OpenAI has product_category. Nothing is edited twice. A price change in your store updates both projections on the next rebuild.

Delivery that matches the overview

The native feed is exported as parquet, jsonl.gz, csv.gz or tsv.gz, sharded at 500,000 items or about 450 MB (under the overview’s “Up to 500k items per shard” and “under ~500MB” guidance), with stable names such as feed-organic-000.jsonl.gz so each daily SFTP delivery overwrites the previous snapshot as the overview asks: “Keep the same file name on every update and overwrite it with the latest snapshot instead of creating a new name each run.” Failed deliveries retry, then open a health alert. If you are choosing between the export formats, the comparison in parquet vs jsonl vs csv for the OpenAI feed goes into the trade-offs.

Common mistakes

Trusting Google’s acceptance as proof of validity. Google is more permissive on GTIN length, image formats and sale prices. A row Google shows for months can fail three OpenAI rules at once.

Copying item_id into group_id. OpenAI wants a “Stable parent-listing ID shared by all variants” that differs from each item identifier. Exporters that fill the group column with the product ID when a product has a single variant produce rows where the two are equal.

Writing TRUE in a boolean column. Spreadsheet software capitalizes booleans on save. OpenAI wants the lowercase string. Check the file after the last tool that touched it, not before.

Assuming an empty column means “default to the sensible thing”. For shipping_price, target_countries and is_ads_eligible, the OpenAI default is the restrictive one.

Adding utm_medium=feed to the Google feed but not the OpenAI one. The best practices page suggests feed attribution parameters on url “when you need feed-specific click tracking”. If you do it, do it in both projections, and read the attribution guide for how those clicks are then matched to orders.

Starting with the whole catalog. The overview says to “Start with a small sample (around 100 items).” Fix the mapping on 100 rows, then scale.

What to do next

Connect your store to Convrail’s product feed and read the first run journal: it lists every Google-derived row that the OpenAI specification would reject, with the field and the fix, before anything is delivered. The OpenAI product feed specification explained covers each field in more depth.

Sources

Frequently asked questions

Can I upload my Google Shopping feed to OpenAI as is?

Only in the Google-compatible mode, and only after OpenAI confirms that mode for your registered feed. The specification lists eight required columns for it (id, title, description, link, image_link, availability, price, brand) and still needs a seller name and eligibility settings configured on the OpenAI side.

Is pre_order or preorder the right availability value?

It depends on the format you registered. The OpenAI native format uses pre_order; the Google-compatible format uses preorder and does not accept unknown. Mixing them gets the row rejected.

Does OpenAI have an equivalent of google_product_category?

Not a mandatory taxonomy. OpenAI uses an optional product_category field that takes your own path from broad to specific, separated by a greater-than sign, which is closer to Google's product_type than to the Google taxonomy.

Do I need two catalogs to serve Google Shopping and ChatGPT?

No. One catalog can be projected into both formats. Convrail validates the OpenAI feed against the specification and keeps a Google-compatible projection of the same rows, so you maintain one source.

What is the fastest way to find out why a Google-derived row is rejected by OpenAI?

The OpenAI documentation describes no per-row error report, so check on your side before delivery. Convrail's run journal lists each rejected item with the field and the rule it broke.

Measure the ChatGPT channel this week

Free during early access while we onboard the first stores. Leave your email and we send the install link when your spot opens.

No newsletter. One email with the install link, nothing else.