Skip to main content
This guide walks through building a small client that mirrors on-hand quantities from the Vori API into your own system — the pattern you can use to feed purchasing, replenishment, or an online storefront. Inventory is different from a transaction feed. A transaction is written once and never changes, so you append it and move on. A product’s on-hand quantity changes all day, under an ID that stays the same. What you are building is a mirror you keep up to date, not a log you add to.

Authenticate with an API key

See the authentication docs for how to create an API key. Make sure the role you assign it can read products.

List inventory

The list endpoint supports the following filters, declared as typed query parameters rather than one opaque filter blob: Every list response uses one envelope:
Each record is one product’s current on-hand quantity — see the store product inventory object for its fields. The id is the product ID, so it is what you upsert on in your own table.

Page through results

Pagination is cursor-based, not offset-based — pass limit and starting_after/ending_before with the id of the last record you saw, and keep going while has_more is true:
Results are always ordered by id descending — this order is not client-selectable. A product’s id never changes, so a quantity that moves while you are paging does not shuffle products between pages.

Poll for changes

Your first run has nothing to catch up from, so it leaves the updated_at filter off and pulls every quantity. After that, ask only for what moved. Track a watermark and filter on updated_at[gte] from where the last run left off:
updated_at moves whenever the on-hand quantity changes — a sale, a delivery, a count, an adjustment. Renaming a product or changing its price does not move it, so a poll returns only the products whose quantity actually changed. The five-minute buffer means each window starts a little before the last one ended, so a quantity written while the previous run was in flight is picked up rather than stepped over. Consecutive runs overlap slightly as a result, which is why your write has to upsert on id. Re-reading a product is harmless. Missing one is not. One run covers every store your API key can read, so keep a single watermark rather than one per store. Every record carries store_id, so you can split the results by store in your own system afterwards. Polling store by store multiplies your request count and leaves you several watermarks to keep in step, for no benefit.

Handle rate limits

Requests are rate-limited per API key. Every response carries your current standing:
  • X-RateLimit-Limit — max requests allowed in the current window
  • X-RateLimit-Remaining — requests left in the window
A 429 response includes Retry-After (seconds) — back off and retry rather than hammering the endpoint. Give the retry a bound so a long rate-limit stretch ends in a clear error rather than spinning forever:

Putting it together

Everything above in one script. Point VORI_API_KEY at your key, replace write_to_your_inventory_store, and run it on whatever schedule suits you — cron, Airflow, a worker loop. The first run finds no watermark and pulls the whole catalog. Every run after that pulls only what changed, so the same script covers both and you do not have to remember which mode you are in. It writes each page as it arrives rather than collecting everything first, so memory stays flat however large the catalog is, and a run that dies partway keeps the pages it already wrote. The checkpoint records how far through the current window it got and where the next window will start.
The checkpoint is saved after every page and carries both values, but only the cursor changes as you go — the watermark keeps the previous run’s value until the whole window has been written.Products come back in id order, which says nothing about when each one was counted. A run partway through has covered part of the catalog, not the earliest or latest part of the window. Advancing the watermark there and then failing would leave the next run starting after products it never read, skipping them until something counts them again.
Each row in data is the complete current picture for one product, so this script never needs a second call per product. There is no history to walk and no deltas to apply — whatever current says is what the shelf is expected to hold right now.