For the complete documentation index, see llms.txt. This page is also available as Markdown.

How the repurchase algorithm decides

This article explains why the algorithm recommends the products it does. For the practical side (the variable, builtin, preset, input/output contract, and errors), see Integrate & call the repurchase algorithm.

The core intuition

For each product in a user's purchase history, the algorithm:

  1. Estimates D, the product's repurchase cadence in days.

  2. Computes days_since_last_purchase, how long since the user last bought it.

  3. Defines a repurchase window around D.

  4. If the user is inside the window, the product is eligible and gets an urgency score; otherwise it's discarded.

Eligible products are returned sorted by descending urgency. The idea: don't recommend a product right after it was bought (too early), nor indefinitely long after (the need is probably already covered), but around the moment the observed cadence says the user is due to buy again.

Estimating the cadence D

How D is estimated depends on the user's history for that specific product.

Two or more purchases: use the user's own cadence

Sort the purchase dates, take the intervals between consecutive purchases, and set D to their mean.

Example. Purchases on Jan 1, Mar 1, May 1 → intervals of 59 and 61 days → D = 60 days.

The user's own cadence is used as-is, with no smoothing and no blending with the site-wide average.

A single purchase: fall back to the site-wide average

One purchase can't measure an interval, so the algorithm falls back to the product's site-wide average cadence (avg_repurchase_interval_days).

  • If a site-wide average exists → it becomes D.

  • If none exists (nobody has ever repurchased the product) → the product is excluded. There is no second fallback.

The repurchase_frequency column always reflects the value actually used: the user's own cadence, or the site-wide average when it's the fallback.

The site-wide average

The site-wide average is the safety net for users who bought a product only once. For each product:

  1. Consider purchases over the trailing 12 months.

  2. Keep only users who bought that product at least twice on distinct dates. A one-time buyer says nothing about cadence.

  3. For each such user, compute their average cadence (mean of the intervals between their successive purchases).

  4. The product's site cadence is the mean of those per-user cadences, where every multi-buyer weighs equally regardless of how many times they bought.

Two product-level indicators come out of this:

Indicator
Meaning

avg_repurchase_interval_days

Average repurchase cadence across all multi-buyers. Null if no multi-buyer exists → the product can't serve as a fallback.

multi_buyer_count

Number of users who bought the product ≥ 2 times (a confidence indicator).

These refresh with the catalog; a daily cadence is enough, since the 12-month aggregate barely moves day to day.

The repurchase window

Around the estimated cadence D, an eligibility window is defined as:

With the default ratio of 1/3, the window is [2/3·D, 4/3·D].

A product is eligible only when the user sits inside the window:

Before the window (too early) → excluded. After it (overdue) → excluded. The ratio is configured once per site (repurchase_window_ratio in Redis) and is shared by every repurchase rule on that site, so it's not a per-rule parameter. Per-rule tuning happens through where / sort on days_since_last_purchase and urgency_score.

Example. D = 30 days → window [20, 40] days after the last purchase.

The urgency score

The urgency score is a symmetric triangular peak centered on D: 1.0 exactly at the forecasted date, falling linearly to 0 at the window edges.

The compact form:

With D = 30 and ratio = 1/3 (window [20, 40]):

Days since last purchase
Urgency score

20

0.0

25

0.5

30

1.0

35

0.5

40

0.0

Sorting urgency_score DESC therefore surfaces the products for which the user is closest to their repurchase date.

End-to-end example

Request excerpt:

Walkthrough as of 2026-04-02, default ratio 1/3:

  • COFFEE-1KG: 3 purchases → intervals of 30 and 31 days → D ≈ 30.5. Last purchase 03/06 → days_since_last ≈ 27. Window ≈ [20.3, 40.7]. 27 is inside → eligible, urgency_score ≈ 0.65.

  • FILTERS-100: 1 purchase → fall back to the site-wide average. If avg_repurchase_interval_days ≈ 45D = 45. Last purchase 02/20 → days_since_last ≈ 41. Window ≈ [30, 60]. 41 is inside → eligible. Had there been no site-wide average, it would be excluded.

The response returns the eligible products with their catalog columns plus repurchase_frequency, days_since_last_purchase, and urgency_score, sorted by urgency_score DESC and limited to 12.

Reminder: the API doesn't fetch history on its own. The caller supplies purchase_history on every request. See Integrate & call the repurchase algorithm for how to wire that up.

Defaults at a glance

Parameter
Default
Configured where

Input variable

purchase_history (ITEMS_HISTORY)

Declared automatically on analytics unlock

Cap on products in history

50 (most recent kept)

Not configurable

Window width ratio

1/3 (≈ ±33%) → [2/3·D, 4/3·D]

Per site (repurchase_window_ratio), not per rule

Site-wide average lookback

Trailing 12 months

Not configurable

Multi-buyer filter

Users with ≥ 2 purchases of the product

Not configurable

Default sort

urgency_score DESC

Editable in the rule

Default volume

12

Editable in the rule

Last updated

Was this helpful?