Introduction
When you search for a product on Google, you see that "Shopping" panel with photos and prices. To get your own products in there, you have to hand your product data to Google Merchant Center (GMC). That bundle of product data is the product feed.
Most stores hand feed generation off to a dedicated app. But for the apparel-and-gear EC site I worked on, we deliberately skipped the app and generated our own XML feed from the headless EC side instead.
This article explains why we chose self-hosting, and the overall picture of the automation that keeps thousands of SKUs fresh every day.
Why generate the feed ourselves
We wanted to move past the app's limits
Feed generation apps are convenient. But the flip side of that convenience is that you can't step outside the box the app gives you. Requests like "change how the title is built for just these products" or "exclude anything with a certain tag" often can't be done from the app's settings screen.
With self-hosting, we write the conversion logic from product data to feed ourselves. Being able to map things freely to fit the business was the biggest motivation.
Supporting multi-site operations
In this project, we ran several EC sites off the same product foundation. Each site handles slightly different products, languages, and currencies.
With an app, contracts and settings pile up per site. Self-hosting lets us emit per-site feeds from one codebase — just specify the site on the endpoint and its feed is generated. That single point of management made operations far easier.
It works for both ads and free listings
People assume the product feed only exists for Shopping "ads," but the same feed also powers Google's free listings — the slots where products appear in search results without spending a cent.
In other words, a well-maintained feed multiplies your exposure without ad spend. Once you see the feed as the foundation of acquisition rather than an accessory to advertising, the value of building it properly yourself becomes clear.
The overall self-hosted system
A single endpoint returns the feed
The mechanism is simple: hit one endpoint, /api/merchant/feed.xml, and it returns XML that GMC can read. You just register this URL in GMC. From there, GMC periodically fetches the URL and pulls in the latest product data.
Fetch products, inventory, and prices via the Admin GraphQL API
/api/merchant/feed.xml converts to the GMC spec
Holds the generated result with a few-hour TTL
Periodically fetches the URL → reflects into Shopping ads and free listings
Pulling product data from Shopify
The source of truth for inventory and price is Shopify. The feed generation API calls Shopify's Admin GraphQL API to fetch the title, description, price, availability, images, GTIN (the product's international identifier), and more.
We use GraphQL because it lets us grab exactly the fields we need in one shot. Information that would take many REST requests comes back in a single query. When you're dealing with a large SKU count, that efficiency gap really adds up.
Converting to GMC-spec XML
The fetched product data can't be handed to GMC as-is. GMC has fixed field names (title, description, link, image_link, price, availability, and so on) and formatting rules.
So we convert each Shopify product, one at a time, into an XML block that follows this spec. Designing these conversion rules is the crux that determines feed quality. We dig into it in the mapping design article.
Three perspectives that matter in operation
Caching to handle large SKU counts lightly
If we fetched thousands of SKUs via GraphQL and rebuilt the XML every time GMC came to fetch the URL, a heavy process would run on every request.
So we cache the generated result with a TTL of a few hours. Feed contents don't change by the minute, so that's plenty. By controlling page size and serving in chunks, we can return large SKU counts without straining memory. We cover this design in caching and health checks.
If it stops, the ads stop — so we monitor
It's easy to overlook, but if the feed stops, the ads stop. If the feed fails to generate or comes back empty, GMC can no longer list the products, and ad delivery cuts out right along with it.
And it happens quietly. There's no error screen — sales just drift down. That's exactly why a health-check API that watches whether the feed is generating properly is indispensable.
Quality control to reduce disapprovals
GMC reviews the products you hand it. If required fields are missing or a product violates policy, that product is "disapproved" and won't be listed.
Disapprovals quietly erode exposure
A single disapproval is hard to notice, but they accumulate and the number of listed products slowly shrinks. "For some reason the ads aren't performing" often has a pile of disapprovals hiding behind it.
Reliably excluding draft and unpublished products and validating for missing required attributes — we cover these quality-control details in managing feed quality.
Conclusion
By generating the Google Shopping product feed ourselves instead of relying on an app, we can freely design mapping, exclusion control, and multi-site support.
- The value of self-hosting: flexible mapping beyond the app's limits, plus central management of multiple sites
- The mechanism: fetch products via Shopify Admin GraphQL, convert to GMC-spec XML, and serve from a single endpoint
- The operational crux: lighten the load with caching, monitor with health checks, and cut disapprovals with quality control
The feed isn't an accessory to advertising — it's the foundation of acquisition that also powers free listings. If it stops, the ads stop too, which is exactly why it's worth building carefully and monitoring diligently.
Each theme is explored in the sub-articles below.
Mapping Product Data to the Feed Specification
How to convert titles, GTINs, categories, and availability into Google's format.
Feed Caching and Health-Check Operations
Caching to handle large SKU counts, and monitoring that delivery never silently stops.
Managing Feed Quality — Exclusions and Validation
Excluding products that shouldn't be listed, and validating data to reduce disapprovals.