Marketplace integration

Keeping stock in sync across a dozen sales channels without overselling

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.

Why overselling happens

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.

Overselling is a timing problem, not a data problem. The stock number is correct; it just hasn't arrived everywhere yet.

One source of truth, and nothing writes around it

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.

Hold back a buffer

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.

Push low numbers first, and prioritise the risky ones

  • When stock drops, the update that prevents an oversell matters more than one changing 40 to 38. Queue decreases ahead of increases.
  • An item at quantity one or two across many channels is where the real risk sits. Push those immediately and individually, and batch the high-stock changes.
  • The instant the source hits zero, set the channel to zero. Nothing waits for the next batch.

Version every update so stale ones can't win

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.

Reconcile on a schedule, because sync will drift

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.

Match the SKUs before any of this matters

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.

The channel's own cache adds lag you don't control

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.

Bundles and kits share stock in a way a single-SKU view hides

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.

Multiple warehouses mean the source of truth has a location dimension

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.

Monitor the sync itself, not just the stock numbers

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.

Test the race condition directly

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.

How do you stop overselling when the same product is listed on multiple marketplaces?

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.

Share this guideLinkedInXWhatsAppFacebook
All guides