FurniOpsDocs

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:

  1. a vendor pushing changes to it;
  2. a vendor's own change feed;
  3. conditional requests (ETag, If-Modified-Since), which cost almost nothing when nothing changed;
  4. hints such as a platform's per-product updated_at;
  5. 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.changed per 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.

On this page