The same SKU listed on ten marketplaces will oversell the moment two of them sell the last unit before the others hear about it. Here's the small set of rules that keeps stock counts honest across channels.
By Rehan Idrisi · · Updated · 7 min read
Part of: Logistics & Shipping, Automation
We've built and maintained the sync layer between a single inventory source and dozens of marketplaces and web shops. Listing products and pulling orders are the easy parts. The feature that breaks first, every time, is keeping the stock number identical everywhere while several channels are selling the same unit at once.
You have one physical quantity for a SKU, listed on, say, ten channels. Channel A sells the last unit. Now "0" has to reach the other nine. That push takes seconds to minutes: an API call per channel, subject to that channel's rate limits, queued behind everything else you're syncing. Channel B can sell the same unit inside that window. You're oversold, and every oversold order is a cancellation, a refund, and a metric the marketplace holds against your account.
There is exactly one authoritative quantity per SKU and it lives in your system, never in a channel. Channels are downstream copies. An order from any channel decrements the source, and the source then fans the new number out to every channel. If a warehouse tool, a spreadsheet import and three marketplaces can all write stock directly, you have five sources of truth and no way to reconcile them.
Don't list your full quantity. Keep a small buffer unlisted: a fixed number for low-stock items, a percentage for fast movers. It absorbs the sync lag. The buffer sells instead of the count going negative, and you top the listed number back up on the next push. You trade a little visible availability for far fewer cancellations.
Stock updates arrive out of order under retries and parallel workers. Attach a monotonic version or timestamp to each change, and have the channel-push layer refuse anything older than what it last sent. Skip this and a delayed "quantity 5" lands after "quantity 0" and re-opens a sold-out listing.
However careful the real-time path is, a missed webhook or a failed push eventually leaves a channel showing the wrong number. Run a full sweep on a schedule, every SKU against every channel, source value against listed value: hourly for a busy catalogue, nightly for a slow one. Log every correction. A rising correction count is your early warning that something in the real-time path has broken.
The real-time sync gets the credit, but it's the scheduled reconciliation sweep that catches the errors nobody noticed.
Everything above assumes each channel's listing maps correctly to one internal SKU. Mismatched or duplicated mappings are the other large source of stock errors. Validate the mapping at the moment a listing is connected, checking for a consistent SKU format and a matching barcode (GTIN, EAN or UPC) where one exists. Months later, when the numbers don't add up, is a much worse time to find out.
The Product CSV Validator flags duplicate SKUs and bad barcodes in an export before you import it. The SKU Generator keeps new codes in one consistent pattern, and the GTIN Converter checks and normalises barcodes across formats.
A push can reach a marketplace's API successfully and still not show on the live listing. Some channels cache listing data on their own side and refresh it on their own schedule, whatever speed your update arrived at. That lag is invisible from where you're standing: the API call succeeds, your logs show the push as delivered, and the listing stays stale for a window you can neither control nor reliably query. Where a channel exposes a 'confirm applied' status separate from 'request accepted', we check it. Where it doesn't, the reconciliation sweep is the only thing that catches the gap.
A bundle listed as its own SKU has no physical stock of its own. It's built from component SKUs, and two different bundles can draw on the same component. Decrementing the bundle's own counter on a sale isn't enough. The sale has to decrement every component it consumes, and every other bundle or standalone listing built from that same component has to see the reduced number too. We resolve a bundle sale down to its components before it touches the inventory ledger at all, so a component running low never gets oversold merely because the depletion happened through a bundle instead of a direct sale of that SKU.
Once stock lives in more than one physical location, 'the quantity for this SKU' stops being a single number. It becomes a number per location, and what you expose to a channel depends on whether that channel fulfils from a specific warehouse or from a combined pool. We keep per-location quantities in the source system and compute each channel's pushed number from the rule that channel needs: a combined total where it can fulfil from anywhere, a single location's count where it can't. Flattening everything to one number loses information a fulfilment decision will want later.
A silent drop in push success rate to one channel won't show up in a spot-check of individual SKUs. Neither will a queue quietly growing because that channel started rate-limiting harder than usual. Both show up as a trend in the sync layer's own metrics. We track push latency and failure rate per channel and alert on a sustained rise in either, kept separate from the reconciliation sweep's correction count, so a channel-side problem gets caught as an operational issue before it becomes a run of oversold orders on that specific channel.
Two channels selling the last unit inside the same propagation window is the failure mode that matters, and it's the kind of timing bug that slips through normal testing while being easy to reproduce on purpose. We simulate concurrent orders against the same low-stock SKU from multiple channels in a staging environment, then confirm that the buffer, the version check and the zero-out priority behave the way the design assumes under real concurrency. One order arriving at a time proves nothing.
Keep one authoritative stock quantity in your own system, decrement it on every channel's orders, and fan the new number out to all channels. Hold back a small unlisted buffer to absorb sync lag, push decreases and zero-outs ahead of routine changes, version updates so stale ones can't apply, and run a scheduled sweep to correct drift.
Generate consistent SKU codes in bulk from a custom pattern, with variant permutations, sequential numbering and collision checking. Free, no sign-up.
Check a product CSV for missing columns, broken variant handles, duplicate SKUs, invalid barcodes and bad prices before you import it.
Convert product codes between GTIN-8, GTIN-12 (UPC-A), GTIN-13 (EAN-13) and GTIN-14, validate check digits, and normalise a whole catalog in bulk.
Turn a CSV or TSV table into JSON records, or flatten JSON back into a spreadsheet-ready table.