Ürün feed'i (product feed), mağazanızda satılan her ürünü fiyatı, stok durumu, görseli ve tanımlayıcılarıyla birlikte makinece okunabilir tek bir listede toplar. Google Merchant Center ve Meta'nın katalog altyapısı ürünlerinizi bu listeden okuyup reklamlarda ve alışveriş yüzeylerinde gösterir; listedeki hatalar da uyarı ya da reddedilen ürün (disapproved item) olarak size geri döner. Bu rehberde iki platformun beklediği alanları, Türkiye'deki mağazalar için ayarları, feed'i üretmenin yollarını ve en sık görülen ret nedenlerini ele alıyoruz. Platform kuralları sık değişiyor; buradaki tespitler Ekim 2026 itibarıyla resmî belgelere dayanıyor.
Ürün feed'i nerede kullanılır?
- Google: Merchant Center'a gönderilen ürünler ücretsiz listelemelerde (free listings), Alışveriş reklamlarında (Shopping ads) ve Maksimum Performans (Performance Max) kampanyalarında kullanılır. Türkiye desteklenen ülkeler arasındadır ve listelenen para birimi Türk lirasıdır (TRY). Ürün verisinin dili, açılış sayfasının diliyle aynı olmalıdır.
- Meta: Ticaret Yöneticisi'ndeki (Commerce Manager) katalog, Facebook ve Instagram'daki Advantage+ katalog reklamlarını (eski adıyla dinamik reklamlar) ve kullanılabildiği pazarlarda mağaza deneyimlerini besler.
- Diğer kanallar: Fiyat karşılaştırma siteleri, pazar yerleri ve XML ile ürün alan bayiler genellikle kendi şemalarını ister.
Biçim seçimi: XML, metin dosyası, E-Tablolar veya API
| Yöntem | Ne zaman uygun? | Dikkat edilecek nokta |
|---|---|---|
| XML (RSS 2.0 veya Atom, g: ad alanı) | Kodla üretilen, iç içe alan (shipping gibi) içeren kataloglar | Tek bir kaçışlama (escaping) veya karakter kodlaması hatası tüm dosyanın okunamamasına yol açabilir |
| Metin dosyası (Google: sekmeyle ayrılmış .txt veya .tsv; Meta: CSV veya TSV) | Dışa aktarımla üretilen, düz yapılı kataloglar | Alan içindeki sekme, satır sonu veya virgül sütunları kaydırır |
| Google E-Tablolar (Google Sheets) | Az ürünlü mağazalar ve ek veri kaynakları | Elle bakım hataya açıktır |
| API (Google Merchant API; Meta'nın katalog ve Batch API'si) | Fiyatı ve stoğu gün içinde sık değişen kataloglar | Kimlik doğrulama, kota ve hata yönetimi sizin sorumluluğunuzdadır |
API tarafında önemli bir değişiklik yaşandı: Google'ın Content API for Shopping hizmeti 18 Ağustos 2026'da resmî olarak kullanım ömrünün sonuna ulaştı (sunset). Etkin bir uzatması olmayan istemcilerin istekleri 1 Eylül 2026'dan beri aralıklı olarak HTTP 410 Gone hatasıyla dönüyor; tüm uç noktaların kapatılması 2027 başına planlanmış durumda ve Google bu takvimin değişebileceğini belirtiyor. Yerine Merchant API v1 geldi. Shopify'daki Google & YouTube uygulaması gibi bir platform entegrasyonu kullanıyorsanız geçişi sağlayıcı üstlenir. Google'ın geçiş duyuruları yalnızca API entegrasyonlarını kapsar; dosya yükleme, zamanlanmış getirme ve E-Tablolar kaynakları API çağrısı olmadığından bu geçişten etkilenmez.
Asgari bir XML feed'i şöyle görünür:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
<channel>
<title>Örnek Mağaza</title>
<link>https://www.example.com</link>
<description>Örnek Mağaza ürün feed'i</description>
<item>
<g:id>TSH-001-KIR-M</g:id>
<g:title>Örnek Giyim Basic Tişört %100 Pamuk Kırmızı M</g:title>
<g:description>Bisiklet yaka, %100 pamuk, 30 °C'de yıkanabilir.</g:description>
<g:link>https://www.example.com/basic-tisort?renk=kirmizi&beden=m</g:link>
<g:image_link>https://cdn.example.com/urun/tsh-001-kirmizi.jpg</g:image_link>
<g:availability>in_stock</g:availability>
<g:price>449.90 TRY</g:price>
<g:sale_price>399.90 TRY</g:sale_price>
<g:sale_price_effective_date>2026-11-27T00:00+0300/2026-11-30T23:59+0300</g:sale_price_effective_date>
<g:brand>Örnek Giyim</g:brand>
<g:gtin>8690123456789</g:gtin>
<g:condition>new</g:condition>
<g:item_group_id>TSH-001</g:item_group_id>
<g:color>Kırmızı</g:color>
<g:size>M</g:size>
<g:gender>unisex</g:gender>
<g:age_group>adult</g:age_group>
<g:google_product_category>212</g:google_product_category>
<g:product_type>Giyim > Tişört</g:product_type>
</item>
</channel>
</rss>
Metin dosyasında ilk satır sütun adlarıdır. Aşağıdaki başlık satırı Meta'nın CSV biçimi için yazıldı; Google'a aynı sütunları sekmeyle ayrılmış bir dosyada verin:
Code:
id,title,description,availability,condition,price,sale_price,sale_price_effective_date,link,image_link,brand,gtin,mpn,item_group_id,color,size,gender,age_group,google_product_category
Google özellikleri: zorunlu, koşullu, isteğe bağlı
Merchant Center'ın Ürün verileri spesifikasyonu (Product data specification) her özelliği "Zorunlu", "Duruma göre değişir" veya "İsteğe bağlı" olarak işaretler. Türkiye'yi hedefleyen bir mağaza için özet:| Özellik | Durum | Not |
|---|---|---|
| id | Zorunlu | En fazla 50 karakter; benzersiz ve kalıcı. SKU idealdir. |
| title | Zorunlu | En fazla 150 karakter; marka, ürün tipi ve ayırt edici özellik (renk, beden, kapasite) |
| description | Zorunlu | En fazla 5.000 karakter; kampanya metni değil, ürün bilgisi |
| link | Zorunlu | Doğrulanmış alan adında, oturum açma istemeyen ürün sayfası |
| image_link | Zorunlu | Kalıcı URL; boyut ve içerik kuralları tablonun altında |
| availability | Zorunlu | in_stock, out_of_stock, preorder, backorder; son ikisinde availability_date de gönderilir |
| price | Zorunlu | Sayı ve ISO 4217 kodu (449.90 TRY); ABD ve Kanada dışında KDV dâhil |
| sale_price, sale_price_effective_date | İsteğe bağlı | İndirimli fiyat price değerinden düşük olmalı; tarih verilmezse indirim hemen başlar |
| brand | Zorunlu (yeni ürünlerde) | Film, kitap ve müzik kayıtları hariç; en fazla 70 karakter |
| gtin | Duruma göre değişir | Üreticinin atadığı GTIN varsa gönderin; Google bunu özellikle tavsiye ediyor |
| mpn | Zorunlu (üretici GTIN'i yoksa) | Üreticinin parça numarası; en fazla 70 karakter |
| identifier_exists | İsteğe bağlı | Varsayılan değer yes; no yalnızca tanımlayıcı gerçekten yoksa |
| condition | İkinci el ve yenilenmiş üründe zorunlu | new, refurbished, used; yeni ürünlerde isteğe bağlı |
| google_product_category | İsteğe bağlı | Tek kategori; sayısal numara önerilir. Bazı ürünlerde belirli bir kategori şarttır. |
| product_type | İsteğe bağlı | Kendi kategori yolunuz, en fazla 750 karakter; kampanyaları bölmekte işe yarar |
| shipping | Duruma göre değişir | Google'ın listelediği ülkelerde (ABD, Almanya, Birleşik Krallık gibi) kargo ücreti şart; Türkiye listede yok. Kargoyu hesap düzeyinde tanımlayın, bu özelliği yalnızca istisnalar için kullanın. |
| item_group_id | Koşullu zorunlu | Varyantlı ürünlerde ücretsiz listelemeler için gerekli; reklamlarda Brezilya, Fransa, Almanya, Japonya, Birleşik Krallık ve ABD'de zorunlu |
| color, size, gender, age_group | Koşullu zorunlu | Giyim ve aksesuar ürünlerinde aranır; varyantları ayırıyorsa her durumda gönderin |
Ürün tanımlayıcıları: GTIN, MPN, marka ve identifier_exists
GTIN, barkodun altında yazan küresel ürün numarasıdır; 8, 12, 13 veya 14 haneden oluşur ve son hanesi GS1 kontrol basamağıdır (check digit). Türkiye'de bu numaralar GS1 Türkiye üyeliğiyle alınır ve 868 veya 869 önekiyle başlar. Üyelikte firmanıza, numaralandıracağınız ürün çeşidi sayısına göre uzunluğu değişen bir Firma Önek Numarası (GCP) tahsis edilir: örneğin 9 haneli önek 1.000, 7 haneli önek 100.000 ürün çeşidine yeter.Hangi alanı göndereceğinize şu sırayla karar verin:
- Üreticinin atadığı GTIN var: gtin ve brand gönderin. Bayisi olduğunuz markaların ürünleri çoğunlukla bu gruptadır; numarayı tedarikçinin ürün kartından alın, tahmin etmeyin.
- GTIN yok, üretici parça numarası var: brand ve mpn gönderin. Yedek parça ve endüstriyel ürünlerde yaygın durum budur.
- Kendi markanız (private label), yalnızca siz satıyorsunuz ve barkod numarası almadınız: Google bu durumda GTIN göndermenizin gerekmediğini, bunun yerine brand ve mpn göndermenizi söylüyor. Ürünü başka perakendecilere veya barkod isteyen pazar yerlerine de verecekseniz GS1 Türkiye üyeliği mantıklıdır.
- Hiçbir tanımlayıcı yok: Kişiye özel, el yapımı, tek örnek veya antika ürünlerde identifier_exists değerini no yapın.
Açılış sayfasıyla tutarlılık: fiyat, stok ve yapılandırılmış veri
Google, feed'deki fiyat ve stok bilgisini açılış sayfası ve ödeme adımıyla karşılaştırır; uyuşmazlık bulursa ürünü reddeder. Türkiye'deki mağazalar için dikkat edilecek noktalar:- Fiyatı KDV dâhil ve TRY olarak gönderin; ürün sayfası ve ödeme adımı da aynı tutarı göstermeli. Fiyatı veya para birimini ziyaretçinin konumuna göre değiştirmeyin: Googlebot'un varsayılan IP adresleri ABD'de görünür, bu yüzden otomatik kur dönüşümü doğrudan uyuşmazlık üretir.
- Ondalık ayırıcı olarak nokta kullanın; Google'ın tüm örnekleri bu biçimdedir. Türkçe yerel ayarın ürettiği "1.299,90 TL" feed'de "1299.90 TRY" olarak yazılır; bu dönüşümü biçimlendirilmiş metinden değil, sayısal değerden yapın. Google ondalık kısımda en fazla iki hane istiyor, fazlasını kendisi yuvarlıyor.
- İndirim tarihlerini saat dilimiyle yazın. Türkiye yıl boyunca UTC+3'tedir; saat farkı (offset) belirtilmeden yazılan "2026-11-27T00:00" UTC kabul edilir ve indirim Türkiye saatiyle 03.00'te başlar.
- İndirimli fiyat duyurularının hukuki koşullarını, indirim öncesi fiyatın nasıl belirleneceği de dâhil olmak üzere, Ticari Reklam ve Haksız Ticari Uygulamalar Yönetmeliği çerçevesinde hukuk danışmanınızla kontrol edin.
JSON:
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Örnek Giyim Basic Tişört Kırmızı M",
"sku": "TSH-001-KIR-M",
"gtin13": "8690123456789",
"brand": {"@type": "Brand", "name": "Örnek Giyim"},
"image": ["https://cdn.example.com/urun/tsh-001-kirmizi.jpg"],
"offers": {
"@type": "Offer",
"url": "https://www.example.com/basic-tisort?renk=kirmizi&beden=m",
"price": 399.90,
"priceCurrency": "TRY",
"priceSpecification": {
"@type": "UnitPriceSpecification",
"priceType": "https://schema.org/StrikethroughPrice",
"price": 449.90,
"priceCurrency": "TRY"
},
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition"
}
}
Varyantlar: item_group_id eşlemesi
Feed'de her satılabilir SKU ayrı bir öğedir. Ana ürün (parent) ayrıca gönderilmez; varyantlar ortak bir varyant grubu (item_group_id; Google'ın Türkçe belgelerinde "Öğe grubu kodu") altında toplanır:- id varyantın SKU'su, item_group_id ana ürünün kodudur. İkisi de kalıcı olmalı; grup kodunu değiştirmek gruplamayı tutarsızlaştırır.
- Varyantı ayıran özellikler (color, size, material, pattern) her öğede dolu olmalı; aynı gruptaki iki öğe aynı kombinasyonu taşımamalı.
- Her varyantın kendi URL'si olmalı (/basic-tisort?renk=kirmizi&beden=m gibi) ve sayfa o varyant seçili olarak açılmalı. Varsayılan varyantla açılan sayfa, fiyat ve stok uyuşmazlığının en yaygın nedenlerinden biridir.
- Varyant olmayan ürünleri, örneğin bir takımın ceketini ve pantolonunu, aynı gruba koymayın.
Merchant Center hesap ayarları (Türkiye)
Feed kusursuz olsa bile hesap ayarları eksikse ürünler yayına çıkmaz:- İşletme bilgileri: Ticari unvan, adres ve müşteri hizmetleri iletişim bilgisi sitedekiyle tutarlı olmalı.
- Site doğrulama ve sahiplenme: HTML etiketi veya dosyası, Google Etiket Yöneticisi, Google Analytics, e-ticaret platformunuz ya da e-posta doğrulamasıyla yapılır. Bir alan adını yalnızca bir hesap sahiplenebilir; birden fazla hesabın aynı alan adını kullanması için çoklu müşteri hesabı (MCA) gerekir.
- Kargo: Kargo ve iade (Shipping and returns) ayarlarında Türkiye için TRY cinsinden ücreti (ücretsiz, sabit, belirli tutarın üzerinde ücretsiz veya ağırlığa göre), sipariş kesim saatini, hazırlık ve taşıma sürelerini tanımlayın. Türkiye, kargo bilgisinin zorunlu olduğu ülkeler arasında değil; ancak teslimat süresini ve ücretini göstermek alışveriş yapanın kararını doğrudan etkiler.
- İade politikası: Süre, koşullar ve masraf açıkça yazılmalı ve tüketicinin Mesafeli Sözleşmeler Yönetmeliği'nden doğan 14 günlük cayma hakkıyla uyumlu olmalı.
- Site gereksinimleri: Kolay bulunan iletişim bilgisi, kredi kartı, banka kartı veya kapıda ödeme gibi en az bir yaygın ödeme yöntemi, SSL ile korunan ödeme sayfası, satın almadan önce gösterilen tüm masraflar ve sorunsuz tamamlanan bir ödeme akışı.
Meta katalog: veri kaynakları ve piksel eşleşmesi
Meta'da kataloğa ürün eklemenin yolları şunlardır: Ticaret Yöneticisi'nde form doldurarak tek tek ekleme, tek seferlik dosya yükleme, bir adresten zamanlanmış feed, Google E-Tablolar, Shopify veya WooCommerce gibi iş ortağı platform entegrasyonları, Meta pikseliyle sayfalardan okuma ve API (tekil ürün uç noktaları ile Batch API). Mevcut ürünlerin yalnızca bazı alanlarını güncellemek için birincil kaynağa ek veri akışı (supplementary feed) bağlanabilir; bu akış ürün ekleyip silemez ve her id değeri birincil kaynaktaki bir ürünle eşleşmelidir.Meta, Google biçimindeki XML feed'ini (g: ad alanı) kabul ettiği için birçok mağaza tek dosyayla iki platformu besler. Aradaki farklar:
| Konu | Google Merchant Center | Meta katalog |
|---|---|---|
| Temel zorunlu alanlar | id, title, description, link, image_link, availability, price; yeni üründe brand | id, title, description, availability, condition, price, link, image_link; ayrıca brand, gtin veya mpn'den en az biri |
| id uzunluğu | En fazla 50 karakter | En fazla 100 karakter |
| title ve description | 150 ve 5.000 karakter | 200 ve 9.999 karakter, düz metin |
| Stok değerleri | in_stock, out_of_stock, preorder, backorder | Belgelenen: in stock, out of stock |
| Görsel | JPEG, WebP, PNG, GIF, BMP, TIFF; en fazla 16 MB | JPEG veya PNG; en az 500×500 piksel, en fazla 8 MB |
| Kategori | İsteğe bağlı | Her üründe gönderilmesi istenir: google_product_category veya fb_product_category |
Advantage+ katalog reklamlarının çalışması için piksel ve Dönüşümler API'si (Conversions API) olaylarındaki ürün kimlikleri katalogdaki id değerleriyle aynı olmalı ve piksel, kataloğa olay kaynağı olarak bağlanmalıdır. Meta, uygun olduğu sayfalarda Search, ViewCategory, ViewContent, AddToCart ve Purchase olaylarının kurulmasını istiyor; ViewCategory standart bir olay olmadığından fbq('trackCustom', ...) ile gönderilir. Kullanıcı henüz varyant seçmemişse ViewContent olayında item_group_id değerini content_type: 'product_group' ile gönderebilirsiniz; AddToCart ve Purchase olaylarında product_group kullanılmaz.
Code:
fbq('track', 'ViewContent', {
content_ids: ['TSH-001-KIR-M'], // katalogdaki id ile aynı
content_type: 'product',
value: 399.90,
currency: 'TRY'
});
fbq('track', 'Purchase', {
contents: [{id: 'TSH-001-KIR-M', quantity: 2}],
content_type: 'product',
value: 799.80,
currency: 'TRY'
}, {eventID: 'order-100245'}); // sunucu olayındaki event_id ile aynı
Feed'i üretmek: eklenti mi, özel üretici mi?
WooCommerce'te resmî Google for WooCommerce ve Meta for WooCommerce eklentileri, Shopify'da ise Google & YouTube ile Facebook & Instagram uygulamaları ürünleri doğrudan eşitler. Katalog düzgün modellenmişse bu yol yeterlidir. Özel üretici şu durumlarda gerekir: ürün verisi ERP'de veya PIM'de duruyorsa, başlıkları kurallarla zenginleştirmek istiyorsanız, aynı veriden pazar yeri ve bayi XML'leri de üretiyorsanız ya da on binlerce ürünlük bir kataloğu bellek sorunu yaşamadan yazmanız gerekiyorsa.Aşağıdaki Python (3.9 ve üzeri) betiği ürünleri bir üreteçten (generator) okur ve XML'i akış halinde (streaming) yazar. Kaçışlamayı XMLGenerator'a bırakır, XML 1.0'ın kabul etmediği kontrol karakterlerini temizler, fiyatı Decimal ile biçimlendirir ve dosyayı geçici bir adla yazıp sonunda atomik olarak yerine koyar:
Python:
import html, logging, os, re
from decimal import Decimal, ROUND_HALF_UP
from xml.sax.saxutils import XMLGenerator
from zoneinfo import ZoneInfo
log = logging.getLogger("feed")
IST = ZoneInfo("Europe/Istanbul")
BAD_XML = re.compile("[\x00-\x08\x0b\x0c\x0e-\x1f\ufffe\uffff]") # XML 1.0'da yasak
TAGS = re.compile(r"<[^>]+>")
def text(value, limit):
s = html.unescape(TAGS.sub(" ", value or "")) # HTML'i düz metne çevir
return " ".join(BAD_XML.sub("", s).split())[:limit]
def money(amount, currency="TRY"): # "1299.9" -> "1299.90 TRY"
return f"{Decimal(amount).quantize(Decimal('0.01'), ROUND_HALF_UP)} {currency}"
def gtin_ok(code): # GS1 kontrol basamağı
if not code.isdigit() or len(code) not in (8, 12, 13, 14):
return False
body, check = code[:-1], int(code[-1])
total = sum(int(d) * (3 if n % 2 == 0 else 1) for n, d in enumerate(reversed(body)))
return (10 - total % 10) % 10 == check
def fields(p):
f = {"g:id": p["sku"], "g:title": text(p["title"], 150),
"g:description": text(p["description"], 5000),
"g:link": p["url"], "g:image_link": p["image"],
"g:availability": "in_stock" if p["stock"] > 0 else "out_of_stock",
"g:price": money(p["price"]), "g:condition": "new",
"g:brand": text(p["brand"], 70), "g:item_group_id": p.get("parent_sku"),
"g:color": p.get("color"), "g:size": p.get("size"), "g:mpn": p.get("mpn")}
if p.get("sale_price"):
f["g:sale_price"] = money(p["sale_price"])
if p.get("sale_start") and p.get("sale_end"): # saat dilimli datetime
s, e = p["sale_start"].astimezone(IST), p["sale_end"].astimezone(IST)
f["g:sale_price_effective_date"] = f"{s:%Y-%m-%dT%H:%M%z}/{e:%Y-%m-%dT%H:%M%z}"
gtin = (p.get("gtin") or "").strip()
if gtin_ok(gtin):
f["g:gtin"] = gtin
elif gtin:
log.warning("geçersiz GTIN, kaynağı düzeltin: %s %s", p["sku"], gtin)
elif not p.get("mpn"):
f["g:identifier_exists"] = "no" # yalnızca gerçekten kod yoksa
return f
def el(x, name, value):
x.startElement(name, {})
x.characters(str(value)) # &, < ve > burada kaçışlanır
x.endElement(name)
def write_feed(products, path, site, shop):
tmp = path + ".tmp"
with open(tmp, "wb") as out:
x = XMLGenerator(out, encoding="utf-8")
x.startDocument()
x.startElement("rss", {"version": "2.0", "xmlns:g": "http://base.google.com/ns/1.0"})
x.startElement("channel", {})
for name, value in (("title", shop), ("link", site), ("description", shop)):
el(x, name, value)
for p in products: # üreteç: katalog belleğe alınmaz
if not p.get("image") or Decimal(p["price"]) <= 0:
log.warning("atlandı (görsel/fiyat yok): %s", p["sku"])
continue
x.startElement("item", {})
for name, value in fields(p).items():
if value:
el(x, name, value)
x.endElement("item")
x.ignorableWhitespace("\n")
x.endElement("channel")
x.endElement("rss")
x.endDocument()
os.replace(tmp, path) # okuyucu yarım dosya görmez
Feed'e dokunmadan düzeltme yapmak için Merchant Center'da ek veri kaynakları (supplemental data sources) ve özellik kuralları (attribute rules) kullanılır. İkisi de Ayarlar > Eklentiler (Settings > Add-ons) bölümünden "Gelişmiş veri kaynağı yönetimi" (Advanced data source management) eklentisinin etkinleştirilmesini gerektirir. Tipik kullanımlar: eksik GTIN'leri bir tablodan eklemek, başlıkların başına marka yazmak, kâr marjı veya sezon gruplarını custom_label_0-4 alanlarına yazıp kampanyaları bunlara göre bölmek. Tükenen ürünü availability ile bildirin; depo kapanışı veya bakım gibi kısa süreli durumlarda ürünü silmeden reklamlardan çekmek için pause özelliği (ads değeriyle) kullanılabilir. Google duraklatmayı en fazla 14 gün tutmanızı istiyor; daha uzun süren duraklatma ürünün reddedilmesine yol açar.
Reddedilen ürünler: sorun giderme tablosu
| Belirti | Olası neden | Çözüm |
|---|---|---|
| Fiyat uyuşmazlığı | Önbellekteki eski sayfa, KDV hariç feed fiyatı, konuma göre kur dönüşümü, varsayılan varyantı gösteren sayfa, yalnızca sepette uygulanan indirim | Feed'i ve sayfayı aynı kaynaktan üretin, fiyat değişince önbelleği temizleyin, indirimi sale_price ile verin |
| Stok uyuşmazlığı | Seyrek üretilen feed, yapılandırılmış veride sabit InStock, tükenen varyantın seçili açılması | Stok değişiminde feed üretimini veya API çağrısını tetikleyin; işaretlemeyi gerçek stoktan üretin |
| Eksik veya geçersiz tanımlayıcı | Hatalı kontrol basamağı, kısıtlı önek, varyantlara kopyalanmış GTIN, GTIN'i olan üründe identifier_exists=no | GTIN'i tedarikçiden doğrulayın; yoksa brand ve mpn gönderin ya da identifier_exists=no kullanın |
| Görsel sorunu | Küçük görsel, tanıtım yazısı veya filigran, yer tutucu görsel, görsel sunucusunun Google tarayıcılarını engellemesi | Temiz ürün görseli kullanın; CDN ve WAF kurallarında Googlebot-Image'a izin verin |
| Açılış sayfası taranamıyor | robots.txt engeli, üyelik duvarı, yurt dışı IP engeli (geo-blocking), bot koruması, 404 veya tükenen ürünü ana sayfaya yönlendirme | Googlebot ve Storebot-Google'ı engellemeyin; AdsBot-Google genel * kuralını yok saydığı için onu adıyla engellemediğinizden emin olun; ülke engelinde Google tarayıcılarını istisna tutun |
| Yanlış beyan veya site gereksinimleri | Eksik iletişim veya iade bilgisi, gizlenen masraflar, karşılanamayan teklifler | Eksikleri tamamlayıp Merchant Center'dan inceleme isteyin |
| Politika kısıtlaması | Ülkeye göre kısıtlı kategoriler (alkol, reçeteli ilaç, silah gibi) | Alışveriş politikalarını okuyun; gerekirse excluded_destination ile ürünü ilgili yüzeyden çıkarın |
| Meta'da düşük katalog eşleşmesi | Piksel olaylarında katalogda bulunmayan kimlikler (ana ürün kodu, veritabanı satır numarası) | content_ids alanına feed'deki id değerini yazın ve pikseli kataloğa bağlayın |
Sık sorulan sorular
GTIN zorunlu mu?Üreticinin atadığı bir GTIN varsa gönderin; göndermemek görünürlüğü sınırlar ve ürünün reddedilmesine yol açabilir. Yalnızca sizin sattığınız ve barkod almadığınız kendi markanızın ürünlerinde brand ve mpn yeterlidir; el yapımı ve kişiye özel ürünlerde identifier_exists=no kullanılır.
XML mi, Google E-Tablolar mı?
Birkaç yüz ürünlük ve elle yönetilen kataloglarda E-Tablolar pratiktir. Ürünler bir veritabanında duruyorsa XML veya metin dosyasını kodla üretin; elle müdahale ve sürüm karmaşası ortadan kalkar.
Feed ne sıklıkla yenilenmeli?
Google'da ürünlerin süresi son yenilemeden 30 gün sonra dolar; bunu hedef değil, en geç sınır olarak görün ve feed'i pratikte en az günde bir kez üretin. Fiyat ve stok gün içinde değişiyorsa değişen ürünleri API ile anında gönderin; Meta'da zamanlanmış feed saatlik de çalışabilir.
Ürünüm neden fiyat uyuşmazlığı yüzünden reddedildi?
Google'ın açılış sayfasında gördüğü fiyat, feed'deki fiyatla aynı değil. Sayfayı oturum açmadan ve Türkiye dışından (örneğin bir VPN ile) açıp hem görünen fiyata hem de yapılandırılmış verideki price değerine bakın; en sık nedenler KDV, kur dönüşümü, önbellek ve varsayılan varyanttır.
Merchant API'ye geçmem gerekiyor mu?
Content API for Shopping'i doğrudan çağıran bir entegrasyonunuz varsa evet. Merchant API v1'i kullanmadan önce Google Cloud projenizi registerGcp yöntemiyle bir kez kaydetmeniz gerekir; ürünler productInputs.insert ile API türündeki bir veri kaynağına yazılır. Dosya, zamanlanmış getirme veya E-Tablolar kullanıyorsanız bir şey yapmanız gerekmez.
Sonuç ve sonraki adımlar
Sağlam bir feed üç şeye dayanır: tek bir veri kaynağı, açılış sayfasıyla birebir tutarlılık ve doğru tanımlayıcılar. Önerilen sıra şöyle: Merchant Center'daki sorunlu ürünleri ret nedenine göre gruplayıp en çok ürünü etkileyen sorundan başlayın; id, fiyat ve stok alanlarını tek kaynaktan üretin; yapılandırılmış veriyi feed'le eşitleyin; Meta pikselindeki ürün kimliklerini katalogla eşleştirin; Content API kullanıyorsanız Merchant API v1 geçişini tamamlayın. Feed altyapısını ve kampanya ölçümünü birlikte kurmak isterseniz AIOR'un pazarlama ve SEO hizmetlerine göz atabilirsiniz. Kendi feed'inizle ilgili sorularınızı bu konuya yazabilirsiniz.A product feed is a single machine-readable list of everything you sell, with each item's price, availability, images and identifiers. Google Merchant Center and Meta's catalog read your products from that list and show them in ads and on shopping surfaces, and errors in it come back to you as warnings or disapproved items. This guide covers the attributes each platform expects, the settings that matter for a store in Turkey (Türkiye), the ways to generate a feed, and the disapprovals we see most often. Platform rules change frequently; everything below reflects the official documentation as of October 2026.
Where a product feed is used
- Google: products in Merchant Center power free listings, Shopping ads and Performance Max campaigns. Turkey is a supported country and its listed currency is the Turkish lira (TRY). Your product data should be in the same language as your landing pages.
- Meta: the catalog in Commerce Manager feeds Advantage+ catalog ads (formerly dynamic ads) on Facebook and Instagram, and shop experiences in the markets where they are available.
- Other channels: price comparison sites, marketplaces and dealers who take XML exports usually want their own schema.
Choosing a format: XML, delimited text, Google Sheets or an API
| Method | Good fit | Watch out for |
|---|---|---|
| XML (RSS 2.0 or Atom with the g: namespace) | Code-generated catalogs and nested attributes such as shipping | One escaping or encoding mistake can make the whole file unreadable |
| Delimited text (Google: tab-delimited .txt or .tsv; Meta: CSV or TSV) | Flat catalogs produced by an export | Tabs, line breaks or commas inside a field shift the columns |
| Google Sheets | Small stores and supplemental data | Manual upkeep invites errors |
| API (Google Merchant API; Meta's catalog and Batch APIs) | Catalogs whose prices and stock change several times a day | Authentication, quotas and error handling are on you |
On the API side there has been a major change. Google's Content API for Shopping reached its sunset on 18 August 2026. Since 1 September 2026, requests from clients without an active extension fail intermittently with HTTP 410 Gone, and the full shutdown is planned for early 2027 (Google notes that the timing may still be revised). Merchant API v1 is the replacement. If you sync through a platform integration such as the Google & YouTube app on Shopify, the provider handles the migration. Google's migration notices cover API integrations only; file uploads, scheduled fetches and Google Sheets sources are not API calls, so the switch does not touch them.
A minimal XML feed looks like this:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
<channel>
<title>Example Store</title>
<link>https://www.example.com</link>
<description>Example Store product feed</description>
<item>
<g:id>TSH-001-RED-M</g:id>
<g:title>Example Wear Basic T-Shirt 100% Cotton Red M</g:title>
<g:description>Crew neck, 100% cotton, machine washable at 30 °C.</g:description>
<g:link>https://www.example.com/basic-t-shirt?color=red&size=m</g:link>
<g:image_link>https://cdn.example.com/products/tsh-001-red.jpg</g:image_link>
<g:availability>in_stock</g:availability>
<g:price>449.90 TRY</g:price>
<g:sale_price>399.90 TRY</g:sale_price>
<g:sale_price_effective_date>2026-11-27T00:00+0300/2026-11-30T23:59+0300</g:sale_price_effective_date>
<g:brand>Example Wear</g:brand>
<g:gtin>8690123456789</g:gtin>
<g:condition>new</g:condition>
<g:item_group_id>TSH-001</g:item_group_id>
<g:color>Red</g:color>
<g:size>M</g:size>
<g:gender>unisex</g:gender>
<g:age_group>adult</g:age_group>
<g:google_product_category>212</g:google_product_category>
<g:product_type>Clothing > T-Shirts</g:product_type>
</item>
</channel>
</rss>
In a delimited file, the first row holds the column names. The header below is written for Meta's CSV format; give Google the same columns in a tab-delimited file:
Code:
id,title,description,availability,condition,price,sale_price,sale_price_effective_date,link,image_link,brand,gtin,mpn,item_group_id,color,size,gender,age_group,google_product_category
Google attributes: required, conditional and optional
Merchant Center's Product data specification labels each attribute as required, "it depends" or optional. A summary for a store selling in Turkey:| Attribute | Status | Notes |
|---|---|---|
| id | Required | Up to 50 characters, unique and stable. The SKU is ideal. |
| title | Required | Up to 150 characters: brand, product type and the distinguishing detail (color, size, capacity) |
| description | Required | Up to 5,000 characters of product information, not promotional copy |
| link | Required | Product page on your verified domain that needs no sign-in |
| image_link | Required | Stable URL; size and content rules below the table |
| availability | Required | in_stock, out_of_stock, preorder, backorder; the last two also need availability_date |
| price | Required | Number plus ISO 4217 code (449.90 TRY); VAT included everywhere except the US and Canada |
| sale_price, sale_price_effective_date | Optional | The sale price must be lower than price; without dates the sale starts immediately |
| brand | Required for new products | Except movies, books and musical recordings; up to 70 characters |
| gtin | It depends | Submit the manufacturer-assigned GTIN when one exists; Google strongly recommends it |
| mpn | Required when there is no manufacturer GTIN | Manufacturer part number, up to 70 characters |
| identifier_exists | Optional | Defaults to yes; set no only when identifiers truly do not exist |
| condition | Required for used and refurbished items | new, refurbished, used; optional for new products |
| google_product_category | Optional | One category, numeric ID preferred. Some products must use a specific category. |
| product_type | Optional | Your own category path, up to 750 characters; useful for splitting campaigns |
| shipping | It depends | Shipping costs are required in the countries Google lists (the US, Germany and the UK among them); Turkey is not on the list. Define shipping at account level and keep the attribute for exceptions. |
| item_group_id | Conditionally required | Needed for variants in free listings; required for ads in Brazil, France, Germany, Japan, the UK and the US |
| color, size, gender, age_group | Conditionally required | Expected for apparel and accessories; always send them when they distinguish variants |
Product identifiers: GTIN, MPN, brand and identifier_exists
A GTIN is the global product number printed under a barcode: 8, 12, 13 or 14 digits with a valid GS1 check digit. In Turkey, these numbers come from a GS1 Türkiye membership and start with the 868 or 869 prefix. On joining, your company receives a GS1 Company Prefix (GCP) whose length depends on how many products you need to number: a 9-digit prefix covers up to 1,000 products, a 7-digit prefix up to 100,000.Decide what to send in this order:
- The manufacturer assigned a GTIN: send gtin and brand. Most branded goods you resell fall here. Take the number from the supplier's product data; never guess.
- No GTIN, but a manufacturer part number: send brand and mpn. This is common for spare parts and industrial goods.
- Your own private label, sold only by you, with no GTIN assigned: Google says you do not need to submit a GTIN in this case; send brand and mpn instead. If you will also supply other retailers or marketplaces that demand barcodes, a GS1 Türkiye membership makes sense.
- No identifiers at all: for custom, handmade, one-of-a-kind or antique products, set identifier_exists to no.
Landing page consistency: price, availability and structured data
Google compares the price and availability in your feed with the landing page and checkout, and disapproves the item when they disagree. For stores in Turkey:- Send prices in TRY with KDV (Turkish VAT) included, and make the product page and checkout show the same amount. Do not change the price or currency based on the visitor's location: Googlebot's default IP addresses appear to be in the US, so automatic currency conversion produces mismatches by itself.
- Use a dot as the decimal separator, as all of Google's examples do. The Turkish locale renders "1.299,90 TL", which should become "1299.90 TRY" in the feed; convert from the numeric value, never from the formatted string. Google asks for no more than two decimal places and rounds any extra digits.
- Write sale dates with a time zone. Turkey is on UTC+3 all year; "2026-11-27T00:00" without an offset is read as UTC, so the sale would start at 03:00 local time.
- Check the legal conditions for advertising discounts in Turkey, including how the pre-discount price is determined, against the Regulation on Commercial Advertising and Unfair Commercial Practices with your legal adviser.
JSON:
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Example Wear Basic T-Shirt Red M",
"sku": "TSH-001-RED-M",
"gtin13": "8690123456789",
"brand": {"@type": "Brand", "name": "Example Wear"},
"image": ["https://cdn.example.com/products/tsh-001-red.jpg"],
"offers": {
"@type": "Offer",
"url": "https://www.example.com/basic-t-shirt?color=red&size=m",
"price": 399.90,
"priceCurrency": "TRY",
"priceSpecification": {
"@type": "UnitPriceSpecification",
"priceType": "https://schema.org/StrikethroughPrice",
"price": 449.90,
"priceCurrency": "TRY"
},
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition"
}
}
Variants: mapping item_group_id
Every sellable SKU is a separate item in the feed. The parent product is not submitted on its own; the variants share one item group (item_group_id):- id is the variant SKU and item_group_id is the parent code. Keep both stable; changing the group ID makes the grouping inconsistent.
- The attributes that distinguish variants (color, size, material, pattern) must be filled on every item, and no two items in a group may share the same combination.
- Each variant needs its own URL (such as /basic-t-shirt?color=red&size=m), and the page must open with that variant selected. A page that opens on the default variant is one of the most common causes of price and availability mismatches.
- Do not group products that are not variants, such as the jacket and trousers of a suit.
Merchant Center account settings for Turkey
A flawless feed still goes nowhere if the account is incomplete:- Business information: legal name, address and customer service contact details that match the website.
- Website verification and claim: done with an HTML tag or file, Google Tag Manager, Google Analytics, your e-commerce platform or email verification. Only one account can claim a URL; several accounts sharing a domain need a multi-client account (MCA).
- Shipping: under Shipping and returns, define the cost for Turkey in TRY (free, flat rate, free over a threshold or weight-based), the order cutoff time, and handling and transit times. Turkey is not among the countries where shipping costs are mandatory, but showing delivery time and cost directly affects the shopper's decision.
- Return policy: state the window, conditions and costs clearly, consistent with the 14-day withdrawal right consumers have under Turkey's Distance Contracts Regulation (Mesafeli Sözleşmeler Yönetmeliği).
- Website requirements: easy-to-find contact details, at least one conventional payment method, an SSL-protected checkout, all costs disclosed before purchase, and a checkout that actually completes.
Meta catalog: data sources and pixel matching
Meta offers several ways to put products into a catalog: adding items one by one through a form in Commerce Manager, a one-time file upload, a scheduled feed from a URL, Google Sheets, partner platform integrations such as Shopify or WooCommerce, the Meta Pixel reading your pages, and the API (single-item endpoints and the Batch API). To update only some fields of existing products, attach a supplementary feed to the primary source; it cannot add or remove products, and every id in it must match a product from the primary source.Because Meta accepts Google-format XML feeds (with the g: namespace), many stores serve both platforms from one file. The differences:
| Topic | Google Merchant Center | Meta catalog |
|---|---|---|
| Core required fields | id, title, description, link, image_link, availability, price; brand for new products | id, title, description, availability, condition, price, link, image_link, plus at least one of brand, gtin or mpn |
| id length | Up to 50 characters | Up to 100 characters |
| title and description | 150 and 5,000 characters | 200 and 9,999 characters, plain text |
| Availability values | in_stock, out_of_stock, preorder, backorder | Documented: in stock, out of stock |
| Images | JPEG, WebP, PNG, GIF, BMP, TIFF; up to 16 MB | JPEG or PNG; at least 500×500 pixels, up to 8 MB |
| Category | Optional | Expected on every product: google_product_category or fb_product_category |
For Advantage+ catalog ads to work, the product IDs in your Pixel and Conversions API events must equal the id values in the catalog, and the pixel must be connected to the catalog as an event source. Meta asks for the Search, ViewCategory, ViewContent, AddToCart and Purchase events on the pages where they apply; ViewCategory is not a standard event, so it is sent with fbq('trackCustom', ...). When the shopper has not picked a variant yet, ViewContent may carry the item_group_id with content_type: 'product_group'; never use product_group with AddToCart or Purchase.
Code:
fbq('track', 'ViewContent', {
content_ids: ['TSH-001-RED-M'], // same as the id in the catalog
content_type: 'product',
value: 399.90,
currency: 'TRY'
});
fbq('track', 'Purchase', {
contents: [{id: 'TSH-001-RED-M', quantity: 2}],
content_type: 'product',
value: 799.80,
currency: 'TRY'
}, {eventID: 'order-100245'}); // same event_id as the server event
Generating the feed: plugin or custom generator?
On WooCommerce, the official Google for WooCommerce and Meta for WooCommerce extensions, and on Shopify the Google & YouTube and Facebook & Instagram apps, sync products directly. If the catalog is modelled properly, that is enough. A custom generator pays off when product data lives in an ERP or PIM, when you want rule-based title enrichment, when the same data must also produce marketplace and dealer XML files, or when tens of thousands of products have to be written without memory trouble.The Python script below (3.9 or later) reads products from a generator and streams the XML out. It leaves escaping to XMLGenerator, strips the control characters XML 1.0 does not allow, formats prices with Decimal, and writes to a temporary file that atomically replaces the live one at the end:
Python:
import html, logging, os, re
from decimal import Decimal, ROUND_HALF_UP
from xml.sax.saxutils import XMLGenerator
from zoneinfo import ZoneInfo
log = logging.getLogger("feed")
IST = ZoneInfo("Europe/Istanbul")
BAD_XML = re.compile("[\x00-\x08\x0b\x0c\x0e-\x1f\ufffe\uffff]") # not allowed in XML 1.0
TAGS = re.compile(r"<[^>]+>")
def text(value, limit):
s = html.unescape(TAGS.sub(" ", value or "")) # HTML to plain text
return " ".join(BAD_XML.sub("", s).split())[:limit]
def money(amount, currency="TRY"): # "1299.9" -> "1299.90 TRY"
return f"{Decimal(amount).quantize(Decimal('0.01'), ROUND_HALF_UP)} {currency}"
def gtin_ok(code): # GS1 check digit
if not code.isdigit() or len(code) not in (8, 12, 13, 14):
return False
body, check = code[:-1], int(code[-1])
total = sum(int(d) * (3 if n % 2 == 0 else 1) for n, d in enumerate(reversed(body)))
return (10 - total % 10) % 10 == check
def fields(p):
f = {"g:id": p["sku"], "g:title": text(p["title"], 150),
"g:description": text(p["description"], 5000),
"g:link": p["url"], "g:image_link": p["image"],
"g:availability": "in_stock" if p["stock"] > 0 else "out_of_stock",
"g:price": money(p["price"]), "g:condition": "new",
"g:brand": text(p["brand"], 70), "g:item_group_id": p.get("parent_sku"),
"g:color": p.get("color"), "g:size": p.get("size"), "g:mpn": p.get("mpn")}
if p.get("sale_price"):
f["g:sale_price"] = money(p["sale_price"])
if p.get("sale_start") and p.get("sale_end"): # timezone-aware datetimes
s, e = p["sale_start"].astimezone(IST), p["sale_end"].astimezone(IST)
f["g:sale_price_effective_date"] = f"{s:%Y-%m-%dT%H:%M%z}/{e:%Y-%m-%dT%H:%M%z}"
gtin = (p.get("gtin") or "").strip()
if gtin_ok(gtin):
f["g:gtin"] = gtin
elif gtin:
log.warning("invalid GTIN, fix the source: %s %s", p["sku"], gtin)
elif not p.get("mpn"):
f["g:identifier_exists"] = "no" # only when no code exists
return f
def el(x, name, value):
x.startElement(name, {})
x.characters(str(value)) # &, < and > are escaped here
x.endElement(name)
def write_feed(products, path, site, shop):
tmp = path + ".tmp"
with open(tmp, "wb") as out:
x = XMLGenerator(out, encoding="utf-8")
x.startDocument()
x.startElement("rss", {"version": "2.0", "xmlns:g": "http://base.google.com/ns/1.0"})
x.startElement("channel", {})
for name, value in (("title", shop), ("link", site), ("description", shop)):
el(x, name, value)
for p in products: # generator: catalog never held in memory
if not p.get("image") or Decimal(p["price"]) <= 0:
log.warning("skipped (no image/price): %s", p["sku"])
continue
x.startElement("item", {})
for name, value in fields(p).items():
if value:
el(x, name, value)
x.endElement("item")
x.ignorableWhitespace("\n")
x.endElement("channel")
x.endElement("rss")
x.endDocument()
os.replace(tmp, path) # readers never see a half-written file
To fix data without touching the feed, Merchant Center offers supplemental data sources and attribute rules. Both require activating the "Advanced data source management" add-on under Settings > Add-ons. Typical uses: adding missing GTINs from a sheet, prefixing titles with the brand, and writing margin or seasonal groups into custom_label_0-4 so campaigns can be split on them. Report sold-out products through availability. For short interruptions such as a warehouse closure or site maintenance, the pause attribute with the value ads takes a product out of ads without deleting it; Google asks you to pause for 14 days or less, and a longer pause leads to disapproval.
Disapproved products: troubleshooting table
| Symptom | Likely cause | Fix |
|---|---|---|
| Price mismatch | Stale cached page, feed price without VAT, currency switched by location, page showing the default variant, a discount applied only in the cart | Build feed and page from the same source, purge the cache on price changes, send discounts as sale_price |
| Availability mismatch | Infrequent feed builds, InStock hard-coded in structured data, a sold-out variant preselected | Trigger a feed build or API call on stock changes; generate the markup from real stock |
| Missing or invalid identifiers | Wrong check digit, restricted prefix, one GTIN copied across variants, identifier_exists=no on a product that has a GTIN | Confirm GTINs with the supplier; otherwise send brand and mpn, or identifier_exists=no |
| Image issues | Image too small, promotional text or watermark, placeholder image, image server blocking Google's crawlers | Use clean product shots; allow Googlebot-Image in CDN and WAF rules |
| Landing page not crawlable | robots.txt block, login wall, geo-blocking of foreign IPs, bot protection, 404, or sold-out products redirected to the home page | Do not block Googlebot or Storebot-Google; AdsBot-Google ignores the global * group, so make sure it is not blocked by name; exempt Google's crawlers from country blocks |
| Misrepresentation or website requirements | Missing contact or return information, hidden costs, offers you cannot fulfil | Fill the gaps, then request a review in Merchant Center |
| Policy restriction | Categories restricted by country (alcohol, prescription drugs, weapons and the like) | Read the Shopping policies; where needed, remove the product from that surface with excluded_destination |
| Low catalog match on Meta | Pixel events carrying IDs that are not in the catalog (parent codes, database row numbers) | Put the feed's id in content_ids and connect the pixel to the catalog |
Frequently asked questions
Do I need GTINs?If the manufacturer assigned one, yes: leaving it out limits visibility and can get the product disapproved. For a private label sold only by you and never assigned a GTIN, brand and mpn are enough; for handmade and custom goods, use identifier_exists=no.
XML or Google Sheets?
Sheets is practical for a catalog of a few hundred products maintained by hand. If the products live in a database, generate XML or delimited text from code; that removes manual edits and version confusion.
How often should the feed refresh?
Google expires products 30 days after their last refresh; treat that as the outer limit, not the goal, and rebuild the feed at least once a day. If prices or stock change during the day, push the changed products through the API right away; on Meta, a scheduled feed can also run hourly.
Why was my product disapproved for price mismatch?
The price Google saw on the landing page differs from the one in your feed. Open the page signed out and from outside Turkey (through a VPN, for example), then check both the visible price and the price value in the structured data. VAT, currency conversion, caching and the default variant are the usual suspects.
Do I have to migrate to Merchant API?
Yes, if you have an integration that calls the Content API for Shopping directly. Before using Merchant API v1, register your Google Cloud project once with the registerGcp method; products are then written with productInputs.insert into a data source of the API type. If you use files, scheduled fetches or Google Sheets, there is nothing to do.
Conclusion and next steps
A dependable feed rests on three things: one source of data, exact consistency with the landing page, and correct identifiers. A sensible order of work: group the products Merchant Center flags by disapproval reason and start with the issue that affects the most items; generate id, price and availability from a single source; align the structured data with the feed; match the product IDs in your Meta Pixel events to the catalog; and if you still call the Content API, finish the move to Merchant API v1. If you want help setting up the feed pipeline and campaign measurement together, have a look at AIOR's marketing and SEO services. Questions about your own feed are welcome in this thread.
Last edited: