🚀 Executive Summary
TL;DR: Manually syncing WooCommerce products to Facebook Catalog is a high-effort, error-prone task. This guide provides a Python script to automate the process by fetching products from WooCommerce via its REST API and pushing them to the Facebook Catalog using the Facebook Graph API’s batch endpoint, saving significant time and reducing errors.
🎯 Key Takeaways
- Leverage the WooCommerce REST API to fetch product data, implementing pagination and optionally the
modified\_afterparameter for efficient updates. - Transform WooCommerce product data into Facebook’s specific format, utilizing the
retailer\_id(SKU or product ID) and preparing it for the Facebook Graph API’s batch endpoint for optimized performance. - Crucially, use a long-lived Facebook Graph API Access Token and securely manage all API credentials (e.g., via
config.envor a secrets manager) to prevent authentication failures and security risks. - Automate the sync process using a cron job to ensure regular and dependable updates without manual intervention.
Syncing WooCommerce Products to Facebook Catalog via API
Alright, let’s talk about a task that’s probably eaten up more of your time than you’d like to admit: keeping your Facebook product catalog in sync with your WooCommerce store. I used to spend my Monday mornings babysitting CSV uploads, wrestling with formatting errors, and triple-checking that the latest price changes were reflected. It was a classic high-effort, low-value task.
After one too many “upload failed” notifications, I decided to automate it properly. A simple Python script talking directly to the APIs saved me hours a week and removed a significant point of failure from our e-commerce pipeline. This is the kind of set-and-forget automation we live for in DevOps. Let’s build it.
Prerequisites
Before we dive in, make sure you have the following ready to go. This will save you a lot of back-and-forth later.
- A WooCommerce store with REST API enabled (you’ll need the Consumer Key and Consumer Secret).
- A Facebook Business Manager account with an existing, empty Catalog. You’ll need your Catalog ID.
- A Facebook App with
catalog_managementandbusiness_managementpermissions granted. - A valid, long-lived Facebook Graph API Access Token associated with that app. This is crucial for automation.
- A Python 3 development environment.
The Step-by-Step Guide
Step 1: Setting Up Your Project Environment
I’ll skip the standard virtualenv or conda setup since you likely have your own workflow for that. The key is to create an isolated environment for this project. Once you’re in your environment, you’ll need a couple of Python libraries. You can install them using pip: requests for making the API calls and python-dotenv for managing our credentials securely.
Step 2: Securely Storing Your Credentials
Never hardcode API keys or tokens in your script. It’s a security risk and a pain to manage. In my production setups, I use a proper secrets manager, but for this tutorial, a config.env file is a solid approach. Create a file named config.env in your project directory:
# config.env
# WooCommerce Credentials
WC_STORE_URL="https://your-store.com"
WC_CONSUMER_KEY="ck_xxxxxxxxxxxx"
WC_CONSUMER_SECRET="cs_xxxxxxxxxxxx"
# Facebook Credentials
FB_CATALOG_ID="1234567890123456"
FB_ACCESS_TOKEN="EAA...your-long-lived-token...ZD"
Step 3: The Script – Fetching Products from WooCommerce
First, we need to pull the product data. We’ll create a function that connects to the WooCommerce REST API and fetches all ‘simple’ and ‘variable’ products that are in stock. This code snippet handles pagination to make sure we get everything, not just the first page of results.
import os
import requests
import json
from dotenv import load_dotenv
def fetch_woocommerce_products(store_url, consumer_key, consumer_secret):
"""Fetches all published, in-stock products from WooCommerce."""
products = []
page = 1
per_page = 100 # Max per page is 100
while True:
api_url = f"{store_url}/wp-json/wc/v3/products"
params = {
'per_page': per_page,
'page': page,
'status': 'publish',
'stock_status': 'instock'
}
print(f"Fetching page {page} of products...")
response = requests.get(
api_url,
auth=(consumer_key, consumer_secret),
params=params
)
if response.status_code != 200:
print(f"Error fetching products: {response.text}")
break
data = response.json()
if not data:
# No more products left to fetch
break
products.extend(data)
page += 1
print(f"Successfully fetched {len(products)} products.")
return products
Pro Tip: For stores with thousands of products, pulling everything every time can be slow. A better approach is to store the timestamp of the last sync. Then, you can use the
modified\_afterparameter in your WooCommerce API call to only fetch products that have been added or updated since the last run. It’s a huge performance win.
Step 4: The Script – Formatting Data for Facebook
Facebook’s API is particular about data structure. We need to transform the raw WooCommerce data into a format that the Facebook Catalog API understands. We’ll use the product’s SKU as the unique retailer\_id. If you don’t use SKUs, the WooCommerce product ID is a good fallback.
We will prepare our data for Facebook’s Batch API, which is far more efficient than sending one request per product.
def format_for_facebook_batch(products):
"""Formats WooCommerce products for the Facebook Graph API batch update."""
batch_operations = []
for product in products:
# Use SKU as the primary identifier, fallback to ID if SKU is missing
retailer_id = product.get('sku') or str(product.get('id'))
if not retailer_id:
continue # Skip products without a usable ID
product_data = {
"method": "UPDATE", # Use UPDATE to create or modify
"retailer_id": retailer_id,
"data": {
"availability": "in stock",
"brand": "Your Brand Name", # You can make this dynamic
"condition": "new",
"description": product.get('short_description', 'No description available.'),
"image_url": product['images'][0]['src'] if product['images'] else '',
"link": product.get('permalink'),
"title": product.get('name'),
"price": f"{product.get('price')} USD", # Always include currency
"google_product_category": "Apparel & Accessories > Clothing" # Example, customize this
}
}
batch_operations.append(product_data)
return batch_operations
Step 5: The Script – Pushing to the Facebook Catalog
Now for the final piece. We take our formatted batch of products and send it to the Facebook Graph API. The request is a single POST to a specific endpoint, which is much kinder on their rate limits.
def push_to_facebook_catalog(catalog_id, access_token, batch_data):
"""Pushes a batch of products to the Facebook Catalog."""
api_url = f"https://graph.facebook.com/v18.0/{catalog_id}/batch"
# Facebook's batch API expects a JSON-encoded string for the 'requests' parameter
payload = {
'access_token': access_token,
'requests': json.dumps(batch_data),
'item_type': 'PRODUCT_ITEM'
}
print(f"Pushing {len(batch_data)} items to Facebook Catalog...")
response = requests.post(api_url, data=payload)
if response.status_code == 200:
print("Batch request sent successfully!")
print("Response:", response.json())
else:
print(f"Error pushing to Facebook: {response.status_code}")
print("Error details:", response.text)
return response.status_code
Step 6: Putting It All Together and Automating
Let’s create a main execution block that loads our credentials and calls our functions in order. This is the script you’ll eventually automate.
def main():
"""Main function to run the sync process."""
load_dotenv('config.env')
# Load WooCommerce credentials
wc_url = os.getenv('WC_STORE_URL')
wc_key = os.getenv('WC_CONSUMER_KEY')
wc_secret = os.getenv('WC_CONSUMER_SECRET')
# Load Facebook credentials
fb_catalog_id = os.getenv('FB_CATALOG_ID')
fb_token = os.getenv('FB_ACCESS_TOKEN')
if not all([wc_url, wc_key, wc_secret, fb_catalog_id, fb_token]):
print("One or more environment variables are missing. Check your config.env file.")
return
# 1. Fetch products from WooCommerce
wc_products = fetch_woocommerce_products(wc_url, wc_key, wc_secret)
if not wc_products:
print("No products found or an error occurred. Exiting.")
return
# 2. Format for Facebook
fb_batch = format_for_facebook_batch(wc_products)
# 3. Push to Facebook
if fb_batch:
push_to_facebook_catalog(fb_catalog_id, fb_token, fb_batch)
else:
print("No products to push.")
if __name__ == "__main__":
main()
To automate this, you can set up a simple cron job on a Linux server. Save the script as sync\_script.py. To run it every day at 2 AM, your crontab entry would look like this:
0 2 * * * python3 /path/to/your/project/sync_script.py
Just be sure to use the correct full path to your script.
Common Pitfalls (Here’s Where I Usually Mess Up)
- Access Token Expiration: A standard Facebook Graph API token expires in an hour. For a cron job, that’s a non-starter. You absolutely must generate a long-lived access token or, for a truly robust setup, a system user access token. The documentation is a bit dense, but it’s a one-time setup that prevents endless authentication failures.
- API Rate Limits: If you have a massive catalog, you might hit rate limits. The batch API helps a lot, but you may need to break your product list into smaller chunks (e.g., batches of 500) and send them sequentially with a small delay in between.
-
Mismatched IDs: The
retailer\_idis how Facebook knows which product to update. If you change from using the product ID to the SKU halfway through, you’ll end up with duplicate products in your catalog. Pick one and stick with it.
Conclusion
And that’s it. You now have a reliable, automated pipeline for syncing your products. This script is a solid foundation. You can expand it with better error handling, logging to a file, and notifications for failures. But even in this form, it turns a manual, error-prone task into a dependable background process. Now you can spend your time on more complex problems, which is what DevOps is all about.
Happy automating.
– Darian Vance
👉 Read the original article on TechResolve.blog
☕ Support my work
If this article helped you, you can buy me a coffee:

Top comments (0)