Paging through stock changes
GET /api/v1/stock/changes lists the changes to your stock: adjustments, and reservations made and released. This page explains how to page through it, and how to poll it so you never miss a change.
What a page looks like
Changes come newest first, 50 per page unless you ask for another per_page (at most 200). The pagination object says where you are:
curl "https://picked.nl/api/v1/stock/changes?per_page=100&page=1" \
-H "Authorization: Bearer $PICKED_TOKEN" \
-H "Accept: application/json"
{
"data": [
{
"id": 90211,
"occurredAt": "2026-10-10T06:42:17+00:00",
"eventType": "stock_adjusted",
"productSku": "BEL-ZW-01",
"warehouseCode": "UTR",
"locationName": "A-03-2",
"quantity": -2,
"reason": "damaged",
"movementUuid": "9b2f6c1e-4d7a-4f0b-8c3e-2a5d7e9f1b40"
}
],
"pagination": {
"currentPage": 1,
"perPage": 100,
"lastPage": 1,
"total": 1,
"from": 1,
"to": 1
},
"_links": {
"self": { "href": "/api/v1/stock/changes?per_page=100&page=1" }
}
}
Follow _links.next until it is absent, or ask for the next page until currentPage reaches lastPage. The keys of this endpoint are camelCase, unlike the rest of the API; v1 keeps them that way.
eventType is one of stock_adjusted, stock_reserved and stock_reservation_released. For stock_adjusted, quantity is signed: a negative number is stock that went out. For the two reservation types it is always positive.
Filters
| Parameter | Effect |
|---|---|
since |
Changes at or after this moment, as an ISO 8601 date-time. A value without a time zone is read as UTC. |
until |
Changes at or before this moment. A bare date, such as 2026-10-12, covers that whole day in UTC. |
product_sku |
One product. An unknown SKU answers 404. |
warehouse_id |
One warehouse. |
location_id |
One location. |
event_type |
One of the three event types above. |
reason |
One reason, such as damaged. |
A filter that matches nothing answers an empty page, not an error.
Polling without missing a change
Page numbers are for browsing. Because the newest change comes first, a change that lands while you are paging pushes everything down by one, and you would read one change twice or skip one. To keep a system in step, read with a cursor instead:
- The first time, ask for the changes after cursor
0:GET /api/v1/stock/changes/feed?after=0. They come oldest first, at mostper_pageat a time. - Process them, then store the answer's
nextCursor. - While
hasMoreistrue, follow_links.nextstraight away: it asks again withafterset to that cursor and your filters kept. Once it isfalse, you have everything so far; wait for your next poll and ask again from the cursor you stored.
curl -G "https://picked.nl/api/v1/stock/changes/feed" \
--data-urlencode "after=0" \
--data-urlencode "per_page=200" \
-H "Authorization: Bearer $PICKED_TOKEN" \
-H "Accept: application/json"
{
"data": [
{
"id": 90211,
"occurredAt": "2026-10-10T06:42:17+00:00",
"eventType": "stock_adjusted",
"productSku": "BEL-ZW-01",
"warehouseCode": "UTR",
"locationName": "A-03-2",
"quantity": -2,
"reason": "damaged",
"movementUuid": "9b2f6c1e-4d7a-4f0b-8c3e-2a5d7e9f1b40"
}
],
"nextCursor": "184467",
"hasMore": false,
"_links": {
"self": { "href": "/api/v1/stock/changes/feed?after=0&per_page=200" }
}
}
A change committed while you read lands after your cursor, so you neither skip it nor read it twice. With nothing new, nextCursor is the cursor you sent. Treat the cursor as an opaque string: store it and send it back, but don't compare or calculate with it. It stays valid for good. A change reaches the cursor about five seconds after it was saved; the wait lets changes saved a moment apart arrive in order. The feed takes the same filters as the pages.
Polling every minute or two stays well inside the stock endpoints' rate limit.
Changes or levels?
Stock changes tell you what happened to the stock on the shelves. They don't include what orders claim: when an order is accepted, it lowers the product's sellable figure without adding a stock change. So don't use the changes to decide which products to update in a webshop. Ask GET /api/v1/stock?changed_since=<moment> instead: it lists the levels of every product whose stock, order commitments or locations changed since then, sellable included. Keep your webshop's stock in sync shows how.