Manual stock updates on marketplaces are a constant headache. You list 10 units, but only 3 remain in the warehouse. An order is placed but cannot be fulfilled — the marketplace fines you and your product rating drops. Employees spend hours reconciling numbers, but human error is inevitable: they forget to update, enter the wrong quantity, or fail to account for reserves. Our team brings 10+ years of experience and 40+ successful projects integrating accounting systems with marketplaces. Compared to manual updates, an automated bot cuts stock management time by 10x and reduces canceled orders by 70%. For a store with $90k–130k turnover, typical savings on fines reach up to $9k–13k per year.
Why Automation Beats Manual Updates
Manual entry through the dashboard takes 1–2 hours daily for a store with 5,000 SKUs. A single digit error cascades into canceled orders, negative ratings, and lost search positions. Our bot processes changes in seconds, eliminating mistakes. It pulls data directly from your accounting system — no typos possible. The result: product ratings improve and canceled orders drop by 70%.
Case study: For a client with a catalog of 15,000 SKUs, manual updates took 4 hours daily. After deploying the bot, time fell to 10 minutes, and canceled orders dropped 70%. Fines for incorrect stock were completely eliminated. Compared to off-the-shelf ERP modules, our bot offers more flexible buffer and alert configuration.
According to official Ozon API documentation, PUT /v2/products/stocks allows updating up to 100 products per request.
How We Integrate with Data Sources
Several sources need aggregation. The primary one is your warehouse system (1C, MyWarehouse, Odoo). We additionally connect suppliers (via import) and read marketplace reserves. We also account for virtual reserve from your own website — open shopping carts.
| Accounting System | Integration Method | Complexity |
|---|---|---|
| 1C | HTTP service (REST) or webhook | Medium |
| MyWarehouse | REST API | Low |
| Odoo | XML-RPC / JSON-RPC | Medium |
We structure data using tables stock_levels, marketplace_stocks, and stock_sync_log. The first stores total stock and reserves, the second the current figures for each marketplace, the third the sync history for debugging.
CREATE TABLE stock_levels ( id BIGSERIAL PRIMARY KEY, product_id BIGINT REFERENCES products(id), warehouse_id INT REFERENCES warehouses(id), quantity INT NOT NULL DEFAULT 0, reserved INT NOT NULL DEFAULT 0, available INT GENERATED ALWAYS AS (quantity - reserved) STORED, updated_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE marketplace_stocks ( id BIGSERIAL PRIMARY KEY, product_id BIGINT REFERENCES products(id), marketplace VARCHAR(50) NOT NULL, warehouse_code VARCHAR(100), synced_quantity INT, last_synced_at TIMESTAMP, sync_status VARCHAR(20) DEFAULT 'ok', error_message TEXT, UNIQUE(product_id, marketplace, warehouse_code) ); CREATE TABLE stock_sync_log ( id BIGSERIAL PRIMARY KEY, product_id BIGINT, marketplace VARCHAR(50), old_qty INT, new_qty INT, source VARCHAR(50), synced_at TIMESTAMP DEFAULT NOW() ); Integration with Ozon API
We update stock via the PUT /v2/products/stocks endpoint, sending batches of 100 products to stay within limits.
class OzonStockSyncer { public function syncStocks(array $items): SyncResult { $result = new SyncResult(); $batches = array_chunk($items, 100); foreach ($batches as $batch) { $payload = array_map(fn($item) => [ 'offer_id' => $item['sku'], 'stock' => $item['qty'], 'warehouse_id' => $item['warehouse_id'], ], $batch); $response = Http::withHeaders([ 'Client-Id' => $this->clientId, 'Api-Key' => $this->apiKey, ])->post('https://api-seller.ozon.ru/v2/products/stocks', [ 'stocks' => $payload, ]); if (!$response->successful()) { $result->errors[] = $response->json('message', 'Unknown error'); continue; } foreach ($response->json('result', []) as $item) { if ($item['updated']) { $result->updated++; } else { $result->errors[] = "SKU {$item['offer_id']}: " . ($item['errors'][0]['message'] ?? 'error'); } } } return $result; } } Integration with Wildberries
Wildberries uses a PUT request to /api/v3/warehouses/{warehouseId}/stocks. The code is similar but accounts for specifics — token in header, mapping via barcode.
class WildberriesStockSyncer { public function syncStocks(array $items, int $warehouseId): SyncResult { $payload = array_map(fn($item) => [ 'sku' => $item['wb_barcode'], 'amount' => max(0, $item['qty']), ], $items); $response = Http::withToken($this->apiKey) ->put("https://marketplace-api.wildberries.ru/api/v3/warehouses/{$warehouseId}/stocks", [ 'stocks' => $payload, ]); if (!$response->successful()) { throw new WildberriesApiException($response->json('title', 'API Error')); } return new SyncResult(updated: count($items)); } } How to Configure Buffer Stock and Alerts?
Often you need to keep a buffer — not push the entire stock to the marketplace, reserving quantities for other channels. We implement flexible logic: an absolute or percentage buffer, per-product upper limit. For example, for a product with 50 units in stock, you can set a buffer of 5 units or 10%. Then only 45 units go to the marketplace.
class StockCalculator { public function calculateMarketplaceQty(Product $product, string $marketplace): int { $available = $product->available_stock; $buffer = $product->stock_buffer ?? config("marketplaces.{$marketplace}.default_buffer", 2); $pctBuffer = (int) ceil($available * config("marketplaces.{$marketplace}.buffer_pct", 0) / 100); $reserved = max($buffer, $pctBuffer); $qty = max(0, $available - $reserved); $maxQty = $product->max_marketplace_stock ?? PHP_INT_MAX; return min($qty, $maxQty); } } We set up alerts — if your site shows positive stock but the marketplace shows zero for more than 2 hours, you get a notification. This prevents desynchronization. Alert scenarios:
| Situation | Notification | Action |
|---|---|---|
| Desync > 1 hour | Telegram / Email | Automatic resync |
| Marketplace API error | Telegram | Manual log check |
| Buffer exhausted | Restock warehouse |
More on marketplace API comparisons
| Marketplace | API specifics | Request limit | Update speed |
|---|---|---|---|
| Ozon | PUT /v2/products/stocks | 100 products per request | Up to 1 minute |
| Wildberries | PUT /api/v3/warehouses/{id}/stocks | 50 requests/sec | Up to 2 minutes |
What's Included in the Work?
| Component | Description |
|---|---|
| Documentation | Architecture, data schema, operations manual |
| Access | API key setup, permissions for accounting system and marketplaces |
| Training | Bot demonstration, log and alert analysis for your team |
| Support | Warranty maintenance for one month, then per agreement |
Typical Integration Mistakes
- Incorrect SKU — format differences between the accounting system and marketplace. Solved with a unified mapping system.
- Exceeding request limits — too frequent updates. Increase interval or use batches.
- Authorization error — expired token. Set up automatic key renewal.
- Stock discrepancy — neglecting reserves. Implement buffer stock.
Our Process
- Analysis — study your accounting system, current APIs, catalog size.
- Design — data schema, buffer logic, integration modules.
- Development — write synchronization code, alerts, logging.
- Testing — test all scenarios (including errors) on a staging environment.
- Deployment — roll out on your server or cloud, configure monitoring.
- Maintenance — warranty period, team training.
Timeline
- Basic version (Ozon + WB + 1C): 4–5 business days.
- Complex buffer schemes and additional marketplaces: +1–2 days.
Get a consultation — we'll evaluate your project in one day. Order bot development to eliminate fines and save your team's time.







