100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace

How to Design an Ad Serving System?

Learn how to design a low-latency ad serving system: in-memory targeting, eCPM ranking, budget pacing, and async billing pipelines.

hardQ97 of 224 in System Design Est. time: 6 minsLast updated:
Open Code Lab

Expected Interview Answer

An ad serving system is designed around an ultra-low-latency ad-selection path (typically under 100ms) that ranks pre-indexed, targeted candidate ads from an in-memory store using pacing and budget checks, while logging impressions and clicks asynchronously to an event pipeline for billing, analytics, and machine-learning feedback without ever blocking the response to the user.

When a page or app requests an ad, the ad server must select a winning creative from thousands of active campaigns within a strict latency budget, so candidate ads are pre-filtered by targeting rules (geography, demographics, keywords) using an inverted index held largely in memory (Redis or a custom in-memory store), then ranked by an eligibility and bid score (often eCPM = bid × predicted click-through rate) with real-time budget-pacing checks to ensure a campaign does not overspend its daily budget in the first hour. The winning ad is returned immediately, and the impression event is fired asynchronously into a high-throughput event pipeline (Kafka) that feeds billing/spend deduction, frequency-capping counters, and ML models that retrain click-through predictions. Frequency capping (limiting how many times a user sees the same ad) is enforced via a fast per-user counter (Redis with a sliding window or bloom filter for approximate counts at extreme scale). The whole path must be horizontally scalable and geographically distributed via edge points-of-presence to keep latency low for global traffic, and campaign budget state must eventually reconcile from the async event stream even though the hot path used an approximate, fast-check budget cache.

  • Pre-indexed, in-memory targeting keeps ad selection within a strict latency budget
  • Asynchronous event logging for billing/analytics never blocks the user-facing response
  • Real-time (approximate) budget pacing prevents a campaign from overspending before reconciliation catches up
  • Frequency capping improves user experience and ad relevance without slowing the hot path

AI Mentor Explanation

An ad serving system is like a stadium’s LED perimeter board choosing which sponsor’s ad to flash during the next ball, out of dozens of paying sponsors, all within the few seconds between deliveries. The board operator has pre-sorted sponsors by how much they paid and how relevant they are to the current match segment, so the pick is instant rather than negotiated fresh each time. Afterward, someone logs which sponsor’s ad ran and for how long, purely for billing, without slowing down the next flash of the board. That instant, pre-ranked pick followed by asynchronous billing is exactly how an ad serving system operates.

Step-by-Step Explanation

  1. Step 1

    Pre-index campaigns by targeting rule

    Active campaigns are indexed in memory by geography, demographic, and keyword targeting so candidate lookup at request time is fast.

  2. Step 2

    Rank candidates by eCPM with budget pacing

    Eligible ads are scored (bid × predicted CTR) and filtered against a fast, approximate remaining-budget check to enforce pacing.

  3. Step 3

    Return the winner and fire async events

    The winning creative is returned immediately; impression/click events are pushed to an event pipeline (e.g., Kafka) without blocking the response.

  4. Step 4

    Reconcile billing and retrain models offline

    The async event stream drives accurate billing/spend reconciliation, frequency-cap counters, and periodic retraining of click-through prediction models.

What Interviewer Expects

  • Identifies the strict low-latency requirement (typically <100ms) driving in-memory, pre-indexed ad selection
  • Explains eCPM-based ranking and real-time budget pacing on the hot path
  • Describes asynchronous, non-blocking event logging for billing/analytics via a stream like Kafka
  • Mentions frequency capping and geographic distribution (edge POPs) for global scale

Common Mistakes

  • Querying a database live for every ad request instead of using a pre-indexed in-memory candidate set
  • Doing synchronous billing/logging on the hot path, adding latency to every ad response
  • Ignoring budget pacing, letting a campaign exhaust its entire daily budget in minutes
  • Forgetting frequency capping, leading to poor user experience from repeated identical ads

Best Answer (HR Friendly)

An ad serving system has to pick the best ad out of thousands of options in a fraction of a second, so I would keep eligible, targeted ads pre-ranked in memory rather than querying a database live. Once an ad is chosen and shown, all the billing and tracking work happens in the background afterward, so it never slows down what the user actually sees.

Code Example

Ad selection hot path with async event logging
def select_ad(user_context, ad_slot):
    candidates = ad_index.lookup(
        geo=user_context.geo,
        interests=user_context.interests,
        slot=ad_slot,
    )

    eligible = [
        c for c in candidates
        if budget_cache.has_remaining_budget(c.campaign_id)
        and frequency_cap.under_limit(user_context.user_id, c.campaign_id)
    ]

    if not eligible:
        return None

    winner = max(eligible, key=lambda c: c.bid * c.predicted_ctr)

    event_queue.publish_async("impression", {
        "campaign_id": winner.campaign_id,
        "user_id": user_context.user_id,
        "bid": winner.bid,
        "timestamp": now_ms(),
    })

    return winner.creative

Follow-up Questions

  • How would you make budget pacing accurate across thousands of ad servers without a synchronous global lock?
  • How would you detect and filter click fraud in the event pipeline?
  • How would you keep the in-memory ad index fresh as campaigns are created, paused, or exhausted?
  • How would you reduce latency for users in regions far from your primary data center?

MCQ Practice

1. Why do ad serving systems select from a pre-indexed, largely in-memory candidate set instead of querying a database live per request?

Ad selection must happen extremely fast, so campaigns are pre-filtered and ranked from an in-memory index rather than queried live.

2. Why is impression/click logging done asynchronously rather than synchronously before returning the ad?

Blocking the response on billing/analytics writes would slow every ad request, so those are fired into an async event pipeline instead.

3. What is the purpose of real-time budget pacing in an ad serving system?

Pacing checks approximate remaining budget on the hot path so high-traffic bursts do not exhaust a campaign’s budget prematurely.

Flash Cards

Why is ad selection latency-critical?Ad responses typically must return within tens of milliseconds so page/app rendering is not delayed.

What is eCPM ranking?Ranking candidate ads by bid × predicted click-through rate to estimate expected revenue per impression.

Why log events asynchronously?So billing, analytics, and ML feedback never block or slow down the user-facing ad response.

What is frequency capping?Limiting how many times the same user is shown a given ad, enforced via a fast per-user counter.

1 / 4

Continue Learning