Before you begin: Your container should already handle the DSP
Demand-Side Platform (DSP). A software platform that automates bidding decisions in real-time and efficiently connects buyers and audiences through an ad exchange or SSP. Also known as a buy-side platform. Bid Response
An OpenRTB response that is sent by the DSP in response to the SSP's or ad exchange's bid request. It is an event directed back to the seller expressing a valuation for the request and conditions of sale. extension point in general. See Build a gRPC RTD service.
Bid shading lets your container recommend a lower transaction price for a DSP bid before Index Exchange (Index) selects a winner, reducing buyer cost without reducing win rates. This topic is the single home for everything specific to bid shading: the response structure, the business rules, and how to build and test your handler. If you already have a container set up for the DSP Bid Response extension point in general, this topic is all you need to add bid shading to it.
Where bid shading is supported
Bid shading is available under the following conditions:
| Dimension | Support |
|---|---|
Protocol | gRPC only. Not supported on the HTTP integration. |
Extension point | DSP Bid Response extension point only ( |
Hosting model | Partner-hosted and Index Cloud. |
Mutation |
|
First-price only. No effect in second-price auctions. | |
Deals you own only. There is no path today to request permissioning on a deal owned by another partner. |
How it fits into the auction
Bid shading operates at the DSP Bid Response extension point, after DSPs have returned their bids and before Index picks a winner. Your container evaluates each eligible bid and returns a BID_SHADE mutation recommending a lower price. If bid shading is applied, the buyer pays the shaded price instead of their original bid. To learn how to set up a container for the extension point in general, see Build a gRPC RTD service, and Building your container.
Response structure
When lifecycle is LIFECYCLE_DSP_BID_RESPONSE, the bid_response field on the RTBRequest is populated. The following fields are what you need for shading decisions:
Note: bid_request is still present and required at this extension point, even though you are acting on bid_response. For the full field list on both objects, including privacy redaction rules, see the DSP bid response payload reference.
Mutation schema
The BID_SHADE mutation uses the following schema:
| Field | Value |
|---|---|
|
|
|
|
|
|
|
|
{
"intent": "BID_SHADE",
"op": "OPERATION_REPLACE",
"path": "/seatbid/123/bid/456",
"adjustBid": { "price": 1.85 }
}Business rules
The following rules govern how Index applies your bid shading mutations:
Strictly less than the original bid. The adjusted price must be greater than zero and less than the original DSP bid price. Mutations violating this constraint are silently dropped.
The floor still applies. You can shade below the deal or impression floor, but the bid is then blocked by the floor check rather than transacting at the shaded price. Only shade below the floor if you explicitly intend to suppress the bid; it is not recommended otherwise.
Deals you own only. Bid shading mutations on a deal you own are accepted by default. There is currently no path to request permissioning on a deal owned by another partner. Attempting to shade a bid on a deal you do not own results in that mutation being silently dropped.
One mutation per bid. You can shade multiple bids in a single response; return one mutation per bid.
Omit to skip. If you do not want to shade a bid, do not return a mutation for it. Omitting a bid leaves it at its original price.
Mutations are atomic. If one mutation fails validation, it is dropped. Other valid mutations in the response are still applied.
Handler implementation
Route on lifecycle to separate Publisher Request logic from Bid Response logic. For bid shading, only act on LIFECYCLE_DSP_BID_RESPONSE:
func (a *Agent) handleBidResponse(ctx context.Context, req *pb.RTBRequest) (*pb.RTBResponse, error) {
var mutations []*pb.Mutation
for _, seatBid := range req.GetBidResponse().GetSeatbid() {
seat := seatBid.GetSeat()
for _, bid := range seatBid.GetBid() {
// Skip non-deal bids
if bid.GetDealid() == "" {
continue
}
// Skip if BID_SHADE isn't applicable for this request
if !isIntentApplicable(req.GetApplicableIntents(), pb.Intent_BID_SHADE) {
continue
}
price := bid.GetPrice()
shadedPrice := a.model.Shade(bid, req.GetBidRequest())
if shadedPrice >= price || shadedPrice <= 0 {
continue // no shade: omit the mutation
}
// Path is /seatbid/<seatID>/bid/<bidID>: seat + bid ID, not imp ID.
mutations = append(mutations, buildAdjustBidMutation(seat, bid.GetId(), shadedPrice))
}
}
return &pb.RTBResponse{Id: req.Id, Mutations: mutations}, nil
}Note: If a mutation type is returned that was not in applicable_intents, Index silently discards it. Always filter mutations against applicable_intents before building your response.
Performance considerations
The 5ms SLA applies to your entire handler. Bid shading containers typically process multiple bids per call, so per-bid latency must be kept very low.
Pre-load all model data at startup. Do not make external calls during request processing.
Keep inference lightweight. Lookup tables and simple regression models suit this latency profile well. Deep learning inference at this budget requires careful optimization.
Use the tmax
The maximum time, in milliseconds, to submit a bid to avoid timeout. The minimum tmax value that Index allows on a bid request is 100ms. If required, we subtract latency when passing the tmax value to DSPs. deadline. Wrap inner work with a context deadline derived from req.Tmax. Return whatever mutations are ready if the deadline is approaching.Avoid per-bid allocation. At high QPS
Queries Per Second (QPS). The number of bid requests a DSP processes per second. Also known as impressions per second., small per-bid allocations compound. Use pre-allocated buffers and object pools.
Testing validation checklist
Note: Validate the following before requesting deployment. Required items are deployment blockers. To send test traffic to your container, see Using the Index testing tool; use the -lifecycle LIFECYCLE_DSP_BID_RESPONSE flag to generate bid shading test requests.
Required: Adjusted prices are always less than the original bid price and greater than zero.
Required: The container returns a valid
RTBResponse.idechoing the request ID.Required: Handler latency is consistently under 5ms at p95 on your test hardware.
Required: The container returns an empty mutations list, not an error, for bids it chooses not to shade.
The following example shows a request and its expected response:
// request (numeric or string enum both work with freshly generated bindings)
{ "lifecycle": "LIFECYCLE_DSP_BID_RESPONSE", "id": "req-1", "tmax": 5,
"bid_request": { "id": "brq-1", "imp": [ { "id": "imp-1", "bidfloor": 2.00 } ] },
"bid_response": { "seatbid": [ { "seat": "dsp-42",
"bid": [ { "id": "bid-1", "impid": "imp-1", "dealid": "deal-x", "price": 4.00 } ] } ] } }
// expected response (shade factor 0.85 => 3.40)
{ "id": "req-1", "mutations": [ { "intent": "BID_SHADE", "op": "OPERATION_REPLACE",
"path": "/seatbid/dsp-42/bid/bid-1", "adjustBid": { "price": 3.40 } } ] }Reporting
Note: Bid shading performance reporting (Grafana metrics and audit log fields for training your model) is not yet available for partners. This section will be updated once that capability ships.
What's covered elsewhere
The following are not specific to bid shading and are covered where they apply to any container implementation:
| Topic | What it covers |
|---|---|
The full field list for both | |
General container setup, the classification endpoint | |
The testing tool for sending test traffic to your container. Use the | |
Building your container and Validating and deploying your container | Packaging, deploying, and onboarding your container in Index Cloud, including what bid shading partners should confirm during scoping. |
Next: If your container already handles the DSP Bid Response extension point, start with Response structure and Business rules above, then implement the handler pattern and work through the testing checklist before requesting deployment.