×marble
all posts
May 28, 2026·11 min read

Personalization for Ecommerce: Catalog Scale, Attribute Graph, Post-Purchase Intent

Ecommerce personalization is a catalog problem first, a user problem second. How to think about attribute graphs, post-purchase intent, and the lifetime view.

Alex Shrestha·Founder, ×marble

Personalization for Ecommerce: Catalog Scale, Attribute Graph, Post-Purchase Intent

TL;DR.

  • Personalization for ecommerce is a catalog problem first and a user problem second; if your product graph is thin, no amount of user data will save the ranker.
  • The unit of work is the attribute graph — products linked to materials, brands, occasions, complements, and substitutes — not a flat taxonomy.
  • Intent state matters more than demographics: a shopper is researching, buying, or replenishing, and the right rank flips between those modes.
  • Post-purchase is not the end of a session — it is the start of the next one. Replenish, cross-sell, and upgrade are three distinct intent signals.
  • Sub-100 ms re-rank on the SRP is the latency budget that lets you stitch behavior into ranking without breaking the page.

Most ecommerce personalization advice starts with the shopper. We think that is backwards. You cannot personalize for someone if your catalog does not know what its own products are. Before you spend a quarter on a recommendation engine vendor, audit your attribute coverage, your complement edges, and your replenishment intervals. That is where the lift hides.

This post walks through how we think about personalization for ecommerce as an engineering problem: catalog modeling, intent state tracking, post-purchase signals, and lifecycle stitching across web, email, and app. We will name latency budgets and call out the tradeoffs we have hit.

Why ecommerce personalization is a catalog problem first

If you sell apparel and your product data lists category: shirt and a price but no fit, fabric, formality, or season, your ecommerce recommendation engine has nothing to reason over. It will fall back to collaborative filtering — "people who bought this also bought that" — and you will be stuck with the same top sellers on every page.

Build Grow Scale's 2026 ecommerce personalization guide flags this as the most common failure mode: a large share of personalization rollouts stall because of poor data quality, not because the algorithm picked is wrong. The fix is unglamorous and it lives in the catalog team's backlog: enrich attributes, normalize values, and tag every SKU.

A useful test: pick 20 random SKUs from your storefront. Can you build a similar items list using only structured attributes (no text search, no clickstream)? If not, you do not have a recommendation engine problem — you have a catalog problem. Fix that first or the personalization layer will keep recommending the same six items.

What "enriched" actually means

We have seen merchants count color, size, and brand as a complete attribute set. That is the minimum. A working attribute schema for ecommerce ranking includes:

  • Intrinsic: material, dimensions, weight, color family, pattern
  • Use-case: occasion, season, room, activity, skill level
  • Compatibility: works-with (lenses to camera bodies), required (printer to ink model)
  • Replenishment: typical reorder interval, consumable flag
  • Substitutability: equivalent across brands, lower-priced equivalents
  • Constraints: age-restricted, allergen flags, regulatory class

The last three columns are the ones merchandising teams skip. They are also the ones that unlock the highest-margin recommendations.

The attribute graph: products are nodes, edges do the work

Once attributes are filled in, the next move is to stop thinking about the catalog as a table and start thinking about it as a graph. Each product is a node. Each attribute, brand, and category is a node. Edges encode the relationships: is_complement_of, is_substitute_for, requires, goes_with, replenishes_to.

This matters because product personalization decisions are graph traversals, not table lookups. Showing a complementary lens after a camera purchase is a one-hop walk on requires. Suggesting a cheaper alternative when a shopper bounces from a high-priced SKU is a one-hop walk on is_substitute_for filtered by price band. Surfacing the printer ink that fits the printer they bought three months ago is a replenishes_to edge with a time gate.

If you have read our breakdown of knowledge graphs vs vector embeddings, this is the same argument in an ecommerce dialect. Embeddings give you fuzzy "looks similar" matches. The graph gives you specific, explainable, query-able relationships. Most production ecommerce stacks need both — the graph for typed relationships, embeddings for the long tail.

Graph-based recommendations are not new. A 2025 paper in Applied Artificial Intelligence describes a graph attention network approach that integrates user-item interaction graphs with user-user social graphs for personalized ecommerce recommendations — the same intuition, with neural weights on the edges.

Intent state tracking: researching, shopping, replenishing

Once the catalog graph is live, the next layer is figuring out what the shopper is actually doing right now. We model intent as a three-state machine for most consumer categories:

  1. Researching — comparing across brands, reading reviews, deep-linking from search. Recommend variety. Wide net on attributes. Surface comparison content.
  2. Shopping — narrowed to a category, viewing detail pages, checking sizes. Recommend depth: alternatives, bundles, "what others bought with this."
  3. Replenishing — repeat search for a known SKU, low-funnel landing, short session. Skip discovery. Show the exact item, one-tap reorder, subscription offer.

A new visitor browsing eight different running shoe brands in one session is researching. A logged-in customer with last_purchased: running_shoes_30_days_ago is replenishing. The same shoe page should rank very differently in those two states.

Most ecommerce recommendation engines we audit collapse all three states into a single "interest in running shoes" signal. That is why the homepage carousel feels stale: it is averaging across modes.

How to detect state cheaply

You do not need a deep model for this. The features that move the needle are:

  • Time since last purchase in the category (replenishment proxy)
  • Session entry path (search to researching, direct to replenishing, email depends on the campaign)
  • Cart presence and dwell time on product pages (shopping)
  • Recency of category browse vs. category buy

Score those into a 3-way logit and update on every event. We have seen state detection at this fidelity move add-to-cart on category pages without touching the underlying ranker, because the right items show up in the right slots.

Post-purchase intent: replenish, cross-sell, upgrade

The post-purchase window is the most underused real estate in ecommerce. The order confirmation page, the shipping email, the "your order has been delivered" notification — these are warm touchpoints with a shopper who has just spent money. They are not "thank you" moments. They are intent signals.

There are three distinct post-purchase intents and they require different rankers:

  • Replenish. The customer bought a consumable. Predict the reorder interval (skincare commonly around 30 days, printer ink several months, pet food often a handful of weeks depending on bag size) and trigger at that window. Shopify's 2026 AI recommendations guide notes that timing replenishment prompts to typical usage cycles materially improves conversion versus generic "buy again" emails.
  • Cross-sell. The customer bought a primary item that has typed complements. Camera to memory card, lens, case. Sofa to throw pillows, rug. This is a is_complement_of traversal, not a "frequently bought together" lookup, because the latter biases to high-volume cheap items.
  • Upgrade. The customer bought into a product family. Six to twelve months later, the higher tier or refresh model is relevant. This is the longest-cycle intent and the highest AOV when it lands.

Each of these intents lives on a different clock. Mixing them in one "recommended for you" widget dilutes all three. Split the surface — a "running low?" module, a "complete the set" module, and a "ready to upgrade?" module are stronger than a single ranked list.

The bigger point: post-purchase is not a marketing channel, it is an intent channel. Treat the order data the same way you treat clickstream — as a real-time feed into the personalization layer.

Lifecycle stitching across web, email, and app

The post-purchase signal only pays off if it flows back into ranking everywhere else. If a shopper buys a coffee grinder on the web, the next-week marketing email needs to know not to advertise grinders. The mobile app's home feed needs to surface the beans, the filters, and the milk frother instead.

This is the lifecycle stitching problem and it is mostly an identity problem dressed up as a personalization problem. Stitching requires:

  • A stable user key that survives logged-out browsing (device ID), logged-in sessions (user ID), and email opens (hashed email).
  • An event stream that all surfaces write into, not three disconnected analytics tools.
  • A read API that all surfaces query, with the same intent + attribute graph behind it.

Most ecommerce stacks have this on paper. In practice the web team uses one CDP, the email team uses ESP-resident segments, and the app team writes its own ranker. The result is the "did you mean to buy this?" email three days after the shopper bought it. We covered the engineering shape of fixing this in our reference architecture for real-time personalization — the short version is: one event bus, one feature store, one ranker, three surfaces.

When stitching works, lift compounds. A coordinated abandon-cart email, on-site re-rank, and app push that all reference the same SKU and the same shopper state will out-convert any of those three in isolation. Production-side numbers we have seen sit in the directional 15-25% incremental-order range, which lines up with the hybrid-system gains reported by Shopify and others.

Sub-100 ms re-rank: the latency budget

None of this is useful if the page is slow. The hard constraint on ecommerce personalization is that the search results page (SRP) and the category page need to render in under a 200 ms server-side budget. Personalization gets a slice of that — typically <100 ms end to end for the re-rank pass.

What fits in <100 ms:

  • A user-feature lookup from a feature store (target <10 ms)
  • A candidate retrieval from the catalog graph or a vector index (target <30 ms)
  • A re-rank pass over the top 200 candidates with a learned ranker (target <40 ms)
  • Diversity and business-rule constraints (target <10 ms)
  • Network and serialization overhead (target <10 ms)

What does not fit: a fresh embedding pass over the user's last 50 events, an LLM call, a fan-out to three different microservices. Move that work upstream. The session-time path should only look things up, never compute embeddings from scratch.

Two patterns we have seen work:

  1. Pre-computed user features, online retrieval. Update user vectors and intent state asynchronously after every event. At query time, just fetch the vector and the state, then retrieve and re-rank. Keeps p50 under <30 ms in practice.
  2. Two-stage ranker. A cheap candidate retrieval (graph walk, ANN search) gives 200-500 items, then a heavier ranker scores them. Most ecommerce platforms underinvest in the candidate generation step, which is where graph reasoning actually shines — typed edges give better candidates than blind vector neighbors.

If you are building this fresh, our five patterns for adding personalization covers the migration paths from "no personalization" to "real-time ranker."

How ×marble fits in

We built ×marble because the catalog-and-graph engineering above is genuinely hard to do in-house, and most ecommerce teams end up wiring together three vendors that do not speak to each other. ×marble is a personalization knowledge graph: it ingests your catalog, builds the attribute graph (intrinsic + use-case + complement + replenishment edges), tracks intent state across sessions and surfaces, and exposes a ranker with a <50 ms p50 read path.

The shape of the integration is small: send us your catalog and your event stream, point your SRP and category pages at our re-rank API, and read state back into your email and app. We also run personalization-as-a-service for adjacent surfaces — video at video.timesmarble.com and music at marblexmusic.com — but the core engine is what powers ecommerce ranking inside the product.

FAQ

What is ecommerce personalization?

Ecommerce personalization is the practice of ranking products, content, and offers for each shopper using their history, intent, and the catalog's structure. It is more than collaborative filtering — modern systems use attribute graphs, intent state, and post-purchase signals together to produce rankings that change on every event.

How does an ecommerce recommendation engine actually work?

A working ecommerce recommendation engine has three layers: a catalog model (products plus structured attributes and typed relationships), a user model (purchase history plus real-time intent), and a ranker that combines them. At query time, it retrieves candidate items from the catalog graph and re-ranks them against the user model, all within roughly 100 ms.

What is the difference between product personalization and product recommendation?

A recommendation is a single ranked list ("you might also like"). Personalization is the broader system that decides which list to show, in which slot, with which framing, based on the shopper's intent state. The same product can be a "replenish" surface for one shopper and a "discover" surface for another.

How long does ecommerce personalization take to implement?

The infrastructure (event tracking, identity stitching, catalog enrichment) typically takes 4-8 weeks of focused engineering. Visible ranker improvements land 2-4 weeks after that. Most teams underestimate the catalog enrichment step — it is where the lift actually lives and it is the slowest piece to fix because it crosses merchandising and engineering.

Why does my recommendation engine keep showing the same top sellers?

Almost always because the catalog has thin attributes and the engine is falling back to popularity priors. The fix is in the catalog: add complement edges, replenishment intervals, and use-case attributes so the engine has more than category + price to reason over. If you have read our piece on why collaborative filtering is aging, this is the same pattern in ecommerce form.

Further reading

the product behind these notes

×marble is the personalization graph.

One API. A living knowledge graph per user. Day-zero ready, explainable by construction. We built it so you don't have to.

See ×marble