Skip to content
Back to all articles
Shopify Multichannel
Shopify Inventory Sync
Amazon Seller Central
Google Merchant Center
Python

Shopify Multichannel: Syncing 7+ Sales Channels Without Breaking the Catalog

Replacing an 18MB Excel macro with a real inventory pipeline — Amazon Seller and Vendor Central, Google Merchant Center, dropship networks, and why every channel has a different key column.

August 18, 2026 13 min readBy Matheus Abrahão

The real multichannel problem

Everyone thinks the multichannel problem is listing products on more channels. It is not. Listing is a one-time cost.

The problem is that once a product is on eight channels, the quantity number has to be right on all eight, every day, forever — and every channel wants it in a different file format, keyed on a different identifier, in a different column.

I run this daily for a US building-products manufacturer: two Shopify storefronts, three retailer dropship feeds (two big-box US chains and one Canadian arm), Amazon Vendor Central and Amazon Seller Central, plus product-data syndication to six retail partners. Around 3,000 SKUs on the inventory side, 13,000+ variants on the consumer store.

This post is about the engineering of keeping that in sync.

What it looked like before

An 18 MB single-user Excel workbook with roughly 48 worksheet tabs, macro-driven, living on a virtual desktop. It computed sellable quantity per channel and emitted seven daily files. A human then uploaded each file to its destination by hand.

Every failure mode you would predict:

  • File lock. One person at a time. If they are on holiday, inventory does not sync.
  • No version control. The formula that broke last Tuesday is unrecoverable.
  • No schedule. It runs when someone remembers.
  • No audit trail. "Why did this SKU go to zero?" is unanswerable.

Worth saying: it worked. It ran a real business for years. The point of replacing it is not that it was stupid — it is that its correctness lived in one person's head.

Every channel has a different key and a different quantity column

This is the detail that makes multichannel sync annoying rather than trivial. The seven daily outputs:

  • Shopify ×2 — keyed on Variant ID, quantity in Variant Inventory Qty, XLSX, sheet must be named Products.
  • Three retailer dropship templates — keyed on VENDOR SKU, quantity in QTY, legacy .xls, three instruction rows above the header (the real header is on row 4).
  • Amazon Seller — keyed on item_sku, quantity in quantity, plus update_delete = PartialUpdate, XLSX with three reserved metadata rows.
  • Amazon Vendor — keyed on SKU, quantity in Available units, CSV, latin-1 encoding.

Seven files, seven key columns, four file formats, one encoding gotcha. Any generic "sync tool" that promises to handle this without configuration is lying to you about at least three of them.

The pipeline that replaced it

Shape: INGEST → SQLite → TRANSFORM → OUTPUT.

Six tables replaced 48 tabs:

  • products — the SKU master
  • channel_membership — which SKUs are offered on which channel
  • inventory — quantity on hand
  • oos_status — out-of-stock flags
  • overrides — force-in-stock and manual adjustments
  • exceptions — SKUs suppressed on specific channels

Plus one computed view, channel_state.

The core rule set is deliberately tiny, and keeping it tiny is the whole design:

  • A SKU is offered on a channel only if it is a member and not in exceptions.
  • sellable_qty starts at qty_on_hand.
  • OOS and not force-in-stock → 0.
  • Force-in-stock → keep on-hand regardless of OOS.

Four rules. When someone reports a wrong quantity, you can hold all four in your head while you debug.

SELECT p.sku,
       CASE
         WHEN o.force_in_stock = 1 THEN i.qty_on_hand
         WHEN s.is_oos = 1        THEN 0
         ELSE i.qty_on_hand
       END AS sellable_qty
FROM channel_membership m
JOIN products  p ON p.sku = m.sku
JOIN inventory i ON i.sku = m.sku
LEFT JOIN oos_status s ON s.sku = m.sku
LEFT JOIN overrides  o ON o.sku = m.sku AND o.channel = m.channel
WHERE m.channel = ?
  AND NOT EXISTS (
    SELECT 1 FROM exceptions e
    WHERE e.sku = m.sku AND e.channel = m.channel
  );

The output trick: fill the template, do not rebuild it

This is the part I would most recommend copying.

The obvious approach is to generate each channel's file from scratch. The better approach: open the channel's real template file and update only the quantity column, keyed on that channel's own key, preserving everything else byte-for-byte.

Why it matters — the templates carry things you do not want to reimplement and cannot afford to lose: retailer instruction rows, Amazon's TemplateType=fptcustom metadata block, column ordering the parser depends on, and thousands of rows of static product data.

Verification was straightforward: run both systems on the same day and diff the outputs against the macro's own files. 16,654 rows preserved on the largest file, instruction rows intact, Amazon metadata block untouched.

Migration safety: shadow mode, then per-channel cutover

The old system stays the source of truth until each channel is individually proven.

  • Run the new engine alongside the macro, on the same schedule.
  • Compute what it would push. Do not push it.
  • Diff against what the macro actually pushed. Log every divergence.
  • After several consecutive days of matching, flip one channel to live.
  • Repeat per channel.

This pattern — parallel run, then retire — shows up three separate times in this operation, and it has never once been the wrong call.

Being honest about what is not ported yet

The POC deliberately does not implement: reserved inventory, fuzzy SKU matching, old-SKU to new-SKU reassignment during a product-line transition, and the components gap check (do not sell a shower pan whose drain plate is out of stock).

Those four contain most of the remaining institutional knowledge. Naming them explicitly is what keeps a "we replaced the spreadsheet" claim honest — the spreadsheet still knows things the pipeline does not.

Stack choices, and what I refused to use

Python plus SQLite. openpyxl for XLSX, xlrd/xlutils/xlwt for the legacy .xls formats. Cron via a CI scheduler. Metabase or Streamlit for the dashboard. A chat webhook for alerts.

No Airflow. No Kubernetes. No Kafka. For 3,000 SKUs and seven daily files, that infrastructure is a second job, not a solution. SQLite goes to Postgres the day there is a second concurrent writer, and not a day before.

The Amazon feed that broke, and how it was fixed

A real incident worth generalising.

The daily Amazon Seller inventory upload started failing because Amazon retired the Listing Loader template the export targeted. The replacement demanded a Product Type on every SKU — which the daily feed has no business carrying, because it only pushes quantity and handling time.

The fix: switch the output to Amazon's still-supported Price & Quantity flat file, which requires no Product Type. All business logic stayed where it was; a thin formatting tab and an export macro were added on top.

And the part that matters: the old export was left running in parallel for a week as a safety net before retirement, with the change written into a change log.

The general rule — when a channel changes its spec under you, change the format layer, never the logic layer. If your quantity calculation has to be rewritten because Amazon changed a template, your architecture has a seam in the wrong place.

The target end state

Every one of the six manual uploads has an API equivalent:

  • Shopify — Admin API inventorySetQuantities, or a scheduled import-from-URL.
  • The dropship network — V3 REST API, OAuth bearer tokens, with a staging host to test against.
  • Amazon Vendor — SP-API Vendor Direct Fulfillment Inventory.
  • Amazon Seller — SP-API Feeds and Listings.

Proof order: one channel first, easiest auth first. Not all six at once, and not the hardest one first to "prove it works."

Google Merchant Center and the tag sprawl problem

One multichannel side effect that is almost universal: sales channel apps inject their own analytics tags.

The Google & YouTube channel installs a Merchant Center tag. If you also run GTM, and someone previously hardcoded gtag, you now have three. On the store I audited, that contributed to seven distinct Google tag loads on a single page — over a megabyte of transfer and ~1.7 seconds of CPU.

So when you add a channel, audit the storefront afterwards. The channel does more to your site than list your products on it.

Detecting the silent multichannel failure

Products flipped to Active with the sales channel never added. Active, in stock, and unbuyable. First audit: 164 across two stores, 35 with inventory, one with 5,469 units.

The catch: a CSV export's Published column reflects only the Online Store channel. A real "published to N of 8 channels" audit needs the Admin API publications query. If you syndicate widely, the CSV is telling you a comfortable half-truth.

The short version

  • Every channel has its own key column and file format. Design for that, do not fight it.
  • Keep the sellable-quantity rules small enough to reason about.
  • Fill the real template rather than generating files from scratch.
  • Shadow-run before cutover. Cut over one channel at a time.
  • Separate business logic from output formatting, because channels change their formats and your logic should not care.
  • Audit which channels each product is actually published to, with the API, not the CSV.

I build and run multichannel inventory and catalog pipelines across Shopify, Amazon, Google and national retail partners. See [Shopify development services](/shopify-expert) or [hire a Shopify developer](/hire-shopify-developer). Related reading: [the Matrixify field guide](/blog/matrixify-shopify-bulk-operations-guide) and [retail syndication to Lowe's, Home Depot and Menards](/blog/retail-syndication-salsify-lowes-home-depot).

Direct: [WhatsApp +55 11 98851-2788](https://wa.me/5511988512788) · [contato.matheusabrahao@gmail.com](mailto:contato.matheusabrahao@gmail.com)

Need a senior engineer who thinks like an operator?

I take on a small number of Shopify operations and senior engineering engagements each quarter. If your store needs catalog hygiene, technical SEO, performance, or marketing automation done right — let's talk.

Continue reading

Vamos conversar