# Product feeds — Google Merchant Center and Meta

> A store built on `Osysharp.Shop` serves its catalogue as product feeds: `/feeds/google.xml` for Google Merchant Center and `/feeds/meta.xml` for Meta's catalogue, both RSS 2.0 in Google's `g:` namespace. One item per size or colour, only listed products, generated from the store's rows every time a channel fetches it.

<!-- id: api-product-feeds · area: api · stability: preview · html: https://osysharp.com/reference/api/product-feeds/ -->

## Summary        {#summary}
A shopping channel lists your products from a **feed**: a file it fetches from your store on a schedule. A store built
on `Osysharp.Shop` serves two, the way it serves `/sitemap.xml` (see [[Serve]](https://osysharp.com/reference/api/serve/)):

| Address | For |
|---|---|
| `/feeds/google.xml` | Google Merchant Center (Shopping ads, free listings) |
| `/feeds/meta.xml` | Meta's catalogue (Facebook and Instagram shops, dynamic ads) |

There is nothing to export and nothing to keep in step. Each fetch is written from the store's rows as they are.
Register the address in the channel as a scheduled fetch.

## Signature      {#signature}
```osy syntax
[Serve("/feeds/google.xml")] string GoogleProductFeed()
[Serve("/feeds/meta.xml")]   string MetaProductFeed()
string ProductFeed(FeedChannel channel)       // enum FeedChannel { Google, Meta }
Category.GoogleCategory                        // a shelf's place in Google's taxonomy, inherited by its children
```

## Description    {#description}

### What an item carries   {#fields}
One item per **variant**: a customer buys a size or a colour, and a channel shows and prices exactly that. The
variants of one product share `item_group_id`; a product with a single variant is a plain item.

| Field | From |
|---|---|
| `id` | the variant's SKU, else its own id |
| `title` | the product's name and the variant's options: "Loafer (38)" |
| `description` | the product's summary, else the start of its description |
| `link` | the product's page (`ShopSetup.ProductPath`), absolute |
| `image_link`, `additional_image_link` | its photographs, in order |
| `availability` | in stock, or out of stock. A sold-out size stays in the feed and says so |
| `condition` | `new` |
| `price`, `sale_price` | "165.00 SEK". A markdown shows the regular price as `price` and today's as `sale_price` |
| `brand` | the product's brand |
| `mpn` | the SKU |
| `size`, `color` | the variant's options |
| `product_type` | the shelf path: "Coffee > Kenya" |
| `google_product_category` | `Category.GoogleCategory`, from the shelf or the nearest one above it |
| `shipping_weight` | the variant's packed weight, when known: "250 g" |

Only **listed** products are in a feed, and only those with a price and at least one photograph. Both channels
refuse an item without an image. A draft is never listed. Every value is XML-escaped, so a product called
`Tan & Co <Limited>` arrives as text.

### Where the two channels differ   {#differences}
Both channels read the same format: RSS 2.0 with the `g:` namespace
([Google](https://support.google.com/merchants/answer/160589),
[Meta](https://developers.facebook.com/docs/marketing-api/catalog/reference/)). They differ in three places, which is
why there are two addresses:

| | Google | Meta |
|---|---|---|
| availability | `in_stock` / `out_of_stock` | `in stock` / `out of stock` |
| brand | optional. Without one, or without a SKU, the item says `identifier_exists` `no` | required. A product without one gets the shop's own name |
| extra photographs | up to 10 | up to 20 |

### Placing shelves in Google's taxonomy   {#taxonomy}
Google sorts products by its own [product taxonomy](https://support.google.com/merchants/answer/6324436). Set a
shelf's `GoogleCategory` to the taxonomy's number ("187") or its path ("Apparel & Accessories > Shoes"). Every
product on that shelf and on the shelves under it is sent with it. Staff set it with the rest of the shelf.

## Examples       {#examples}

### Placing a shelf in Google's taxonomy from the desk   {#example-taxonomy}
```osy title="a staff action that places a shelf, and so every product under it" test app=api-product-feeds
app FeedsExample {
  use Osysharp.Shop@1 { egress "api.resend.com"; secret "ResendApiKey"; }
  model "model/**/*.osy";
}

using Osysharp.Shop;

/// Puts a shelf in Google's taxonomy. Its products, and those on the shelves under it, are sent with it.
[Authorize(IsShopStaff)]
public void PlaceShelf(Category shelf, string taxonomy) {
  shelf.GoogleCategory = taxonomy;
  UnitOfWork.Commit();
}
```

### One item, as Google reads it   {#example-item}
```xml syntax
<item>
  <g:id>GG-KIAMBU-AA-1000</g:id>
  <g:title>Kiambu AA (1 kg)</g:title>
  <g:description>The product’s summary, as the shop wrote it.</g:description>
  <g:link>https://groundsandgrapes.example/p/kiambu-aa</g:link>
  <g:image_link>https://groundsandgrapes.example/files/products/kiambu-aa.jpg</g:image_link>
  <g:availability>in_stock</g:availability>
  <g:condition>new</g:condition>
  <g:price>560.00 SEK</g:price>
  <g:brand>Grounds &amp; Grapes</g:brand>
  <g:mpn>GG-KIAMBU-AA-1000</g:mpn>
  <g:item_group_id>4b0c…</g:item_group_id>
  <g:size>1 kg</g:size>
  <g:product_type>Coffee</g:product_type>
</item>
```

## See also       {#see-also}
- [[Serve]](https://osysharp.com/reference/api/serve/) — `[Serve]`, which puts these at their addresses, beside `/sitemap.xml` and `/robots.txt`.
- [The SEO kit — schema.org structured data for a page's head](https://osysharp.com/reference/ui/seo-kit/) — what a product page's head tells a search engine.
- [The analytics kit — Plausible or Google Analytics 4, with consent](https://osysharp.com/reference/ui/analytics-kit/) — counting the visits and sales the channels send.
