# guide // sponsored listings

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.

# 01 / what it actually does

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.

# 02 / the architecture

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.

01 ad selection
Produces eligible candidates. Matches targeting against the user and context, then filters on budget remaining, pacing state, and creative approval. Optimized for recall and latency: it hands the auction a small, clean set to price.
02 quality model
Predicts the action rate for each candidate, typically 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.
03 auction
Ranks candidates by eCPM, selects the winners for the available slots, and computes what each pays under the pricing rule. It is deterministic and auditable: given the same inputs it must return the same result and the same price, because an advertiser can be charged for it.
04 pacing and budgets
Owns the counters. Tracks spend against each campaign's daily and lifetime budget in near real time, throttles delivery so a budget lasts the day rather than emptying by 9am, and enforces frequency caps per user. It is the component most likely to be eventually consistent, and the one where that matters most.
05 logging and billing
Records every auction: who competed, who won, the clearing price, the slot, and what the user did next. This log is the source of truth for billing, for the next quality model, and for every measurement downstream. Impressions and clicks are deduplicated and reconciled here before anyone is charged.

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.

# 03 / the auction

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.

# 04 / pacing and frequency caps

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.

auction.py
request path
# 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.

# 05 / blending paid and organic

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.

# 06 / measurement

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.

# 07 / common pitfalls

The failures that show up in revenue, not in offline metrics.

01 pacing runaway
Budget counters that update on a long delay let campaigns overspend a burst before the throttle catches up, so budgets empty at the morning peak and advertisers get billed past their cap. Pace on near-real-time spend and reconcile against the ledger, or the marketplace loses trust on both sides.
02 click inflation
The quality model is trained on logged clicks, and top slots get clicked because they are on top, not only because they are good. Train on raw clicks and you teach the model to keep whoever already won, which entrenches incumbents and misprices everyone else. Correct for position with logged propensities or randomized slots.
03 too much paid density
Every extra sponsored slot lifts today's revenue and quietly lowers the value of the surface, until users trust the results less and engage less. The optimum is not the maximum. Hold sponsored density to a cap you defend with a long-run engagement guardrail, not a quarterly revenue target.
04 attribution as truth
Reporting last-click conversions as the value of the ads program overstates it, often by multiples, because most of those buyers were going to convert anyway. Without a holdout you are optimizing a number that does not measure what you sold. Bake incrementality testing into delivery, not into a one-off study.
# 08 / build vs buy

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.