How to Update Shopify Inventory from a MySQL Database

Use your MySQL database as the inventory source of truth and push levels to Shopify.

What This Integration Does

If your warehouse management system, ERP, or a custom in-house tool owns inventory, Shopify needs to mirror it - not the other way around. This workflow runs on a tight schedule, queries MySQL for SKUs whose quantity has changed since the last sync, and pushes the new levels into Shopify. The result: stock in your storefront stays accurate without anyone updating Shopify by hand.

The pipeline is incremental: only SKUs that changed since the last run get pushed, so a typical run touches a few dozen items rather than the whole catalog. Each push is keyed by SKU and uses Shopify's inventory adjustment endpoint, so re-running the same window is safe.

Prerequisites

  • A MySQL connection with read access to the inventory table (must include sku, quantity, and updated_at).
  • An index on updated_at in MySQL so incremental queries stay fast.
  • A Shopify connection with the write_inventory scope.
  • SKUs in MySQL that exactly match SKUs configured on Shopify variants. Mismatches are the single biggest source of "missing" updates.
  • The Shopify location ID where inventory is tracked (most stores have one default location, but multi-location stores need an explicit ID).

Step 1: Schedule Trigger

Drop a Trigger node and set it to Schedule. Every 5 to 15 minutes works for most stores - tight enough that overselling is rare, loose enough that you don't blow through Shopify's rate budget on a quiet day. The trigger exposes the previous run timestamp.

Step 2: Variable - Compute the Sync Window

Add a Variable assignment for the window:

  • syncStart - the previous run's timestamp, or 1 hour ago on the first run.
  • syncEnd - the current timestamp.

Step 3: Query MySQL for Changed SKUs

Add a Connector node for the mysql connector with the execute-query tool:

SELECT sku, quantity
FROM inventory
WHERE updated_at >= '{{ syncStart }}'
  AND updated_at < '{{ syncEnd }}'
ORDER BY sku

If your inventory table has multiple warehouses, also join or filter on warehouse so each Shopify location gets the right slice.

Step 4: Get Shopify Inventory Item IDs

Shopify's inventory tools key off inventory_item_id, not sku. Add a Connector node calling the shopify get-inventory-levels tool, scoped to the SKUs in your query result (or to the location and let pagination fetch them all on first run, then cache). Build a lookup table mapping sku to inventory_item_id with a Transform node. For a stable catalog, cache that mapping in a mongodb collection so you don't re-fetch it every run.

Step 5: Loop and Adjust

Wrap the next step in a Loop over the rows from Step 3. For each row, resolve the inventory_item_id from the Step 4 lookup, then call the shopify adjust-inventory tool:

  • inventory_item_id: from the lookup
  • location_id: your Shopify location
  • available: {{ row.quantity }} - this is the absolute new level, not a delta

Add a Condition branch first that skips the call if the SKU isn't in the Shopify lookup - those are MySQL SKUs that don't exist in Shopify and need to be reported, not pushed.

Step 6: Report and Alert

After the loop, summarize: total SKUs processed, count updated, count skipped (no Shopify match), count failed. Route to a slack send-message step. If the skipped count is non-zero, include the SKU list so someone can fix the mismatches. Spojit's per-node retry handles transient Shopify 429s automatically; a sustained failure (5 retries) routes to the same Slack channel as a separate alert.

Tips

  • Use the absolute level, not a delta - adjust-inventory with an absolute level is idempotent. Deltas double-count if a run is retried.
  • Cache the SKU lookup - the SKU to inventory_item_id map only changes when products are added. Refresh it once an hour in a separate workflow, not every inventory sync.
  • Concurrency cap - set the loop's concurrency to 2 or 3. Shopify's per-store rate limit is generous but easy to blow with 100 parallel inventory writes.

Common Pitfalls

  • SKU mismatches - SKU-001 in MySQL vs sku-001 in Shopify is the most common failure mode. Either normalize at the source or do a case-insensitive match in the lookup transform.
  • Negative quantities - some inventory systems use negative numbers to represent oversold stock. Shopify accepts negatives via adjust-inventory but most reports expect zero. Decide a policy and clamp in Step 5.
  • Multi-location stores - if you sell from multiple warehouses, each MySQL row needs a location column and the workflow needs one Shopify call per (sku, location) pair. Don't just dump everything to the default location.
  • Backorder confusion - if Shopify is set to "continue selling when out of stock", a 0 quantity isn't the same as "stop selling". Coordinate with the store config so the sync intent is clear.

Testing

Pick three test SKUs in MySQL, change their quantities, and run the workflow manually. Confirm Shopify shows the new levels and the Slack summary matches. Run it again immediately and confirm zero rows update (since nothing changed) - this proves the incremental window is working. Then turn the schedule on and watch the first few runs.

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.