Glossary
Offer ID (item ID): how Google Ads spend is tied to a Shopify product
An offer ID, the id attribute of a product in Google Merchant Center, is the unique, stable identifier Google Ads reports a product's cost and performance against. Google Ads calls it the item ID, and the Shopify-synced IDs Opteno reads take the form shopify_<country>_<product id>_<variant id>, so each one names the Shopify product and variant the advert was for. That makes it the join key between ad spend and profit: if the ID cannot be read, or names a product that no longer exists, the spend cannot be tied to a product and is reported as unattributed.
Last checked · More glossary
What an offer ID is
The offer ID is the merchant's own name for a product in Merchant Center, and Google uses it to look up the product and track its performance. Google's help page for the attribute says to use it "to uniquely identify each product", and that the ID you choose, together with target country and language, "is used to track the product's performance history in Merchant Center and Google Ads".
The same value goes by several names. Merchant Center calls the attribute id; the Merchant API calls the field offerId, "Your unique identifier for the product"; Google Ads calls it the item ID, returned by its API as segments.product_item_id. They all name the same identifier, though Google Ads may report it in lower case.
- Unique: one ID for each different product.
- Stable: "Once you've assigned an ID to a product, don't change it."
- Never recycled: "Don't reuse or recycle the same ID for different products."
- 1–50 characters; ASCII letters, digits, underscores and dashes recommended.
- Case-sensitive, but casing must not be what makes two IDs unique.
Why it matters for profit
The offer ID is what links the two sides of the profit calculation: Google Ads reports cost against it, and a Shopify-synced offer ID contains the product and variant IDs that Shopify records each sale against. Google Ads reports Shopping and Performance Max cost per item ID: its shopping_performance_view "Provides Shopping campaign and Performance Max campaign statistics aggregated at several product dimension levels". Shopify records which variant sold and at what price; cost of goods comes from that variant's cost-per-item, or a store-wide percentage if you set one. A Shopify-synced offer ID contains that variant's ID, so ad spend can be joined to the variant's sales directly.
Once they are joined, profit per product is ordinary arithmetic. ROAS is revenue divided by ad spend. POAS is revenue after refunds and cost of goods, divided by the same spend, and its break-even is 1.00× for every product.
Worked example (illustrative figures)
A variant has two offer IDs, one in the UK feed and one in the German feed. Over 30 days Google reports £60.00 of spend on the UK ID and £40.00 on the German ID, so the variant cost £100.00 to advertise. Shopify shows £400.00 of sales of that variant, no refunds and £160.00 cost of goods. ROAS = 400 ÷ 100 = 4.00×. POAS = (400 − 160) ÷ 100 = 2.40×. Join on the UK ID alone and spend falls to £60.00, so the same variant appears to return 6.67× ROAS and 4.00× POAS — better than the truth, because £40.00 went missing.
The Shopify-synced format Opteno parses
Opteno reads offer IDs of the form shopify_<country>_<product id>_<variant id>, split on underscores into exactly four parts. Letter case is ignored, so shopify_US_… and shopify_us_… are the same ID.
Step 1: shopify
A fixed prefix. An ID that does not start with it is not read as a Shopify offer.
Step 2: <country>
Two letters, usually the country of the feed the offer is published to, such as uk or de. Google's help also shows shopify_ZZ_, where the code names no country.
Step 3: <product id>
Digits only: Shopify's product ID, the number after /products/ in the product's admin page address.
Step 4: <variant id>
Digits only: the ID of the particular variant — the size or colour — the offer advertises.
One variant can carry several offer IDs, one per feed country. The example IDs shopify_uk_8000000000001_45000000000011 and shopify_de_8000000000001_45000000000011 are the same variant advertised to two countries, and Opteno files the spend on both against that one variant. Assuming one offer per variant silently drops spend, as the worked example above shows.
When an ID cannot be parsed or matched
Spend on an offer ID that cannot be tied to a product is reported as unattributed, on its own line, never spread across the catalogue to make the totals tidy. Attributed and unattributed spend reconcile to what the ad account spent; if Google reports more product spend than a campaign's total, the excess is counted, not netted away.
A guess has a victim: spread unknown spend across the catalogue and every product looks slightly worse than it is, and one that tips below break-even can be excluded for someone else's spend.
- An ID that does not parse: no shopify_ prefix, not exactly four parts, a country part that is not two letters, or a product or variant part that is not all digits.
- An ID that parses but whose product is not in the Shopify catalogue — for example, a product that has since been deleted.
- Campaign spend that no product row accounts for, in Shopping, Performance Max or any other campaign type.
Worked example (illustrative figures)
The same account also spent £10.00 on an item ID of LS-BLUE-M, a SKU rather than a Shopify-synced ID. Attributed spend is £100.00, unattributed is £10.00, and the account total is £110.00. The variant's POAS stays at 2.40×; the £10.00 is shown beside it, not hidden inside it.
Country codes: uk in feed IDs, GB on orders
The United Kingdom must be treated as one country, but it arrives spelt two ways. Shopify-synced feed IDs can spell it uk (shopify_uk_…), while Shopify orders use the ISO 3166-1 code GB. Stored as written, the ad spend lands in one country and the sales it produced in another, and a ROAS computed from either half means nothing.
Opteno folds UK into GB wherever a country enters — the offer ID, the ad row, the order and the dashboard's country filter — so a saved filter asking for UK still finds the data. It is a short list of documented exceptions, not a fuzzy matcher: EL, the EU's code for Greece, becomes GR; other two-letter codes pass through unchanged; anything else is treated as unknown rather than attached to the wrong country.
The country part is a feed label, not a promise of a real country. Google's own help gives shopify_ZZ_ as an example of a raw Shopify product ID. Opteno passes such a code through as it is, so spend on a shopify_ZZ_ ID is not attached to any real country.
Worked example (illustrative figures)
Of the variant's £400.00 in sales, £250.00 came from UK orders. Without folding, a country breakdown shows "UK" with £60.00 of spend and no sales (ROAS 0.00×) and "GB" with £250.00 of sales and no spend (ROAS not computable). Folded, it shows one row, GB: 250 ÷ 60 = 4.17× ROAS.
Pitfalls that break the join
Changing an ID resets its history
Google says: "If you change the ID, you'll overwrite your product and its history." Switching feed apps or ID schemes can change every ID at once; if it does, compare profit on either side of the switch date, not across it.
Stale feeds name deleted variants
Rebuilding a product's options in Shopify gives its variants new IDs, while Google can go on reporting spend against the old ones. Opteno keeps that spend on the product, because the product half of the ID still matches. Where Google's product title for the offer ends with the name of exactly one current variant, it links the offer to that variant. Where every variant was replaced and the offer and variant counts match one for one, it pairs old and new in order and records that the link was made by position. Otherwise the variant is left blank. If the product itself was deleted, the spend is unattributed.
IDs that are not Shopify-synced
Google's own help recommends the product SKU as the ID ("We recommend that you use your product SKU for this value"), and many feeds follow that advice. A SKU carries no Shopify product or variant ID, so Opteno cannot attribute that spend to a product and reports it as unattributed.
The advertised variant is not always the one bought
The offer ID ties spend to the variant whose advert was clicked. A shopper who clicks the medium and buys the large leaves the spend on one variant and the sale on another. Read a variant inside its product before excluding it.
Questions about offer IDs
Where do I find a product's offer ID?
In Merchant Center it is the product's id attribute. In Google Ads it is the item ID, and the Google Ads API returns it as segments.product_item_id. In the Shopify-synced IDs Opteno reads (shopify_<country>_<product id>_<variant id>), the last two numbers are the Shopify product ID and variant ID; the product ID is also the number after /products/ in the product's Shopify admin address.
Does Performance Max report spend by item ID?
Yes. The Google Ads API's shopping_performance_view provides Shopping and Performance Max statistics aggregated by product dimensions, including the item ID. Spend in a Performance Max campaign that no product row accounts for is not lost: Opteno reports it as unattributed, so the account still reconciles.
What happens if an offer ID changes?
Google's help page says that if you change the ID, "you'll overwrite your product and its history". Google's Merchant Center help also notes that Shopify products in overlapping markets, now separated into dedicated country data sources, are treated as new items, which resets their performance history, even though the raw product IDs stay the same.
My feed uses SKUs as IDs. Can Opteno attribute that spend?
Not to a product. Opteno reads the Shopify product and variant from IDs of the form shopify_<country>_<product id>_<variant id>; a SKU carries neither, so that spend is reported as unattributed rather than guessed. The account total still reconciles, but those products' rows will not show the spend.
Why do UK and GB appear as two countries in some reports?
Shopify-synced feed IDs can spell the United Kingdom uk, while Shopify orders use the ISO code GB. Stored as written, spend and sales land in two rows and neither row's ROAS means anything. Opteno folds UK into GB wherever a country enters, so both land in one row.
See which products are losing you money
Connect Shopify and Google Ads in under five minutes. From $14.99 a month, cancel whenever you want, and no change to your store or your campaigns without your approval.