← All work

Blinkit · 2023

NPR Engine for AOV Growth & Waste Reduction

Using recommendation logic to grow baskets without creating excess inventory

Pricing & Monetization | Growth & Activation

The recommendation rail in action

Role
Sr. Data Analyst, product strategy
Timeline
~6 weeks
Worked with
Growth, Category, Merchandising, Supply Chain
Tools
SQL, BI dashboards, PMI analysis, A/B testing

The problem

The rail after add-to-cart ranked items by platform-wide popularity, so it mostly suggested more of what was already in the cart.

What I did

Redesigned the scoring around cart affinity (PMI), persona clusters and subtype affinity, with a time-of-day boost for perishables and inventory guardrails.

The result

Impression to add-to-cart rose from 16% to 25%, AOV went up ₹8 per order, and perishable disposal fell 5.8%. Shipped to all stores.

The problem

Blinkit's Next Product Recommendation (NPR) rail is the strip of suggestions that appears after a customer adds something to cart. It already existed, but it ranked items by overall popularity, often from the same product type.

Old logic: popularity ranked

Add to cart: Bagels

Rail shows: Milk, Bread, Croissant, Pita bread

Near-zero incremental add-to-cart.

What the rail needed to do

Suggest what this customer is most likely to add next, not more of what is already in the basket.

Do it with live store inventory and category priorities in mind.

Why it was harder than it looked

This was not just a ranking problem. The rail had to balance what felt like a natural next add, the right price point for an impulse buy, live inventory at the store, support for niche categories, and the business goal of reducing perishable disposal.

  • Right next add

    The rail had to show what a customer was actually likely to add next, not just something loosely related.

  • Price matters

    Even relevant products would fail if they were too expensive for an impulse add.

  • Thin user history

    Many customers did not have enough order history for strong personalisation.

  • Live inventory

    A recommendation only worked if it was actually in stock at that store in that moment.

  • Beyond obvious picks

    The logic could not only favour the highest-volume products. It also had to support smaller but valid category pairings.

  • Business tradeoffs

    The rail had to improve add-to-cart behaviour while also supporting margin and reducing perishable disposals.

It wasn't a data problem. Blinkit had rich add-to-cart and order history. It was a logic problem: the rail used the wrong signal, item popularity instead of cart affinity. The fix needed three things:

  1. 01

    Signal design

    Which data sources to use and how to weight them

  2. 02

    Scoring logic

    How to combine signals into a single rankable score

  3. 03

    Operational guardrails

    Inventory, time, and pricing filters before serving

The scoring system

For each cart state, the system shortlisted candidates, scored them, then filtered and ranked them before serving.

  1. 01

    Cart state

    Customer adds an item to cart

  2. 02

    Score ~30 candidates

    PMI + persona + subtype affinity

  3. 03

    Filter and rank

    Perishable boost, price check, inventory check

  4. 04

    Top 15 tiles served

    Ranked, filtered, real time

Final score = 0.5 × PMI basket + 0.3 × personalisation + 0.2 × subtype affinity

Why these weights:

  • Cart context mattered most, because the rail had to respond to what was already in the basket.
  • Personalisation helped when history was available.
  • Subtype affinity was the fallback, and supported broader category pairings.

Why PMI, not raw counts

Every SKU was scored against every other SKU by how often they appeared in the same cart. Raw co-occurrence has a bias: items like milk and eggs are in so many carts that they top every list. PMI corrects for that.

Raw counts: popular items dominate

Cart: Nutella

Top match: Milk. High volume, low relevance.

PMI: normalise for popularity

Cart: Nutella

Top match: Strawberries. Lower volume, higher affinity.

PMI(A, B) = log [ P(A, B) / (P(A) × P(B)) ]

Personas instead of individuals

Individual history is thin in quick commerce. Most users order 2 to 4 times a month, so 90 days gives only 8 to 12 orders. Instead, users were clustered into behavioural personas, and each persona gave a dense, reliable signal. A user can belong to several personas, and recommendations combine all of them.

Filters before serving

Scoring ranks the candidates. Filters decide what actually gets served, and they run in order.

  1. 01

    Perishable boostAfter 7 PM

    Perishables with high disposal risk get a score multiplier, which lifts them within the already-ranked list. Not a separate clearance experience.

  2. 02

    Price range filterImpulse guard

    Candidates priced above 2× the customer's average order value are filtered out. Recommendations should feel like natural additions, not upsells that need a separate mental commitment.

  3. 03

    AOV sortAll day

    Within the filtered set, sort by descending price to maximise basket value.

  4. 04

    Inventory checkHard filter

    Remove anything not in stock at the customer's dark store. Runs at serve time, not when scores are generated.

  5. 05

    Top 15 servedOutput

    First 15 tiles, in order: ranked by score, time-adjusted, price-filtered, inventory-safe.

A tradeoff I had to defend

Leadership concern

Surfacing perishables after 7 PM might feel like the app is offloading unwanted products, which could hurt trust.

How I defended it

The boost was invisible to customers. Perishables ranked higher inside an already personalised list, not in a separate clearance experience. After launch: no drop in repeat order rate or rail engagement, and the disposal reduction held.

What the pilot taught us

Before the national rollout, the algorithm was soft-launched to validate the scoring and catch edge cases.

  • Rail ATC rate

    Cross-ptype logic validated

    Users added recommended items at a measurably higher rate than the same-ptype control group.

  • Data quality fix

    City-level variation surfaced

    Personalisation signals were too noisy for dark stores with under 200 daily orders, so they switched to city-level aggregation.

  • Bounce rate

    Inventory guardrail was critical

    Removing out-of-stock recommendations reduced rail bounce rate and improved repeat rail engagement.

The same algorithm gives very different results depending on the cart, the user's history and the time of day:

Results

The rail shipped to all stores. Conversion went up and perishable disposal went down, all without a separate customer-facing experience.

Built at Blinkit · 2023