Designing a Generalized Ads Marketplace Platform

Building a platform is different from building a product. Learn how Uber evolved its ads exchange into a generalized marketplace platform through stable abstractions, composition, and fleet isolation.

۱۰ دقیقه مطالعهمتن اصلی

Senior Staff Software Engineer

Introduction

Uber Ads initially focused on Sponsored Listings, where a relatively centralized serving architecture was sufficient to support the product’s needs. As the business grew, the platform expanded to support Sponsored Items, Shoppable Display Ads, Display Banners, Video Ads, and more.

As new products and surfaces were introduced, the serving stack began evolving in different directions. Even within Sponsored Listings, retrieval, scoring, and bidding logic varied significantly across surfaces. For example, the home feed relied on ranking tightly coupled with organic content, while search ads depended heavily on upstream retrieval and scoring systems shared with the organic search stack.

At the same time, the organization planned to consolidate the Uber Eats ads platform with another ad server powering ads on the Rider app and other display-heavy surfaces. Supporting these use cases required the marketplace to evolve beyond native restaurant ads into a generalized advertising platform capable of handling heterogeneous formats, ad supply sources, and buying models.

As the engineering organization grew, operational complexity increased as well. More teams were contributing to the shared codebase, making deployments, ownership boundaries, and runtime isolation increasingly difficult to manage.

By early 2025, it became clear that the existing architecture was no longer well-suited for the growing diversity of products, surfaces, and operational requirements. To address these challenges, the team revisited the architecture of the ad marketplace with the goal of building a more flexible and extensible platform for future growth.

Generalized Marketplace Architecture

The redesign focused on several architectural principles.

First, the marketplace needed to remain product-agnostic while supporting different supply sources, buying models, and serving flows.

Second, the platform needed clear operational and organizational boundaries as more teams and integrations onboarded.

Finally, the team wanted to preserve a single shared codebase while allowing marketplace capabilities to be composed differently across production fleets through configuration.

These goals led to a generalized marketplace architecture with explicit separation between supply, marketplace, and demand responsibilities.

Supply, Marketplace, and Demand Separation

These design goals led us toward a more generalized marketplace architecture with explicit separation between supply, marketplace, and demand responsibilities.

At a high level, the architecture resembles a traditional ad exchange connecting publishers and advertisers. However, because Uber owns both the supply side and most of the demand sources, the platform can exchange richer contextual information between components than is typically possible in open RTB (real-time bidding) ecosystems. The architecture is logically divided into three layers: the supply side, the marketplace, and the demand side.

The supply side layer is responsible for handling incoming requests from different product surfaces and converting them into a normalized marketplace request. Each surface can provide its own contextual information and constraints, such as candidate entities, placement metadata, supported ad formats, or trip-related signals.

The marketplace layer acts as the orchestration layer. It processes the marketplace request, selects the appropriate auction or deal strategy, invokes eligible demand providers, and ultimately determines the winning ads returned to the caller.

Importantly, the marketplace itself remains largely product-agnostic. Surface-specific logic is isolated at the supply layer, while bidding logic resides within demand providers. This allows the marketplace to support multiple ad products and buying models without embedding product-specific behavior directly into the core auctioning logic.
Demand providers are responsible for retrieving and evaluating eligible ads, computing relevance and prediction signals (such as pCTR and pCTO), and calculating bids according to the requested buying model and auction strategy.

This separation allowed the platform to support heterogeneous supply and demand sources while preserving a shared marketplace abstraction, clearer ownership boundaries, and better operational scalability.

Flowchart of an ad-exchange marketplace connecting supply sources, auctions, deal strategies, and demand providers.

Figure 1: Generalized Ads Marketplace architecture.

Marketplace Spec Abstraction

While the separation between supply, marketplace, and demand layers established clear ownership boundaries, the marketplace still needed a consistent way to orchestrate requests coming from fundamentally different surfaces and buying models.

For example, Search ads relied on candidate stores retrieved upstream as part of the organic retrieval flow, while Rider display and video ads required trip-related context and different auctioning behaviors. Embedding these surface-specific details directly into the marketplace layer would quickly turn the system into a collection of tightly coupled conditional flows.

To address this, the platform introduced a normalized marketplace abstraction called the AdSpec. The AdSpec contains the subset of information required by the marketplace layer to make orchestration decisions, including:

  • auction or deal strategy
  • eligible demand providers
  • supported ad formats
  • pricing and auction configuration

A simplified version of the abstraction includes:

  • ad slots count
  • deal type
  • supported formats
  • cost model
  • auction configuration

The AdSpec is populated by the supply layer based on the requirements of a specific surface.

For example, home feed ads may generate an AdSpec configured for sponsored listings, 5 ad slots, CPC pricing, and a second-price private marketplace auction. Meanwhile, journey ads on the Rider app may produce an AdSpec configured for display and video formats, a single ad slot, preferred deals, and fall back to an open auction.

As a result, the marketplace remains largely product-agnostic, while new ad surfaces, formats, and buying models can be introduced without deeply modifying the core marketplace logic.

Extension-Based Request Model

While the AdSpec standardized the information required for marketplace orchestration, different surfaces still needed to pass additional contextual data required by specific demand providers.

For example:

  • Search-related ads may require candidate stores retrieved upstream
  • Rider surfaces may provide trip-related signals
  • Storefront experiences may include carousel or section metadata

Embedding all possible contextual attributes directly into the shared marketplace request would make the schema increasingly difficult to evolve over time. As more products and surfaces were introduced, the request model would gradually turn into a large, tightly coupled object shared across unrelated use cases.

To avoid this, we introduced an extension-based request model.

Surface-specific contextual data is attached to the marketplace request as logically isolated extensions. Demand providers can then selectively consume only ‌extensions relevant to their use cases, while the marketplace layer itself remains largely agnostic to their content.

Figure 2 shows how extensions are passed to demand providers through the marketplace orchestrator.

Figure 2: Extension-based request flow.

This approach provided several important benefits:

  • new contextual signals could be introduced without modifying the core marketplace schema
  • unrelated use cases remained logically isolated from one another
  • deprecated features could be removed by cleaning up individual extensions instead of refactoring shared marketplace logic
  • demand providers could evolve independently while still interacting through a common marketplace abstraction

Together, the AdSpec abstraction and extension-based request model allowed the marketplace to remain generic while supporting increasingly heterogeneous ad products and serving flows.

API Boundary Design

As the marketplace evolved to support multiple surfaces and ad formats, API boundaries became an increasingly important architectural consideration.

Different product experiences often required multiple ad placements to be resolved together within a single request. For example, a home feed response could contain sponsored listings, shoppable display units, and display banners simultaneously. At the same time, the platform needed to preserve clear ownership boundaries, observability, and operational flexibility across heterogeneous serving flows.

Ultimately, the platform aligned on page-level endpoints. This model provided a balance between flexibility and operational simplicity. A single request could return multiple ad formats for a page while still preserving clear boundaries around business logic, observability, and infrastructure controls.

Page-level APIs also aligned naturally with several operational requirements:

  • Clearer ownership boundaries for product-specific serving logic
  • Endpoint-level observability and alerting
  • Independent validation and fallback behavior
  • Simpler rate limiting and traffic management
  • Manageable endpoint growth as new formats and surfaces were introduced

Demand Providers and Auction Strategies

To support multiple buying models and heterogeneous demand sources, the marketplace separated deal orchestration from bid generation.

The marketplace layer is responsible for selecting the appropriate deal strategy for a request, while demand providers are responsible for retrieving ads, calculating bids, and preparing auction candidates.

Initially, the platform supported two primary deal strategies: auction-based marketplace and waterfall/preferred deals.

In the auction-based flow, eligible demand providers return bids that participate in either first-price or second-price auctions to determine the winning ads. This strategy is primarily used for private marketplace-style auctioning flows, such as sponsored listings and sponsored items.

In the preferred deals flow, the marketplace selects ads according to a predefined priority of demand sources, with optional fallback to auction-based bidding when no eligible preferred deal candidates are available. This model is primarily used for guaranteed or reserved inventory flows, such as certain display and video placements.

Depending on the selected deal strategy, demand providers may return either auction bids or fully qualified ads ready for delivery.

Demand providers are composed using shared marketplace capabilities such as campaign hydration, pacing, ranking, bid calculation, validation, and filtering. This allowed different serving flows to reuse common marketplace components while still supporting custom retrieval strategies.

For example, some providers consume candidates already attached to the request by the supply layer, while others retrieve candidates through dedicated upstream services before reusing the same shared marketplace components to construct bids.

The architecture also supports external demand providers that adapt bids sourced outside of Uber’s marketplace ecosystem through normalization, validation, deduplication, and enrichment steps before participating in marketplace auctions.

By separating orchestration strategies from demand-provider implementations, the marketplace could support multiple serving models and integrations without tightly coupling them to the core auctioning logic.

Operational Architecture and Fleet Isolation

The generalized marketplace architecture introduced clear logical boundaries between the supply, marketplace, and demand layers. However, as the platform evolved into a shared system serving multiple products and traffic types, operational scalability became equally important.

Runtime Failure Isolation

The marketplace controller isolates failures originating from individual demand providers without failing the entire request. When a provider experiences errors, timeouts, or invalid responses, the marketplace continues evaluating remaining eligible providers and completes the auction using the available candidates.

The marketplace also emits provider-level metrics and operational signals routed to the corresponding owning teams. This establishes clear operational ownership boundaries while improving overall marketplace resiliency.

Fleet Isolation

While provider-level isolation improved request resiliency, it didn’t fully address broader operational concerns.

Initially, all marketplace traffic was served from a shared production fleet. As the platform expanded to support native in-app ads, Rider display ads, and offsite programmatic traffic, this model created large blast radiuses, deployment coupling across teams, and increased operational risk from external integrations.

For example, a problematic deployment related to display ads could require rolling back unrelated changes supporting sponsored listings or offsite advertising flows.

To address these challenges, the platform introduced multiple isolated production fleets of the ad-exchange service.

Rather than creating separate ad servers for each product area, all fleets continue running the same shared marketplace codebase, while differences between fleets are defined through configuration and platform capability composition instead of code-level branching.

Figure 3: Environment-based fleet isolation architecture.

This approach provided several operational benefits:

  • Reduced blast radius by isolating failures and high-risk traffic
  • Independent deployments and rollbacks across teams and traffic types
  • A shared marketplace platform without fragmenting into multiple ad servers
  • Flexibility to support different integrations and runtime configurations per fleet

Conclusion

The transition from a product-specific serving stack to a generalized marketplace required new abstractions and a fundamental shift in our operational and organizational boundaries. As new products, formats, and external integrations continue to emerge, we’ve walked away with three core engineering insights that guide how we scale platform systems at Uber.

Building a Platform Requires Shifting from Features to Capabilities

Product-specific systems optimize for immediate use cases. Platforms, however, must expose reusable capabilities that can be composed differently across products and traffic types.

Rather than introducing code-level branches for every new ad format or serving flow, we focused on building configurable platform capabilities—such as pacing, campaign hydration, and pricing models—that could be selectively enabled depending on the use case.

Protect the Core by Pushing Complexity to the Edges

As the number of products and integrations increased, keeping the marketplace core stable became critical for long-term scalability.

The marketplace remained intentionally focused on stable orchestration concepts such as auctions, deal strategies, and provider selection, while surface-specific variability was isolated within supply adapters, request extensions, and demand providers. This allowed new serving flows and integrations to evolve independently without continuously increasing the complexity of the marketplace core.

Match Your Infrastructure Boundaries to Your Organizational Boundaries

Runtime isolation alone was insufficient once the exchange became a shared platform serving heterogeneous traffic classes and multiple engineering teams.

Introducing environment-isolated production fleets reduced deployment coupling, limited the blast radius of failures, and allowed teams to deploy, scale, and evolve independently while still sharing the same marketplace platform and codebase.

As the platform continues to evolve, the generalized marketplace architecture provides a flexible foundation for onboarding new ad products, demand sources, and buying models without fundamentally changing the core orchestration model.

Acknowledgments

Cover Photo Attribution: The cover photo was generated by AI using Google Gemini.

Yuriy Bondaruk

Senior Staff Software Engineer

Yuriy Bondaruk is a Senior Staff Software Engineer at Uber, where he works on large-scale ads marketplace and serving systems. He’s been a founding engineer of Uber Ads and focuses on marketplace architecture, real-time bidding systems, and platform scalability.

منتشرشده در

Uber Engineering

خواندن در منبع

ادامه مطالعه