Generating Product Feeds for Google Merchant Center
Google Merchant Center accepts data only in strictly regulated format — XML (RSS 2.0 or Atom 1.0) or via Content API. Any deviation from the specification results in product rejection and lost shopping ad traffic. We develop a feed generator that passes validation on the first attempt and automatically stays up-to-date.
What's Included
- Analysis of catalog structure and mapping fields to GMC attributes
- XML feed generation according to Google Product Data Specification
- Setup of cron task or webhook trigger for feed updates
- Feed registration in Merchant Center account and initial error diagnostics
Required and Recommended Attributes
| Attribute | Requirement | Note |
|---|---|---|
id |
required | unique SKU, never changes |
title |
required | up to 150 characters, no all caps |
description |
required | up to 5000 characters |
link |
required | canonical product URL |
image_link |
required | HTTPS, min. 100×100 px |
price |
required | format 19.99 USD |
availability |
required | in_stock / out_of_stock / preorder |
brand |
recommended | required for clothing, electronics |
gtin |
recommended | increases Quality Score |
google_product_category |
recommended | numeric ID from Google taxonomy |
custom_label_0..4 |
optional | segmentation in Smart Shopping |
XML Feed Structure
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">
<channel>
<title>Store Name</title>
<link>https://example.com</link>
<description>Product Catalog</description>
<item>
<g:id>SKU-12345</g:id>
<g:title>Nike Air Max 270 Men's Black Running Shoes</g:title>
<g:description>Running shoes with Air Max technology...</g:description>
<g:link>https://example.com/products/nike-air-max-270</g:link>
<g:image_link>https://cdn.example.com/products/nike-270-black.jpg</g:image_link>
<g:availability>in_stock</g:availability>
<g:price>8990.00 RUB</g:price>
<g:brand>Nike</g:brand>
<g:gtin>0012345678905</g:gtin>
<g:google_product_category>187</g:google_product_category>
<g:condition>new</g:condition>
<g:custom_label_0>sale</g:custom_label_0>
</item>
</channel>
</rss>
PHP/Laravel Feed Generator
class GoogleMerchantFeedGenerator
{
public function generate(): string
{
$products = Product::with(['images', 'category', 'brand'])
->where('is_active', true)
->where('stock', '>', 0)
->cursor(); // cursor() for large catalogs — doesn't load RAM
$xml = new \XMLWriter();
$xml->openMemory();
$xml->setIndent(true);
$xml->startDocument('1.0', 'UTF-8');
$xml->startElement('rss');
$xml->writeAttribute('xmlns:g', 'http://base.google.com/ns/1.0');
$xml->writeAttribute('version', '2.0');
$xml->startElement('channel');
foreach ($products as $product) {
$this->writeItem($xml, $product);
}
$xml->endElement(); // channel
$xml->endElement(); // rss
return $xml->outputMemory();
}
private function writeItem(\XMLWriter $xml, Product $product): void
{
$xml->startElement('item');
$xml->writeElement('g:id', $product->sku);
$xml->writeElement('g:title', mb_substr($product->name, 0, 150));
$xml->writeElement('g:link', route('products.show', $product->slug));
$xml->writeElement('g:image_link', $product->mainImage()?->cdn_url ?? '');
$xml->writeElement('g:price', number_format($product->price, 2, '.', '') . ' RUB');
$xml->writeElement('g:availability', $product->stock > 0 ? 'in_stock' : 'out_of_stock');
$xml->writeElement('g:brand', $product->brand?->name ?? '');
$xml->writeElement('g:condition', 'new');
$xml->endElement();
}
}
Feed is cached in file storage and served via a dedicated route with Content-Type: application/xml header. For catalogs exceeding 100,000 items, the feed is split using supplemental feeds mechanism in GMC.
Alternative: Content API for Shopping
For online stores with frequent price and inventory changes (multiple times per day), XML feed is inconvenient due to indexing delays. Content API allows sending changes in real-time:
// Google API Client Library for PHP
$service = new Google\Service\ShoppingContent($client);
$product = new Google\Service\ShoppingContent\Product([
'offerId' => 'SKU-12345',
'title' => $product->name,
'link' => $productUrl,
'price' => ['value' => '8990.00', 'currency' => 'RUB'],
'availability' => 'in_stock',
'channel' => 'online',
'contentLanguage' => 'ru',
'targetCountry' => 'RU',
]);
$service->products->insert('merchant-account-id', $product);
The API approach is used when the store updates prices multiple times per day or uses dynamic pricing.
Common Setup Errors
-
Missing required attribute — most often
gtinfor branded products. Solution: addidentifier_exists: nofor items without barcodes -
Image not crawlable — CDN is blocked by
robots.txtor requires authentication. Solution: allow Googlebot in CDN settings - Price mismatch — feed price doesn't match landing page price. Solution: synchronize data source
Timeline
Setting up feed generator, initial upload, and resolving validation errors — 3–5 working days. Google's standard indexing of products takes another 2–7 days after successful feed submission.







