Live prices and stock
Change a price, a sale or the stock between two catalogs, and search shows it within seconds, without an index run.
Prices and stock change all day, while the catalog may arrive once a night. PUT /api/v1/offers writes such changes into the live search in between. This page changes the price of the pendant lamp LEUCHTE-KUGEL (Kugel: globe) and puts it back. Its two variants first:
curl -s 'http://127.0.0.1:7800/v1/product?id=LEUCHTE-KUGEL' | jq -c '.product.variants[] | {id, price: .offer.price}'{"id":"LEUCHTE-KUGEL-KLAR","price":149.0}
{"id":"LEUCHTE-KUGEL-RAUCH","price":159.0}The lamp comes in Klarglas (clear glass) and Rauchglas (smoked glass), at 149 € and 159 €.
Change a price
Send each change as its product, its variant, the channel and the fields that change. Each change is answered on its own:
changed=$(curl -s -X PUT -H "Authorization: Bearer $key" -H 'Content-Type: application/json' $api/offers -d '{"offers": [
{"product": "LEUCHTE-KUGEL", "variant": "LEUCHTE-KUGEL-KLAR", "channel": "de", "price": 129},
{"product": "LEUCHTE-KUGEL", "channel": "de", "price": 129}
]}')
revision=$(jq .revision <<< "$changed")
jq -c '.rows[]' <<< "$changed"{"state":"accepted"}
{"state":"rejected","code":"variant_needed","message":"\"LEUCHTE-KUGEL\" has variants, so name the one whose offer changes in `variant`."}The first change is kept. The second is left out, since a product with variants names the one that changes; a product the live catalog lacks is left out too, as new products come with the catalog. The kept changes make one revision, the answer's revision.
No index run is queued. The indexer waits a second for changes that follow, compiles the changed products again and replaces their documents in the live indexes. Every answer's trace names the revision it reads in live.offers, so wait until it names this one:
for _ in {1..30}; do
curl -s 'http://127.0.0.1:7800/v1/product?id=LEUCHTE-KUGEL&debug=1' \
| jq -e ".trace.live.offers >= $revision" >/dev/null && break
sleep 1
done
curl -s 'http://127.0.0.1:7800/v1/product?id=LEUCHTE-KUGEL' | jq -c '.product.variants[] | {id, price: .offer.price}'{"id":"LEUCHTE-KUGEL-KLAR","price":129}
{"id":"LEUCHTE-KUGEL-RAUCH","price":159.0}The clear lamp now costs 129 € on its tile and its product page alike. Release and snapshot stay as they were, and with the revision they name what answered. GET $api/live names the revision too, with when it arrived, and the panel shows that time as Prices and stock on Overview and Releases.
Reference: PUT /api/v1/offers · rows · GET /api/v1/live
Put it back
A change replaces only the fields it names, so the old price is one more change away:
revision=$(curl -s -X PUT -H "Authorization: Bearer $key" -H 'Content-Type: application/json' $api/offers \
-d '{"offers": [{"product": "LEUCHTE-KUGEL", "variant": "LEUCHTE-KUGEL-KLAR", "channel": "de", "price": 149}]}' \
| jq .revision)
for _ in {1..30}; do
curl -s 'http://127.0.0.1:7800/v1/product?id=LEUCHTE-KUGEL&debug=1' \
| jq -e ".trace.live.offers >= $revision" >/dev/null && break
sleep 1
done
curl -s 'http://127.0.0.1:7800/v1/product?id=LEUCHTE-KUGEL' | jq -c '.product.variants[] | {id, price: .offer.price}'{"id":"LEUCHTE-KUGEL-KLAR","price":149}
{"id":"LEUCHTE-KUGEL-RAUCH","price":159.0}Both lamps cost what the catalog says again.
Sales, stock and availability
A change may name any field of an offer, and the fields it leaves out stay as they are. This one puts the smoked lamp on sale for a weekend and sells out the clear one:
curl -s -X PUT -H "Authorization: Bearer $key" -H 'Content-Type: application/json' $api/offers -d '{"offers": [
{"product": "LEUCHTE-KUGEL", "variant": "LEUCHTE-KUGEL-RAUCH", "channel": "de", "sale_price": 139,
"sale_window": {"from": "2026-11-27T00:00:00+01:00", "until": "2026-11-30T00:00:00+01:00"}},
{"product": "LEUCHTE-KUGEL", "variant": "LEUCHTE-KUGEL-KLAR", "channel": "de", "availability": "out_of_stock", "stock": 0}
]}'end_sale: true ends a sale, and delivery_days changes the delivery time. A sale window that opens or closes rebuilds the search at that moment, as it does for a sale the catalog sets. Prices are what search shows; the checkout decides what the shopper pays.
Reference: sale_price · end_sale · availability
When the next catalog arrives
A change applies to every catalog that first arrived before it. Another catalog supersedes it, since the shop says what holds now:
| What comes next | The change |
|---|---|
| The same catalog, sent again | Stays |
| Another catalog | Goes, and the catalog's offer holds |
| A rebuild, such as after a publish | Stays, as does every change since its catalog |
A change sent while a new catalog's index run builds applies to that catalog too.
Until the next index run, what the run derived from the whole catalog stays as it was: the dictionary's counts, derived price buckets and how many values each facet counts.
Limits
- 1,000 changes per call. A larger batch is refused whole with a
422, so send a large sync in calls of 1,000, back to back. - One write a second. Changes that arrive close together become one write, so a burst batches itself.
- A catalog first. Before any catalog is live, the call answers
409.
Reference: offers_per_call · 409
Next
- Variants: what a variant's offer holds.
- Channels: the channels and currencies an offer belongs to.
- Importing and mapping: send the whole catalog.