1C-Bitrix Integration with Google Shopping Ads
Google Shopping displays product ads with an image, price, and title directly in the search results. The required chain is: Bitrix → Google Merchant Center → Google Ads. The weak link is the feed: if prices in the feed differ from prices on the site, Google rejects products and ads stop showing. Integration is not a one-time export — it is an automated feed update with monitoring of product statuses in Merchant Center.
Integration Architecture
[Bitrix catalog]
↓
[Feed generator: PHP script or module]
↓
[XML feed on hosting / S3]
↓
[Google Merchant Center: scheduled import]
↓
[Google Ads: Shopping campaigns]
The feed is updated on the Bitrix side; Google Merchant Center fetches it from a URL at a defined frequency (every 24–72 hours or forcefully via the Content API).
Google Shopping Feed Structure (Merchant Center)
Google requires the Google Shopping XML format (or TSV). Required fields:
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
<channel>
<title>My Store</title>
<link>https://your-site.com</link>
<item>
<g:id>PRODUCT-SKU-123</g:id>
<g:title>Product Title (150 chars max)</g:title>
<g:description>Detailed description for the ad</g:description>
<g:link>https://your-site.com/catalog/product-123/</g:link>
<g:image_link>https://your-site.com/upload/img/product-123.jpg</g:image_link>
<g:price>1990 USD</g:price>
<g:sale_price>1490 USD</g:sale_price> <!-- if on sale -->
<g:availability>in stock</g:availability>
<g:condition>new</g:condition>
<g:brand>BrandName</g:brand>
<g:gtin>4607086560001</g:gtin> <!-- barcode, EAN -->
<g:mpn>ART-0001</g:mpn> <!-- article number, if no GTIN -->
<g:product_type>Electronics > Smartphones</g:product_type>
<g:google_product_category>5032</g:google_product_category>
<g:shipping>
<g:country>US</g:country>
<g:service>Courier</g:service>
<g:price>5 USD</g:price>
</g:shipping>
</item>
</channel>
</rss>
g:google_product_category — a numeric ID from the Google taxonomy (downloadable from support.google.com). An incorrect category reduces display relevance.
Feed Generator from Bitrix Catalog
// /local/php_interface/cron/google_shopping_feed.php
define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
require_once($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php');
\Bitrix\Main\Loader::includeModule('iblock');
\Bitrix\Main\Loader::includeModule('catalog');
$IBLOCK_ID = 10; // Catalog ID
$outputFile = $_SERVER['DOCUMENT_ROOT'] . '/feeds/google_shopping.xml';
$xml = new XMLWriter();
$xml->openUri($outputFile);
$xml->startDocument('1.0', 'UTF-8');
$xml->startElement('rss');
$xml->writeAttribute('version', '2.0');
$xml->writeAttribute('xmlns:g', 'http://base.google.com/ns/1.0');
$xml->startElement('channel');
$xml->writeElement('title', 'My Store');
$xml->writeElement('link', 'https://your-site.com');
// Query catalog elements
$elements = \CIBlockElement::GetList(
['SORT' => 'ASC'],
[
'IBLOCK_ID' => $IBLOCK_ID,
'ACTIVE' => 'Y',
'!CATALOG_QUANTITY' => 0, // in stock only
],
false,
false,
['ID', 'NAME', 'DETAIL_PAGE_URL', 'PREVIEW_TEXT', 'DETAIL_TEXT']
);
while ($el = $elements->GetNextElement()) {
$fields = $el->GetFields();
$props = $el->GetProperties();
// Get price
$price = \CPrice::GetBasePrice($fields['ID']);
// Get image
$image = \CFile::GetPath($fields['PREVIEW_PICTURE'] ?: $fields['DETAIL_PICTURE']);
// Availability
$productData = \CCatalogProduct::GetByIDEx($fields['ID']);
$availability = ($productData['QUANTITY'] > 0) ? 'in stock' : 'out of stock';
$xml->startElement('item');
$xml->writeElement('g:id', $fields['ID']);
$xml->writeElement('g:title', substr(strip_tags($fields['NAME']), 0, 150));
$xml->writeElement('g:description', substr(strip_tags($fields['PREVIEW_TEXT'] ?: $fields['DETAIL_TEXT']), 0, 5000));
$xml->writeElement('g:link', 'https://your-site.com' . $fields['DETAIL_PAGE_URL']);
if ($image) {
$xml->writeElement('g:image_link', 'https://your-site.com' . $image);
}
if ($price) {
$xml->writeElement('g:price', number_format($price['PRICE'], 2, '.', '') . ' ' . $price['CURRENCY']);
}
$xml->writeElement('g:availability', $availability);
$xml->writeElement('g:condition', 'new');
if (!empty($props['BRAND']['VALUE'])) {
$xml->writeElement('g:brand', $props['BRAND']['VALUE']);
}
if (!empty($props['BARCODE']['VALUE'])) {
$xml->writeElement('g:gtin', $props['BARCODE']['VALUE']);
} elseif (!empty($props['ARTICLE']['VALUE'])) {
$xml->writeElement('g:mpn', $props['ARTICLE']['VALUE']);
$xml->writeElement('g:identifier_exists', 'no');
}
$xml->endElement(); // item
}
$xml->endElement(); // channel
$xml->endElement(); // rss
$xml->endDocument();
$xml->flush();
The script runs via cron every 2–4 hours. The feed file is accessible at the URL https://your-site.com/feeds/google_shopping.xml.
Content API: Forcing Updates for Individual Products
When a price or stock level changes, waiting for the next feed scan is impractical — Google may show an outdated price for a full day. The Content API allows updating a specific product instantly:
// composer require google/apiclient
$client = new \Google\Client();
$client->setAuthConfig('/path/to/service-account.json');
$client->addScope(\Google\Service\ShoppingContent::CONTENT);
$service = new \Google\Service\ShoppingContent($client);
$product = new \Google\Service\ShoppingContent\Product([
'offerId' => 'PRODUCT-SKU-123',
'price' => new \Google\Service\ShoppingContent\Price([
'value' => '1490.00',
'currency' => 'USD',
]),
'availability' => 'in stock',
]);
$service->products->update($merchantId, 'online:en:US:PRODUCT-SKU-123', $product);
On the OnAfterCatalogProductUpdate event in Bitrix — call the Content API update for the specific product. This adds ~100–200 ms to the save operation, but the price in Google updates within a few minutes.
Common Reasons for Product Rejection in Merchant Center
-
price mismatch— the price in the feed does not match the price on the product page. Google crawls the site separately and compares. Most commonly caused by discounts not reflected in the feed, or a cached page with an outdated price -
missing required attribute [gtin]— no barcode. For many categories GTIN is mandatory. If it is absent — addidentifier_exists: no -
image too small— image is smaller than 100×100 px (250×250 for clothing). Bitrix stores images inupload/, original dimensions must be verified -
landing page not crawlable— the product page is inaccessible to the Google bot. Checkrobots.txtandnoindex
Development Timeline
| Stage | Contents | Duration |
|---|---|---|
| Catalog field mapping | Mapping Bitrix fields/properties → feed fields | 1 day |
| Feed generator | PHP script + cron | 2–3 days |
| Merchant Center setup | Feed upload, site verification, taxes/shipping | 1 day |
| Content API integration | Update on price/stock change | 2–3 days |
| Diagnostics and error fixing | Resolving rejection causes | 1–2 days |







