Design the Pinterest Home Feed
- Frame the home feed as a recommendation funnel over a billions-item corpus, not a social write-fan-out feed, and say why
- Build the funnel stage by stage, from parallel candidate generators to diversification
- Work two-tower retrieval, the multi-task ranker, SSD diversity, embedding sync and cold-start
Understanding the problem
A user opens Pinterest. Fill their home feed with the few hundred Pins, drawn from a corpus of billions, that this specific person should want to see next.
The phrasing sounds like the Meta or LinkedIn feed prompt, and that resemblance is the trap. There is no one you follow whose Pins get fanned out to your timeline. A Pin is an item in an interest corpus of a couple billion images, and the feed is the machinery that reaches into that corpus and finds the handful a given person wants next. So the first sentence out of your mouth sets the whole round: this is a recommendation funnel. Candidates, then ranking. Not a delivery system.
Hold that distinction, because the entire architecture follows from it. A social feed's hard problem is delivery: someone posts, you fan that write out to every follower's inbox, and you fight celebrity hot-spots and timeline storage. Pinterest barely writes anything on the fan-out path. Its hard problem is retrieval at the front: given a user, find good candidates among billions of items, fast, when there is no follow graph telling you which ones are even eligible. Embeddings and approximate nearest-neighbor search do that job. Then a multi-stage ranker sorts what retrieval found, and a diversification pass keeps the feed from collapsing into ten near-identical Pins.
Functional requirements
Scope it to the funnel and let the interviewer pull you wider. The core is: take a user opening the app, assemble a relevance-ranked, diverse page of Pins, and keep paginating as they scroll.
In scope
- Generate candidate Pins for a user from several retrievers running in parallel
- Retrieve from billions of Pins by embedding similarity over an ANN index
- Score and rank candidates with a multi-task model on repin, click, and hide
- Diversify and blend the ranked list before it reaches the client
- Paginate an infinite masonry scroll, and handle a brand-new user and a brand-new Pin
Below the line (name it, then skip)
- Visual and image search (its own design, the next lesson)
- Pin upload, image storage, the thumbnail pipeline, and the CDN
- Ads insertion and the ad auction (a separate funnel that blends into organic)
- The notification system and its volume control
- Offline training infrastructure beyond cadence and how it feeds serving
Non-functional requirements
Reason through the targets, because they explain why the funnel has the shape it does. The feed has to feel instant on app open, and it is a read-dominated system: vastly more feed requests than Pins created. That latency budget is what forces a funnel instead of one giant model. You cannot run a heavy neural ranker over billions of Pins per request, so you spend cheap compute to go from billions to thousands, then progressively more expensive compute on the survivors. Each stage is roughly an order of magnitude smaller and an order of magnitude more expensive per item than the one before it.
Relevance and freshness pull against each other and both matter. The feed should reflect what you engaged with seconds ago alongside your long-run taste, which means at least part of retrieval and ranking has to read very recent actions. New Pins have to become reachable quickly even though they have no engagement history yet, so the index has to keep ingesting new Pins within seconds. And the whole thing is multi-tenant across roughly half a billion monthly users with wildly different interest profiles, so personalization is computed fresh per request.
Estimate the load
Put down the numbers that are known, and be honest about the ones that are not. Pinterest publishes the corpus size and the top-level funnel ratio; it does not publish a full-funnel latency or QPS budget, so do not invent one. The one component number that is public is Pixie's, and it is from 2018.
- Corpus: Billions of Pins, ~500M users. The retrievable corpus is on the order of a couple billion Pins, served to roughly half a billion monthly users. There is no follow graph gating which Pins are eligible, so every Pin is a candidate for everyone, which is exactly why retrieval is the hard part.
- The published ratio: Billions to thousands. Pinterest states that retrieval narrows billions of Pins down to about a thousand or a few thousand candidates handed to ranking. The exact per-stage counts beyond that are not disclosed, so frame it as billions to thousands and resist quoting precise survivor counts you cannot source.
- Latency: Interactive, exact budget undisclosed. The feed must return fast enough to feel instant on app open, but Pinterest does not publish a full-funnel latency budget. The one public component figure is Pixie at roughly 1,200 requests per second per server at 60ms p99, and that is a 2018-era number for one retriever, not the whole feed.
- Refresh of the ranker: Retrained about twice a week. The full ranker is retrained roughly twice weekly to fight engagement decay, where a model trained on last week's behavior steadily loses accuracy. Item embeddings are precomputed offline and refreshed; the user side is computed live per request.
One number is worth deriving even though Pinterest does not publish it: the size of the item index. Take a couple billion Pins, each carrying one embedding of a few hundred dimensions stored as fp32. At 256 dimensions that is about 1KB per vector, so the raw item vectors come to roughly two terabytes before the HNSW graph adds its neighbor links on top. That doesn't fit in one machine's memory, and that fact explains two design choices. The item tower is precomputed in batch instead of per request. And the ANN index is sharded across many Manas hosts instead of living on one box. Hold the dimension loosely, it is our assumption rather than a published figure, but the order of magnitude is what forces the sharded, cached item side.
The shape that falls out: read-heavy, latency-bound, billions of eligible items with no graph to prune them, and a freshness requirement on both the items and the user's intent. That is the argument for a multi-stage retrieval-and-ranking funnel, which is what comes next.
The set-up
Core entities
Name the objects before you draw. The unusual ones here are embeddings, and the fact that a user is represented by more than one vector.
Pin and its item embedding
A Pin is an image plus metadata, and for retrieval what matters is its learned embedding, a vector that places it near related Pins in a shared space. Pinterest computes these with graph and content models (PinSage-style GraphSage embeddings over the Pin-board graph, plus visual signals). Item embeddings are precomputed offline and cached, because they change slowly and there are billions of them.
User represented by several embeddings
A user is several vectors. PinnerSage clusters a user's action history and represents each cluster by a medoid Pin, so one user becomes multiple embeddings, one per interest. Each is searched separately. This is why a single person can get cooking, hiking, and interior-design candidates in the same feed without those tastes averaging into mush.
Candidate
A Pin pulled by one of the retrievers as a plausible thing to show this user. The generators together produce thousands of candidates, often with overlap, which then get deduplicated and scored. A candidate carries which generator found it and a cheap retrieval score.
Realtime action sequence
The user's most recent actions, the latest roughly 100 of them, each carrying the engaged Pin's embedding, the action type, and a timestamp. This sequence is the feature that lets the feed react to what you did seconds ago, and it feeds both the live user embedding and the ranker.
One relationship carries the design: users and Pins live in the same embedding space, so "find Pins for this user" becomes "find item vectors near this user's vectors." That single idea is what replaces the follow graph. There is no edge saying you should see a Pin; there is geometry saying it is close to your interests. Everything downstream, the parallel retrievers, the approximate-nearest-neighbor index, the pre-ranker's dot product, is machinery for computing that closeness cheaply at this scale.
API
The client surface is thin, one call that returns a page and a cursor. The interesting interface is internal, between the funnel's stages. Keep the external call boring and spend your words on the retrieval and ranking RPCs.
High-level design
Build it as a pipe that narrows. A request comes in and the candidate generators each retrieve from the ANN index in parallel. Their outputs merge into a few thousand candidates, and light-weight scoring prunes that cheaply. The Pinnability ranker scores the survivors expensively, and diversification reshuffles the top into the page that ships. The diagram codes each box by role: blue for the request and the client, violet for the model-serving stages, amber for the blending pass, green for the corpus and the embedding index.
1) Build the user, retrieve in parallel. The request arrives carrying a user id and context, device, time of day, session, and no follow graph to lean on. The two-tower retriever computes the user's embedding online from their latest roughly 100 actions, and PinnerSage supplies several medoid embeddings, one per interest cluster. Each of those vectors issues its own nearest-neighbor query against the Manas HNSW index. It returns the k closest item vectors over billions of Pins in roughly logarithmic time. Pixie walks the Pin-board graph in parallel as one more source. The billions of item embeddings were computed offline and sit in the index already, so nothing on this path re-embeds the corpus. The open question this step leaves, how you actually search billions of vectors per request without a linear scan, is the two-tower retrieval deep dive.
2) Merge and pre-rank. The generators overlap, so their outputs are deduplicated into a few thousand candidates. Each is tagged with the retriever that found it and a cheap retrieval score. Light-weight scoring, a two-tower dot product, then ranks those few thousand and keeps only the few hundred strongest. This stage earns its place on the latency budget. The full ranker is too expensive to run over thousands of items inside an interactive response. A little compute here hands it a shortlist instead of a flood. Nothing fancy decides relevance yet. The pre-ranker is deliberately blunt, and the cost pressure that makes it necessary is the same pressure the ranker deep dive unpacks when it explains why the heavy model moved onto GPUs.
3) Rank for real. The few hundred survivors hit the Pinnability ranker. It encodes the user's realtime action sequence with a transformer: the latest roughly 100 actions, each represented by the engaged Pin's GraphSage embedding plus action type and timestamp. It crosses features explicitly with DCN-v2, and scores every candidate on repin, click, and hide at once through separate task heads. It runs on GPUs because the transformer added real latency, and it is retrained about twice a week so it does not drift as behavior shifts. Most of the relevance decision is made here, from per-Pin probabilities that get blended into one score. Why it predicts several actions instead of one click, and why the sequence is read in order, is the ranker deep dive.
4) Diversify and ship. A relevance ranker left alone will stack ten near-identical chairs at the top because each one scores well on its own. Sliding Spectrum Decomposition re-ranks the top of the list. Each placed Pin's value is its relevance minus a penalty for how much it resembles the Pins already chosen above it, in visual, text, and graph embedding space. The page, a few dozen Pins, then goes back to the client with a cursor for the next request. Scroll again and the whole funnel reruns with the now-updated action sequence, so a save you made ten seconds ago shifts what you see next. How the diversity penalty is tuned against relevance without flattening a genuinely deep interest is the diversity deep dive.
Technology choices
Every box is a category until you name a real system and defend it. Pinterest is public about these, so use the real names and be ready for the trade-off behind each.
- The retrieval index is an HNSW approximate-nearest-neighbor engine served on Manas, Pinterest's in-house retrieval system. HNSW stands for Hierarchical Navigable Small World. The vectors are wired into a layered graph where each node links to its nearest neighbors. A query hops greedily downhill toward its closest matches, answering a nearest-neighbor lookup over billions of vectors in roughly logarithmic time. You reach for an ANN index because the exact alternative, comparing the user vector against every item vector, is a linear scan of billions per request that no interactive latency budget survives. Manas runs two modes: a batch mode over static precomputed indices, and a realtime mode backed by an LSM engine fed from a stream. Expect a freshness probe: how does a brand-new Pin become reachable? The realtime mode answers it, since it ingests new vectors within seconds instead of waiting for the next batch build.
- The candidate generators run in parallel and stay heterogeneous, several different retrievers rather than one. Two-tower embedding retrieval carries the bulk of coverage. PinnerSage issues one nearest-neighbor query per interest cluster so distinct tastes don't average into mush. The older Pixie graph walk brings a different paradigm: proximity over the Pin-board graph instead of in vector space. The reason to run all of them is recall: each retriever has its own blind spots, and the union of their candidates beats whatever the single best one returns alone. The architecture makes this cheap to extend, since a new retriever is just one more parallel source whose candidates join the merge before scoring. If an interviewer asks why one strong two-tower model isn't enough, answer that no single retriever covers every way a Pin can be relevant. The merge closes the coverage gaps.
- The two rankers are a two-tower dot-product pre-ranker and a transformer-encoder plus DCN-v2 full ranker served on GPUs. The split comes from the same cost asymmetry the funnel is built around. A cheap model can afford to look at thousands of candidates, an expensive one only at a few hundred. So the cheap one runs first to protect the expensive one. Name the lineage, since it shows the cost climbing stage by stage. Gradient-boosted trees did early lightweight ranking. A two-tower DNN dot product took over pre-ranking, and the transformer-plus-DCN-v2 network became the full ranker. The probe here is usually about the GPUs. The transformer added enough latency that CPU serving stopped being viable. That's why the light-weight scoring stage has to exist, to keep the candidate set small.
- Diversification is Sliding Spectrum Decomposition in the blending stage, scoring similarity between Pins with their visual, text, and graph embeddings. It's written as an explicit objective instead of a heuristic cap like "no more than three chairs in a row." Each candidate's value is its relevance minus a penalty for overlapping the Pins already placed. That matters because category caps lean on labels that are coarse and often wrong, while an embedding penalty catches look-alikes that share no taxonomy at all. The trade-off it hands you is a single dial: lean too hard on the diversity penalty and you bury genuinely relevant Pins, lean too little and the feed clumps into near-duplicates. If asked how you'd tune it, say you measure both relevance and on-page variety and move the dial against live engagement. The right setting is product judgment rather than a fixed constant.
Potential deep dives
The signal lives in the stages. An interviewer here drives at how retrieval works, why ranking is multi-task, how diversity gets enforced, and the two failure modes that catch people: keeping embeddings comparable across model rollouts, and cold-start.
1) Two-tower retrieval: how do you search billions of Pins per request?
This is the heart of the design. The question underneath it: with no follow graph, how do you find a few thousand good Pins out of billions, fast, for a specific person? Work it as a progression.
Approach
Run a relevance model over the whole corpus for each request and take the top few thousand. Conceptually clean: the model sees everything, nothing good is missed at retrieval.
Challenges
It is a linear scan of billions of items per request, which no latency budget survives. Even a cheap model is hopeless at this scale and this QPS. The entire reason a funnel exists is that you cannot afford to look at every Pin, so any design that scores the full corpus per request has skipped the actual problem.
Approach
Train a two-tower model: a user tower and an item tower that map users and Pins into the same embedding space, trained so a user sits near Pins they engage with. Embed every Pin offline, embed the user at request time, and retrieve by similarity. Relevance becomes geometry, and you no longer need a graph edge to justify showing a Pin.
Challenges
The model is right but the search is naive. Computing the user-to-item dot product against every item vector is still a linear scan, just over vectors instead of Pins. You have the correct representation and an impractical lookup. You need a sublinear way to find the nearest item vectors, and you need the item side cached so you are not re-embedding billions of Pins per request.
Approach
Split the work by how often each side changes. The item tower runs offline: precompute every Pin's embedding in batch, refresh it, and load the vectors into an approximate-nearest-neighbor index (in-house HNSW on Manas). The user tower runs online, once per request, off the user's recent actions, because intent shifts within a session. Retrieval is then an ANN query, find the k item vectors nearest the user vector in roughly logarithmic time, returning thousands of candidates without touching the rest of the corpus. Run several such retrievers in parallel: PinnerSage issues one query per interest cluster so distinct tastes do not average out, and a realtime ANN mode keeps fresh Pins searchable within seconds.
Challenges
Two-tower retrieval cannot model fine user-item interactions, the towers only meet at a dot product, which is exactly why this is retrieval and not final ranking; the ranker downstream does the rich scoring. Training has its own trap: with only positives, the model collapses, so it trains against in-batch negatives with a sample-probability correction so popular Pins do not dominate. And approximate search trades a little recall for speed, so you tune the index for the recall the feed needs. The win is that retrieval is now sublinear and the heavy interaction modeling is deferred to a stage small enough to afford it.
2) The Pinnability ranker: why multi-task, and why a transformer?
Retrieval found a few thousand plausible Pins. Ranking decides their order, and this is where Pinterest spends real compute. Two design questions drive it: what does the model predict, and what does it read.
Start with what it predicts. A single "will they click" score is a weak target on Pinterest. The action that matters most is a save (a repin), and you also want to avoid showing things people hide. So Pinnability is multi-task: one network with several heads predicting repin/save as the primary objective, plus click, hide, and other engagement classes. The heads share a representation. Signals that predict a save also inform a hide, and learning them together is cheaper and more accurate than training separate models. The final ordering is a weighted combination of the head probabilities; the exact weights are proprietary, so describe the mechanism (a weighted blend of repin, click, and negative-hide probabilities) rather than inventing the coefficients.
Approach
Rank with a GBDT over engineered features: the user's long-run interest affinities, the Pin's historical engagement, freshness, simple cross features. This is fast, well understood, and was a real stage in Pinterest's lineage for lightweight ranking. It captures durable taste well.
Challenges
It misses sequence. A GBDT on aggregate features cannot easily express "this user just saved three camping Pins in the last minute, so lean camping right now." Recent-intent signal gets flattened into averages, and the feed feels slow to react within a session. You need the model to read the user's latest actions in order, not just their summary statistics.
Approach
Feed the model the user's realtime action sequence: the latest roughly 100 actions, each represented by the engaged Pin's GraphSage embedding plus action type and timestamp. Encode it with a transformer so order and recency carry weight. Add DCN-v2 to cross features explicitly, and keep the multiple task heads. Now the ranker reacts to intent from seconds ago and to long-run taste at once, and the same network scores repin, click, and hide together. Pinterest scaled the sequence length toward thousands of lifelong actions for deeper personalization. Serve it on GPUs, since the transformer added large latency, and retrain about twice a week to fight engagement decay.
Challenges
It is expensive: the transformer drove a move from CPU to GPU serving, which is the direct reason the light-weight scoring stage exists to keep the candidate set small enough to afford this. Twice-weekly retraining is a real pipeline obligation, since a stale ranker measurably loses repins. And a long action sequence raises a feature-freshness problem: the latest actions have to reach the model in near real time, which means a streaming feature path, not a nightly batch. The reported payoff was on the order of more repins and fewer hides, which is why the complexity earns its place.
3) Diversity: how do you stop ten near-identical Pins?
A ranker that scores relevance well will happily fill the top of the feed with ten almost-identical chairs, because each one is individually relevant. That is a worse experience than a few good chairs and some variety, so diversification earns its own stage.
Approach
After ranking, enforce a rule: no more than N Pins from the same board, category, or creator in a window. Simple, predictable, and it does break up the most obvious runs of duplicates.
Challenges
Hard caps are blunt. They depend on category labels that may be coarse or wrong. Two near-identical Pins can sit in different categories and slip through. And a fixed cap either over-restricts a deep interest or under-restricts a shallow one. You are approximating "looks too similar" with "shares a label," and similarity is not a label.
Approach
Make diversity an explicit objective in blending. Sliding Spectrum Decomposition scores each candidate as its relevance minus a penalty proportional to how much it overlaps, in embedding space, with the Pins already placed in the feed. As you slide down the page placing Pins, each new pick is discounted for resembling its predecessors, and a soft-spacing term pushes near-duplicates apart so they are not adjacent. Similarity is measured with real signals, visual, text, and graph embeddings, so it catches look-alikes that share no taxonomy. The result is a feed that stays relevant while visibly varying.
Challenges
It adds a tunable trade-off: lean too hard on the diversity penalty and you push down genuinely relevant Pins, lean too little and the feed clumps. The penalty is computed against items already placed, so it is inherently sequential within the page, which constrains how it parallelizes. And it leans on the embeddings being good, garbage similarity vectors give garbage diversity. The win is that diversity is now defined by how things look and relate, tuned as a relevance-versus-variety dial rather than a brittle per-category cap.
4) Keeping embeddings comparable across model rollouts, and cold-start
Two failure modes separate a senior answer here, and both are easy to miss because they only bite in production, not on the whiteboard.
The first is embedding version sync. The user tower runs online and the item embeddings were precomputed offline, and they must live in the same space for the dot product to mean anything. Ship a new embedding model and the two can drift out of alignment. The item index might still hold old-model vectors while the user tower computes new-model ones. A nearest-neighbor query across two different spaces returns garbage. The fix is to attach version metadata to the embeddings and the ANN hosts, so a query is only ever matched against item vectors from a compatible model version during a staged rollout. You roll the index and the tower forward together, never letting a request mix versions. Skipping this is the kind of bug that silently tanks relevance during a deploy and is hard to trace.
- Cold-start, new user. With little or no action history, the user towers and PinnerSage have nothing to cluster, so the personalized retrievers are weak. Fall back to popular and trending Pins, and lean on the onboarding interests the user picked at signup, conditioning retrieval on declared interests until real behavior accumulates. Each action they take feeds the realtime sequence, so personalization ramps within the first session rather than waiting for a nightly job.
- Cold-start, new Pin. A freshly created Pin has no engagement history, so any model that relies on past clicks cannot rank or even retrieve it. The escape is content-based embeddings: PinSage-style graph and visual signals give a brand-new Pin a vector from its image and its place in the Pin-board graph, no engagement required. Push it into the realtime ANN index (the streaming mode, fed within seconds) and it becomes retrievable almost immediately instead of waiting for the next batch index build.
- Why both matter in the round. They are the questions that show you have thought past the happy path. A funnel built only for established users looking at established Pins falls short for a growing product, and an interviewer who senses you have only the steady-state story will go straight here.
What is expected at each level
That is a lot of surface. How much you have to land depends on the level the loop is calibrated for, and at senior this design round is reported to set that level.
IC14 (mid-level)
Get the funnel right and resist the fan-out reflex. Reach a working end-to-end shape: candidate generation, retrieval, a ranking stage, and diversification, in that order. Say out loud that this is a recommendation funnel, so you never draw a timeline or an inbox. The non-negotiable is that retrieval is the hard part and it uses embeddings plus approximate-nearest-neighbor search over a billions-item corpus. The deeper modeling can surface in conversation.
IC15 (senior)
Depth now, and this is the level the round most often decides. You drive the two-tower split unprompted, user tower online, item tower precomputed, retrieval via HNSW ANN, and you justify the multi-stage funnel as a latency-versus-cost progression. You explain why ranking is multi-task (repin primary, click, hide) and why a single click model would rank the wrong things, and you reach diversification as its own objective rather than a category cap. The bar is a near-complete funnel where you found the retrieval and ranking structure yourself.
IC16+ (staff and beyond)
Depth leads as the scope widens. Expect the realtime action sequence and why the ranker is a transformer over it. Reason about SSD as relevance minus a spectral penalty over embeddings. Raise and solve the embedding version-sync problem without prompting, and handle cold-start for both new users and new Pins. You reason about how the system degrades, a stale ranker, a drifting index, a hot interest, and you hold the wheel while the interviewer pushes from the edges. The bar is that every stage is a deliberate, defended choice and the funnel is yours, not a recited diagram.
Key Takeaways
- ✓The Pinterest home feed is a recommendation funnel over a billions-item interest corpus, not a social write-fan-out feed. There is no follow graph to deliver from, so the hard part moves to the front: retrieval. Open by saying "candidates, then ranking," and consciously do not reuse a Meta or LinkedIn timeline diagram.
- ✓The funnel narrows billions of Pins to thousands, then to a page, through stages that get smaller and more expensive. They are parallel candidate generators, an HNSW ANN index, light-weight scoring, the Pinnability full ranker, and diversification.
- ✓Retrieval is two-tower with the user tower computed online and the item tower precomputed and cached, searched by approximate nearest neighbor (HNSW on Manas). Ranking is a multi-task transformer-plus-DCN-v2 model over the recent action sequence, predicting repin (primary), click, and hide, GPU-served and retrained about twice a week. Diversity is Sliding Spectrum Decomposition: relevance minus a spectral similarity penalty.
- ✓The senior signal is in the production failure modes. Version user and item embeddings so they stay in the same space across model rollouts. Solve cold-start with trending plus onboarding interests for new users, and content embeddings plus a realtime index for new Pins.
Pinterest interview guideKnow what Pinterest asks before you walk in.
14 coding · 5 system design · 16 behavioral questions
Get their questions first.
InterviewLegend is independent and not affiliated with or endorsed by any company it covers.