Facebook and Instagram shop catalogue feed from your own Shopify or WooCommerce store: one Google Sheet Meta Commerce Manager reads on a schedule, with a check for rows Meta will drop
What you get: one Google Sheet that Meta Commerce Manager can read as a scheduled catalogue feed for your Facebook and Instagram shop and catalogue ads. The feed tab has one row per product from your own web shop with the exact column names Meta asks for: id, title, description, availability, condition, price, sale_price, link, image_link, brand, gtin and google_product_category. A separate checks tab flags, in plain English, the rows Meta is likely to drop or show badly: duplicate id (Meta ignores every copy), no image or an image that is not JPEG or PNG, no description, description same as title, title over 65 characters, price missing, and no category. Re-import your products before Black Friday and the feed updates on Meta's next scheduled fetch. Formulas are below. The example rows are made up.
The finished sheet
feed tab (example rows are made up; shop currency GBP)
| id | title | availability | condition | price | sale_price | link | image_link | brand |
|---|---|---|---|---|---|---|---|---|
| CAN-FIG-200 | Hand-poured soy candle, fig | in stock | new | 24.00 GBP | 19.20 GBP | https://your-shop.example/products/fig-candle | https://cdn.your-shop.example/fig.jpg | Your Brand |
| TT-SAGE | Linen tea towel, sage | in stock | new | 18.00 GBP | https://your-shop.example/products/sage-tea-towel | https://cdn.your-shop.example/sage.webp | Your Brand | |
| MUG-SPK | Ceramic mug, speckled | out of stock | new | 21.60 GBP | https://your-shop.example/products/speckled-mug | Your Brand |
checks tab for the same rows
| id | Problems | Ready? |
|---|---|---|
| CAN-FIG-200 | Yes | |
| TT-SAGE | Image not JPEG or PNG | Fix |
| MUG-SPK | No image · No category | Fix |
The candle row shows how a sale is handled: the shop sells it at 19.20 with a compare-at (normal) price of 24.00, so price is the normal price and sale_price the lower one, which is how Meta expects a discount to be sent.
Who this is for, and when you need it
Small brands selling on their own Shopify or WooCommerce shop who want their products in a Meta catalogue for an Instagram or Facebook shop or for catalogue ads. Shopify's Facebook & Instagram sales channel and the Facebook for WooCommerce plugin can sync products for you, and if that sync works for you, use it. This sheet is for when it doesn't: the plugin keeps failing, you want to choose exactly which products go in, you want to fix titles and categories for Meta without changing your shop, or you sell from a shop the plugins don't cover. Only use it for your own products: Meta requires each link to be a product page on your business's own website.
Step 1: get your products into a tab named scrape
With the product data scraper
Open the E-commerce Product Data Scraper (Shopify, WooCommerce, any shop) on the Apify Store and use this input with your own shop address:
{
"startUrls": [{ "url": "https://your-shop.example" }],
"maxProductsPerShop": 2000,
"includeDescription": true
}
maxProductsPerShop: set it above your number of products (the default is 100). You pay only for products delivered.includeDescription: keep it on. Meta requires a description, and the scraper gives it as plain text, which is what Meta'sdescriptionfield wants.- The scraper follows robots.txt and reads your shop's terms page. If your own terms forbid automated access (Shopify's default terms do), it skips the shop and charges nothing. Check the run's Storage → Key-value store →
SHOPS_SUMMARY: your shop should showok. If it showsblocked-by-terms, use your admin export below.
In the run, export the dataset as CSV. In your Google Sheet use File → Import → Upload, "Insert new sheet", and name the tab scrape. Do not rename headers. You get one row per product with columns such as url, productId, sku, gtin, title, brand, description, price, compareAtPrice, currency, inStock, category and images/0. For Shopify shops, brand is the product's vendor, category is the product type, and price, compareAtPrice, sku and gtin (the barcode) come from the cheapest variant. For WooCommerce shops, compareAtPrice is the regular price, category is the product's categories joined by commas, and brand is usually empty, so the sheet uses the brand you type in settings.
Free, from your own shop admin
- Shopify: Products → Export gives one row per variant with columns such as Handle, Title, Body (HTML), Vendor, Type, Variant SKU, Variant Price, Variant Compare At Price, Variant Barcode and Image Src. Build the link yourself as your shop address +
/products/+ Handle. Body (HTML) contains HTML; Meta wants plain text indescription. - WooCommerce: the built-in product CSV exporter (Products → Export) gives name, SKU, description, categories, regular price, sale price, stock and image columns.
The formulas below look columns up by header name, so with an admin export you change only the header names in quotes.
Step 2: the settings tab
| Cell | What to type | Example |
|---|---|---|
| B1 | Brand name, used when a product has none | Your Brand |
| B2 | Shop currency as a 3-letter ISO code, used when a row has none | GBP |
| B3 | Condition for all products | new |
Then add a tab named categories with your shop categories in column A (one per row, as they appear in the category column) and, in column B, the matching Google product category name or ID number, for example Home & Garden > Decor > Home Fragrances > Candles. Meta asks for either google_product_category or fb_product_category; you map each shop category once and every product in it gets it.
Step 3: build the feed tab
Add a tab named feed and drag it to be the first tab, so it is what opens when Meta fetches the link. Row 1 holds the header names exactly as shown (lower case, US English); row 2 holds the formulas, which you copy down one row per row of scrape. Every formula uses the same lookup, shown in full for title:
=IFERROR(INDEX(scrape!$A2:$ZZ2, MATCH("title", scrape!$A$1:$ZZ$1, 0))&"", "")
Below, S("name") is short for that formula with "name" in place of "title"; type the full formula in the sheet.
| Col | Header | Formula (row 2) |
|---|---|---|
| A | id | =IF(S("title")="", "", IF(S("sku")<>"", S("sku"), S("productId"))) |
| B | title | =LEFT(S("title"), 200) |
| C | description | =LEFT(S("description"), 9999) |
| D | availability | =IF(A2="", "", IF(UPPER(S("inStock"))="TRUE", "in stock", "out of stock")) |
| E | condition | =IF(A2="", "", settings!$B$3) |
| F | price | =IF(A2="", "", LET(p, N(VALUE("0"&S("price"))), c, N(VALUE("0"&S("compareAtPrice"))), cur, IF(S("currency")="", settings!$B$2, S("currency")), IF(MAX(p, c)=0, "", TEXT(MAX(p, c), "0.00")&" "&cur))) |
| G | sale_price | =IF(A2="", "", LET(p, N(VALUE("0"&S("price"))), c, N(VALUE("0"&S("compareAtPrice"))), cur, IF(S("currency")="", settings!$B$2, S("currency")), IF(AND(p>0, c>p), TEXT(p, "0.00")&" "&cur, ""))) |
| H | link | =S("url") |
| I | image_link | =S("images/0") |
| J | brand | =IF(A2="", "", IF(S("brand")="", settings!$B$1, LEFT(S("brand"), 100))) |
| K | gtin | =REGEXREPLACE(S("gtin"), "[^0-9]", "") |
| L | google_product_category | =IFERROR(VLOOKUP(S("category"), categories!$A:$B, 2, FALSE), "") |
Notes on the formulas:
- id: Meta asks for a unique content ID and suggests the SKU. Products without a SKU fall back to the shop's product ID. If you run a Meta Pixel with catalogue ads, the id must match the content ID your pixel sends, so check which one your shop's pixel uses before you switch feeds.
- price and sale_price: Meta wants a number with a full stop as the decimal point, a space, then the ISO currency code, with no currency symbol, for example
24.00 GBP.pricetakes the larger of price and compare-at price;sale_priceis filled only when the product is really on sale, because Meta requires the sale price to be lower than the full price. If your sale ends, re-import the scrape tab so the sale price disappears. - Products with variants: the scraper gives one row per product, priced at its cheapest variant. If your sizes or colours have different prices, or your shop sells clothing or shoes (Meta asks for
size,coloranditem_group_idthere), build the feed from your admin export instead, one row per variant, with the parent product's handle in an extraitem_group_idcolumn. - gtin: Meta asks you to send a GTIN only if you are sure it is correct, with no dashes or spaces. Leave the column empty if your barcodes are your own internal codes.
- Keep the feed tab clean: only the Meta columns go on it. Notes, checks and helper columns go on other tabs.
Step 4: the checks tab
Add a tab named checks. In A2 put =feed!A2; in B2 the problem list; in C2 the verdict. Copy both down as far as the feed goes.
B2: =IF(A2="", "", TEXTJOIN(" · ", TRUE,
IF(COUNTIF(feed!$A:$A, A2)>1, "Duplicate id", ""),
IF(feed!I2="", "No image", IF(REGEXMATCH(LOWER(feed!I2), "\.(jpe?g|png)(\?|$)"), "", "Image not JPEG or PNG")),
IF(feed!C2="", "No description", IF(LOWER(TRIM(feed!C2))=LOWER(TRIM(feed!B2)), "Description same as title", "")),
IF(EXACT(feed!B2, UPPER(feed!B2))*REGEXMATCH(feed!B2, "[A-Z]"), "Title in capital letters", ""),
IF(LEN(feed!B2)>65, "Title over 65 characters", ""),
IF(feed!F2="", "Price missing", ""),
IF(feed!L2="", "No category", "")))
C2: =IF(A2="", "", IF(B2="", "Yes", IF(B2="Title over 65 characters", "OK (title may be cut)", "Fix")))
- Duplicate id: Meta says that if the same ID appears more than once, it ignores every instance, so both products vanish. Give one of them its own SKU in your shop.
- Image not JPEG or PNG: Meta's image rules ask for JPEG or PNG, at least 500 × 500 pixels. Many shop themes serve WebP; upload a JPEG or PNG as the product's first image. The check reads only the file ending, so also open one image per product type to confirm the size.
- Title over 65 characters: allowed (the limit is 200), but Meta recommends 65 or fewer so the title is not cut off.
- No category: add the shop category to the categories tab.
Fix problems in your shop (or in the scrape tab, if you don't want to change your shop), then re-import. Add a filter on the checks tab and work through every "Fix" row before the first upload.
Step 5: connect the sheet to Commerce Manager
- In Google Sheets, Share → General access → Anyone with the link (Viewer). Meta's help says the link must not be set to Restricted. Anyone with the link can see your product data, so keep cost prices and private notes out of this spreadsheet.
- In Commerce Manager, open your catalogue → Catalogue → Data sources → Add items → Data feed, choose Google Sheets and paste the sheet's link.
- Choose a schedule (hourly, daily or weekly). The default replace setting deletes from the catalogue any product you removed from the sheet; update only adds and changes products.
- After the first fetch, open the data source's issues list and compare it with your checks tab. Meta's own diagnostics are the final word.
- Before each sale or season, re-import the scrape tab. The feed and checks recalculate and Meta picks up the change on the next scheduled fetch.
Partner programme opening soon
Partner programme opening soon: join the free waitlist. Planned terms are 50% of each referred sale (75% once you have 5+ paying customers), single level, paid through Gumroad only from revenue received.
Sources
- Meta catalogue fields (required
id,title,description,availability,condition,price,link,image_link, plus one ofbrand,gtinormpn; duplicate IDs ignored; title limit 200, 65 recommended; price format; sale price lower than price; JPEG or PNG images of at least 500 × 500; Google or Facebook product category; Google Sheets as a feed format): Meta for Developers, Catalog reference (page updated 5 Oct 2026, read 22 Oct 2026). - Scheduled feeds (hourly, daily or weekly; Google Sheets supported; link sharing set to Anyone with the link, not Restricted; replace and update settings): Meta Business Help Centre, About scheduled data feed uploads for catalogues (read 22 Oct 2026).
- Product data scraper inputs and outputs (
startUrls,maxProductsPerShopdefault 100,includeDescriptionplain text,SHOPS_SUMMARYstatuses, Shopify vendor as brand, product type as category, price, compare-at price, SKU and barcode from the cheapest variant, WooCommerce regular price and categories, skipped shops not charged): Apify Store page (Actor input schema, README and source read 22 Oct 2026). - Menu names in Commerce Manager, Shopify and WooCommerce change from time to time; if a step looks different, search the help centre of that service for "data feed" or "export products".
Related
- Google Shopping product listing check sheet
- Wholesale line sheet from your own Shopify or WooCommerce store
- Export Shopify store products to CSV
- Export WooCommerce store products to CSV
Unadhi Guides are free, independent explainers. We correct errors as soon as we find them: write to mininglife7@gmail.com.