Unadhi Guides

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

Updated 22 Oct 2026 · Disclosure: the product data scraper linked below is an Unadhi product on the Apify Store. This guide is free, and the sheet also works with the free product export from your own Shopify or WooCommerce admin. Current Apify pricing is only on the Store page. This page has no affiliate links and nobody paid to be mentioned. Unadhi is not connected to Meta.

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)

idtitleavailabilityconditionpricesale_pricelinkimage_linkbrand
CAN-FIG-200Hand-poured soy candle, figin stocknew24.00 GBP19.20 GBPhttps://your-shop.example/products/fig-candlehttps://cdn.your-shop.example/fig.jpgYour Brand
TT-SAGELinen tea towel, sagein stocknew18.00 GBPhttps://your-shop.example/products/sage-tea-towelhttps://cdn.your-shop.example/sage.webpYour Brand
MUG-SPKCeramic mug, speckledout of stocknew21.60 GBPhttps://your-shop.example/products/speckled-mugYour Brand

checks tab for the same rows

idProblemsReady?
CAN-FIG-200Yes
TT-SAGEImage not JPEG or PNGFix
MUG-SPKNo image · No categoryFix

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
}

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

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

CellWhat to typeExample
B1Brand name, used when a product has noneYour Brand
B2Shop currency as a 3-letter ISO code, used when a row has noneGBP
B3Condition for all productsnew

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.

ColHeaderFormula (row 2)
Aid=IF(S("title")="", "", IF(S("sku")<>"", S("sku"), S("productId")))
Btitle=LEFT(S("title"), 200)
Cdescription=LEFT(S("description"), 9999)
Davailability=IF(A2="", "", IF(UPPER(S("inStock"))="TRUE", "in stock", "out of stock"))
Econdition=IF(A2="", "", settings!$B$3)
Fprice=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)))
Gsale_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, "")))
Hlink=S("url")
Iimage_link=S("images/0")
Jbrand=IF(A2="", "", IF(S("brand")="", settings!$B$1, LEFT(S("brand"), 100)))
Kgtin=REGEXREPLACE(S("gtin"), "[^0-9]", "")
Lgoogle_product_category=IFERROR(VLOOKUP(S("category"), categories!$A:$B, 2, FALSE), "")

Notes on the formulas:

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")))

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

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

Related

← All guides

Unadhi Guides are free, independent explainers. We correct errors as soon as we find them: write to mininglife7@gmail.com.