How to safely delete inventory records in bulk using the Kibo Inventory APIs, including dry runs, async job monitoring, batch sizing, and self-throttling.
This guide walks through how to safely delete inventory records in bulk using Kibo’s Inventory APIs — including how to structure your requests, validate before executing, monitor deletion jobs, and self-throttle your requests to avoid HTTP 429 Too Many Requests errors.
Inventory API Overview
Best practices, job management, and segmentation via the Inventory API
Inventory Export File
Use Location Export files to build a complete inventory deletion list
API Best Practices
Rate limiting, retry logic, and request patterns for Kibo APIs
Get Inventory (POST)
Look up current inventory by product identifier before deleting
Bulk inventory deletion in Kibo is handled through two API endpoints that share the same asynchronous job-based execution model. Both endpoints return immediately — the actual deletion work happens in the background via Kibo’s job queue. This means there is no risk of HTTP timeouts for large deletions, and you can track progress through the job IDs returned in the response.When you should use these APIs:
Removing discontinued product lines across all or selected locations
Clearing out inventory for a tenant migration or re-platforming effort
Deleting a large set of SKUs identified by a known naming convention
Deletion is permanent. Always use dryRun: true first to verify scope before executing a live deletion.
POST /api/commerce/inventory/v5/inventory/deleteUse this endpoint when targeting one product identifier — a single partNumber, upc, or sku — across one or all locations. It supports basic regex operators in the identifier field (.*+?^$[]), making it well-suited for pattern-based deletion (e.g., all products whose part number starts with a given prefix).
POST /api/commerce/inventory/v5/inventory/deleteItemsUse this endpoint when you have a list of multiple products to delete in one operation. The items array accepts individual product identifiers — each specified by partNumber, upc, or sku. Regex operators are supported per item.
Both endpoints operate asynchronously. When you submit a deletion request, Kibo:
Validates the request.
Enqueues one background job per location affected by the deletion.
Returns immediately with a response — no waiting for the deletion to complete.
This architecture is why these endpoints are safe to use for large-scale operations involving millions of records or hundreds of locations. There is no HTTP timeout risk on the deletion request itself.
The jobIDs array contains one job ID per affected location. For large tenants with many locations, this list can be long. Store all job IDs returned — you will use them to monitor progress.
for each jobID in jobIDs: repeat: response = GET /api/commerce/inventory/v1/queue/{jobID} if response.status == "SUCCESS" or response.status == "FAILED": log result and stop polling this job else: wait 10–30 seconds before polling again
Do not poll at a rapid fixed interval. Poll every 10–30 seconds per job using exponential backoff, particularly if you have many jobs to track simultaneously. Excessive polling of the job endpoint itself consumes your API rate limit quota.
The tenant already receives daily inventory export files
Use the most recent Location Export file — it is the authoritative full-tenant inventory list
The tenant does not have exports set up yet
Contact Kibo Support to enable exports, or use your own catalog/ERP as the source of product identifiers to query the Get Inventory API
The cleanest path for a bulk deletion project is: get the Location Export file → parse all UPC/PartNumber/SKU values from it → use that list as the input to the bulk delete operation. This gives you a provable, timestamped snapshot of exactly what was in inventory at the time of deletion, and a clean audit record.
Option A: The Inventory Export File
Inventory Export File documentationThe Location Export is the most reliable way to get a complete, authoritative list of every item at every location across your entire tenant. It includes key fields such as PartNumber, UPC, SKU, quantity on hand, quantity available, safety stock, floor quantity, and locationCode for every record.The tradeoff: The export is not on-demand via API. It is a scheduled daily file delivered by Kibo to your configured drop point (such as an SFTP site or cloud storage destination). You cannot trigger it yourself via an API call.
If your tenant already has inventory exports enabled and is receiving daily files, the most recent Location Export file is the best starting point — it gives you a clean, parseable list of all product identifiers per location.
If the tenant does not have exports enabled, you need to contact Kibo Support to enable the feature and set up a delivery destination.
Two export types:
Export Type
What it contains
Best for
Location Export
Every item at every location, with location codes
Building a per-location deletion list
Aggregate Export
Total quantities across all locations, no location breakdown
Understanding overall scope
For building a deletion list you will use against the bulk delete endpoint, the Location Export is what you want — it gives you the product identifiers and the location codes together.
Option B: The Get Inventory (POST) API
Get Inventory (POST) APIThe Get Inventory API supports pagination (pageSize) and location filtering. However, there is a critical constraint: the items array in the request body is required, and it specifies which products you want to look up. This is an inquiry/lookup API — not a “dump everything” API. You cannot submit an empty request and get all inventory back.
If you already know the product identifiers (e.g., from your ERP or catalog system), you can paginate through the Get Inventory API per location to confirm what exists in Kibo before building your deletion list.
If you do not have a known product list, the API alone cannot enumerate all inventory records without already knowing what to query for.
Step 3 — Batch Your Live Requests (If Using Delete Items)
If you are using the Delete Items endpoint /api/commerce/inventory/v5/inventory/deleteItems with a large list of products, do not send all items in a single request. Break your list into batches.
Recommended batch size: 500 items per request.The request returns immediately once jobs are enqueued, but the enqueuing itself is synchronous and bounded by database and process timeout limits. Staying at or below 500 items per batch keeps your requests well within the timeout ceiling, even on tenants with a large number of locations.
Hard maximum: 1,000 items per request. The Delete Items endpoint will reject requests that exceed this ceiling under any circumstances.
Between batches, add a deliberate delay of at least 5–10 seconds. This serves two purposes: it gives the job queue time to begin processing the previous batch, and it keeps your sustained request rate below the per-minute API rate limit.Recommended approach:
Split your full product list into batches of 500 items per request.
Do not exceed 1,000 items per batch under any circumstances.
Submit one batch at a time. Collect all jobIDs returned in the response before proceeding.
Wait at least 5–10 seconds between batches.
Do not use batch completion (all jobs at SUCCESS) as a gate before submitting the next batch — the jobs run asynchronously and may take time to work through the queue. Pace by time delay, not by job status.
Poll each job ID using the Get Job API. Log all FAILED jobs and their messages for investigation. A failed job does not automatically retry — you will need to re-submit a targeted deletion request for any failed scope.
Kibo enforces rate limiting per API route to protect platform stability for all tenants. If you exceed the allowed request rate, subsequent requests will be rejected with an HTTP 429 Too Many Requests response until the restriction window expires.
The Retry-After response header tells you exactly how many seconds to wait before making another request. Possible values are 60, 900, 1800, 2700, or 3600 seconds (1 minute up to 60 minutes). Do not continue making requests during this window — they will also be rejected, and sustained violations can extend the restriction period.
Kibo applies limits at both the per-minute (RPM) and per-hour (RPH) level for each API route. Both limits count against the same requests:
Minute limits reset at the start of each new minute.
Hourly limits operate on a rolling 60-minute window divided into 15-minute buckets.
Burst behavior: You can send requests up to the maximum RPM rate, but only for as long as your hourly budget allows. For example, if the inventory route allows 50 RPM and 200 RPH, you can sustain 50 RPM for a maximum of 4 minutes before exhausting your hourly budget. Plan accordingly — do not use your entire hourly quota in a short burst.
To view the exact rate limits for your tenant per API route, go to Dev Center > API > Limits. If you are within limits, the status shows OK in green. If throttled, it shows Throttled in red.
Send one batch at a time. Never fire multiple deletion requests simultaneously.
Each request to delete or deleteItems generates jobs and counts against your per-minute and hourly API rate limits.
Add deliberate delays between batches. Wait at least a few seconds between requests, longer for large lists.
This keeps your sustained request rate below the per-minute limit without exhausting your hourly budget too quickly.
Honor the Retry-After header immediately. If you receive a 429, stop all requests and wait the full duration.
Continuing to send requests during a restriction window will not succeed and may extend the throttle period.
Poll job status at low frequency. Poll each job every 10–30 seconds, not continuously.
Job status poll requests count against the same /api/commerce/inventory/* rate limit pool as your deletion requests.
Schedule large deletions during non-peak hours.
For US tenants, non-peak hours are 05:00–11:00 UTC. For EU tenants, 22:00–04:00 UTC. Running bulk operations during these windows reduces contention. Non-peak hours are calculated in UTC — schedule jobs in UTC to avoid Daylight Saving Time shifting your window.
function submitWithRetry(requestPayload): attempt = 0 while attempt < MAX_RETRIES: response = POST /api/commerce/inventory/v5/inventory/deleteItems (requestPayload) if response.status == 200: return response if response.status == 429: retryAfterSeconds = response.headers["Retry-After"] log("Rate limited. Waiting " + retryAfterSeconds + " seconds before retry.") wait(retryAfterSeconds) // Respect the exact header value attempt += 1 if response.status == 500 or other error: wait(exponentialBackoff(attempt)) // Add delay before retrying non-429 errors attempt += 1 raise Error("Max retries exceeded")
Per the API Best Practices, always add delay before retrying on non-200 responses. Use exponential backoff or a similar strategy rather than retrying immediately.