Freshness
How FurniOps decides how often to check each source, how you tell it what matters now, and how to ask for a fresh stock check.
Polling every vendor every few minutes would be rude to the vendor and wasteful for everyone. FurniOps instead learns how often each source actually changes and checks it about that often, faster for what you are selling right now.
Be told, rather than ask
FurniOps prefers, in order:
- a vendor pushing changes to it;
- a vendor's own change feed;
- conditional requests (
ETag,If-Modified-Since), which cost almost nothing when nothing changed; - hints such as a platform's per-product
updated_at; - full pulls, skipped at the snapshot, record and field level when nothing changed, so an unchanged catalogue writes nothing and sends no event.
Two speeds
- Offers (price, quantity, incoming stock, availability) take a hot path with its own queue
and workers: small payloads, light validation, one
offer.changedper changed offer. A long catalogue run never delays a stock update. - Content (titles, descriptions, specifications, media, categories) takes the full path through normalising and matching. Minutes are fine there.
A source whose connector can read stock separately has two run streams, catalogue and
stock, each scheduled on its own.
Adaptive schedules
For each source and stream, FurniOps estimates how often the data changes from what its recent runs found, and sets the next check to about the median wait for a change, always within the source's politeness limits. What it has learned is on the source:
{
"freshness": {
"catalogue": {
"interval_minutes": 360,
"changes_per_day": 1.8,
"observations": 42,
"learned_at": "2026-10-04T06:00:00Z"
},
"stock": null
}
}changes_per_day is null until enough runs have been seen, and a stream not learned yet is
null. A source with schedule.adaptive: false keeps the intervals you set.
Tell FurniOps what matters now
Variants you have just sold, have in carts or on backorder matter more than the rest of the
catalogue. Record that demand, and the sources offering them are checked at their fastest, every
15 minutes, until it lapses (ttl_minutes, 15 minutes to 7 days, a day by default; send it again
to extend it):
// Variants you sold, have in carts or on backorder are checked at their sources' fastest rate.
await furniops.demand.record({
variants: ["var_034XqBaL2mRLTZPXyRbaUc"],
reason: "cart",
ttl_minutes: 60,
});
// At checkout: ask for a fresh stock check of one variant now.
const check = await furniops.variants.check("var_034XqBaL2mRLTZPXyRbaUc");
console.log(check);POST /v1/variants/{id}/checks asks for the variant's offers to be checked now, for a moment
that matters such as checkout. Each source offering it gets a run (stock only, where the connector
reads stock separately), joins a run already under way, or answers with one that finished moments
ago (within a minute for stock, 15 minutes for a whole catalogue). New values arrive as
offer.changed events. It is an expensive operation with its own
rate budget.
Anomalies are held back
A value far outside a source's history or its peers (a $949 sofa suddenly at $9.49) is quarantined instead of published, so a vendor's typo does not reach your storefront as a change.
Master records and provenance
How many vendors' descriptions of a product become one master record, what each field remembers about its origin, and why IDs never change.
The change feed
The ordered, replayable log of every change you can see, read with a cursor, long-polled with wait, or streamed live over server-sent events.