Sponsored listings architecture
A sponsored listing is not a banner bolted onto a page. It is the output of an auction that runs on every impression, priced against the organic result it displaces. This is how that system is put together: how paid candidates are selected, how the auction sets a price, how spend is paced across a day, and how you know it paid off.
Three jobs: select the eligible ads, run an auction, place the winners.
A sponsored listings system answers a narrow question on every request: given the slots on this surface and the advertisers who want them, which paid items go where, and what does each one cost? That question is answered in three steps that run in sequence, and each step is doing a different job with a different failure mode.
01 ad selection
First you narrow millions of active campaigns down to the handful that are eligible for this specific impression: the ones whose targeting matches the user and context, whose budget is not exhausted, and whose creative is approved for this placement. This is the retrieval step of ads, and like feed retrieval it optimizes for recall and speed, not for final order. An advertiser filtered out here can never win, no matter how high they bid.
02 the auction
The eligible ads then compete. Each one carries a bid, but the bid alone does not decide the winner: the auction ranks on expected value to the platform, which folds together how much the advertiser will pay and how likely the ad is to be acted on. The output is an ordered set of winners and a price for each. This is where the money is decided, so it is where the rules have to be exact and defensible.
03 placement
Placement drops the auction winners into the surface. On a marketplace or search page that means interleaving paid results with the organic ranking without burying the results the user actually came for. It applies the limits a raw auction does not: how many sponsored slots per screen, how often one advertiser can appear, and where a paid item is allowed to sit. Then it logs every decision, because that log is the bill and the experiment record at once.
Five components, one priced decision.
The three steps map onto a small set of long-lived components. Naming them separately matters, because an ads stack has a constraint a pure ranking stack does not: it moves money, so budgets, prices, and billing events all have to reconcile to the cent.
p(click) and, where the objective
is a purchase or install, p(convert). This prediction is what turns a raw bid
into expected revenue, so it sits directly inside the auction rank.
The dependency to state plainly: the quality model is trained on clicks logged from ads the previous auction chose to show. The accuracy of tomorrow's pricing is bounded by how honestly you log today's, and unlike an organic feed, a bias here shows up directly as mispriced money.
Rank on eCPM, charge the second price.
The core of the system is one number computed per candidate: eCPM, effective cost per mille, the expected revenue from showing this ad to a thousand users. It is what lets a low bid on a highly clickable ad beat a high bid on an ad nobody touches, which is the property that keeps the surface useful.
eCPM: putting bids on a common scale
For a cost-per-click campaign the advertiser pays only when the ad is clicked, so the expected revenue per
impression is the bid times the click probability. Scaled to a thousand impressions:
eCPM = bid * p(click) * 1000. That is the number the auction ranks on. A $2.00 bid at
a 1% click rate has an eCPM of $20; a $5.00 bid at a 0.3% click rate has an eCPM of $15 and loses, even though
its bid is higher, because it is worth less per impression to the platform and less useful to the user.
quality score, and why it is not just p(click)
The action-rate term is often wrapped into a broader quality score that also carries relevance and post-click
experience, so that ads leading to a bad landing page are discounted even when they draw the click. Formalizing
that, rank by rank_score = bid * quality, where quality subsumes
p(click) plus any relevance and experience adjustments. The effect is the same: the auction
rewards ads that are both willing to pay and worth showing, and an advertiser cannot buy a top slot on budget alone.
first price vs second price vs GSP
The pricing rule decides what a winner actually pays. Under a first-price auction the winner pays its own bid, which is simple but pushes advertisers to constantly shade their bids downward to avoid overpaying, so prices get noisy and unstable. Under a second-price auction the winner pays the minimum it would have needed to bid to still win, which is the bid just below it, adjusted for quality. Second price is incentive-compatible in the single-slot case: the dominant strategy is to bid your true value, which makes bids honest and the market legible.
Most listing surfaces sell several slots at once, and the generalization is GSP, the generalized second-price
auction. Slots are filled in eCPM order, and each winner pays the amount that clears the bid of the competitor
directly beneath it. Concretely, the winner in slot i pays
price_i = (rank_score of bidder i+1) / quality_i, so the charge is expressed back in
cost-per-click terms the advertiser set out to pay. GSP is not perfectly truthful the way single-slot second
price is, but it is stable, easy to explain to advertisers, and it is what most real sponsored-listing markets
run.
Make a budget last the day, and a user not see the same ad ten times.
An auction with no throttle spends a campaign's daily budget as fast as impressions arrive, which for a popular advertiser means the budget is gone during the morning peak and the ad is dark for the rest of the day. Budget pacing spreads that spend across the hours the advertiser wants to reach, and frequency capping keeps one user from seeing the same ad past the point it works.
budget pacing
The common approach is a pacing multiplier applied to the bid before the auction. The system tracks the ratio of
spend so far to the ideal spend for this point in the day, and when a campaign is spending too fast it lowers
its multiplier so it wins fewer auctions, then raises it back when it falls behind. A simple control loop:
target = daily_budget * (elapsed / day), and if actual spend runs ahead of
target you damp the multiplier toward zero, if it runs behind you push it toward one. The
counters driving this live in the pacing component and update on a delay, which is why it is a controller and not
a hard gate.
# price the eligible ads and pick winners for the open slots def run_auction(user, ads, slots): live = [a for a in ads if budget_left(a) and not frequency_capped(user, a)] for a in live: pace = pacing_multiplier(a) # 0..1, throttles fast spenders a.rank = a.bid * pace * quality(user, a) # paced bid, weighted by quality ranked = sort_desc(live, by=lambda a: a.rank) winners = ranked[:slots] # GSP: each winner pays what clears the bidder just below it for i, a in enumerate(winners): below = ranked[i + 1] if i + 1 < len(ranked) else None a.price = (below.rank / a.quality) if below else reserve_price(a) return winners
frequency caps
Beyond some count, showing the same ad to the same person stops converting and starts annoying, and the annoyance has a cost the auction does not price. A frequency cap is a per-user, per-campaign counter checked at selection time: over-cap ads are filtered before they ever reach the auction. The counter has to be cheap to read on the request path and fast to write, which usually means a short-lived key in an in-memory store keyed by user and campaign, expiring on the cap window.
One decision, not two systems fighting over the page.
The tempting design is two independent systems: an organic ranker orders the results, an ads system picks winners, and a layout step jams the ads into fixed positions. It is easy to build and it slowly poisons the surface, because paid items land in slots the user's intent does not support and the page stops feeling trustworthy. Trust is the inventory, and it is expensive to win back.
The durable design puts paid and organic on one scale and resolves them in a single pass. Score the organic item
by its expected value to the user, v_organic = E[value | show], and the sponsored
item by its expected revenue net of the experience cost, v_ad = eCPM - penalty, where
the penalty prices the relevance gap so an off-topic ad has to pay more to earn the slot. Because both are value
per impression, they compare directly, and a paid item wins a position only when its
v_ad clears the v_organic it displaces. Relevance sets a floor
that spend cannot walk under. Cap sponsored density per screen, resolve placement in the same pass that produces the
organic order, and monetization stops fighting the ranking it sits inside.
Delivery metrics for health, incrementality for truth.
Two kinds of measurement do two different jobs. Delivery metrics tell you the marketplace is healthy and the machine is running. Incrementality tells you whether the ad actually caused anything, which is the only question an advertiser is really paying to answer, and the two are easy to confuse.
delivery and revenue metrics
The day-to-day dashboard is a small set of ratios. CTR, click-through rate, is
clicks / impressions. CVR, conversion rate, is
conversions / clicks. eCPM is realized revenue per thousand impressions, and it is
the one number that summarizes how well the auction is monetizing inventory. On the advertiser side the mirror
metric is ROAS, return on ad spend, revenue_to_advertiser / spend. Fill rate and
budget utilization round it out: they tell you whether demand is thick enough to fill the slots and whether
budgets are pacing out cleanly rather than starving or dumping.
incrementality is the hard part
A high conversion rate on an ad does not mean the ad caused the conversions. Much of it is people who would have bought anyway, and last-click attribution hands the ad full credit for them. The honest measure is incremental: run a holdout where a random slice of users is never shown the campaign, and compare converts between exposed and held-out groups. The lift is the causal effect, and it is routinely a fraction of what attribution reports. Building the holdout into delivery from the start is the only way to price ads on what they actually move rather than on what they take credit for.
The failures that show up in revenue, not in offline metrics.
When to own the auction, and when not to.
Everything above is buildable, and if sponsored listings are a core revenue surface you should probably build it. The auction is where a marketplace or a large catalog captures margin, and the pricing rules, the quality model, and the pacing controller are exactly the parts you want to tune yourself. If ads are the business and you have the ml and infrastructure headcount to keep the counters honest and the model fresh, build it.
The honest case for buying is narrower and real. The system never finishes: click models drift, advertisers adapt to your pricing, and every new placement needs its own pacing and caps, so the cost is a standing one, not a one-time build. If sponsored listings are necessary but not your differentiator, or you need paid slots working before you can justify an ads team, a decisioning API lets you send the eligible ads and the organic order you already have and get a blended, priced result back in the same call. That is the seam Gortex sits in. The sponsored listings API covers the auction, pacing, and blending described here, and solutions shows the same decisioning surface for adjacent problems. Buy to get moving or to skip undifferentiated work, build when the auction is the thing you are selling.