Guides
OpenRTB loss reason codes (lurl)
When a bid loses, the exchange can call the bid's lurl loss notice and substitute a Loss Reason Code into the ${AUCTION_LOSS} macro: 102 Lost to Higher Bid, 100 Below Auction Floor, the 200-range creative filters, and the rest. The full table is below, along with how the notice works and how to read the ranges when you are debugging a falling win rate.
How a loss notice works
A bidder that wants loss telemetry puts a loss notice URL in bid.lurl, with macros where the facts should go:
{
"id": "1",
"impid": "imp-1",
"price": 9.43,
"lurl": "https://dsp.example.com/loss?auction=${AUCTION_ID}&reason=${AUCTION_LOSS}&mtw=${AUCTION_MIN_TO_WIN}",
"adm": "..."
}If the bid loses and the exchange supports loss notices, it expands the macros and calls the URL: ${AUCTION_LOSS} becomes the reason code, ${AUCTION_PRICE} is only meaningful on wins, and exchanges that support ${AUCTION_MIN_TO_WIN} fill in the price that would have turned the loss into a win. The macro reference covers the full substitution table.
Three caveats before you build dashboards on this. Loss notices are optional, so absence of a notice is not evidence of a win. Exchanges differ on which codes they send and when (some batch, some sample, some only send economic losses). And the code list itself lives in the OpenRTB 3.0 document, which is where 2.6 points for both this list and the no-bid reason codes; the values below come from that list.
The loss reason code table
| Value | Name | Meaning |
|---|---|---|
0 | Bid Won | Not a loss at all; exists so one notice pipeline can report every bid outcome. |
1 | Internal Error | Something failed inside the exchange while handling the bid. |
2 | Impression Opportunity Expired | The auction or impression window closed before the bid could be used. |
3 | Invalid Bid Response | The response failed the exchange's parsing or validation. |
4 | Invalid Deal ID | The dealid on the bid did not match any deal known to the exchange. |
5 | Invalid Auction ID | The bid's id did not match the request id of a live auction. |
6 | Invalid Advertiser Domain | The adomain was missing, malformed, or not allowed. |
7 | Missing Markup | No adm and no way to resolve markup for a bid that needed it. |
8 | Missing Creative ID | The crid was absent and the exchange requires it. |
9 | Missing Bid Price | No price on the bid object. |
10 | Missing Minimum Creative Approval Data | The exchange needed creative approval attributes that were not supplied. |
100 | Bid was Below Auction Floor | The bid did not clear bidfloor for the impression. |
101 | Bid was Below Deal Floor | The bid referenced a deal but came in under that deal's floor. |
102 | Lost to Higher Bid | Straight economic loss; someone paid more. |
103 | Lost to a Bid for a Deal | A deal-backed bid took priority over this open-auction bid. |
104 | Buyer Seat Blocked | The publisher or exchange blocks this seat on this inventory. |
200 | Creative Filtered - General; Reason Unknown | The creative was filtered and the exchange is not saying why. |
201 | Creative Filtered - Pending Processing by Exchange | The creative is still in scanning or approval and cannot serve yet. |
202 | Creative Filtered - Disapproved by Exchange | The creative was reviewed and rejected. |
203 | Creative Filtered - Size Not Allowed | Creative dimensions did not fit the impression. |
204 | Creative Filtered - Incorrect Creative Format | Wrong media type or format for the slot (for example, video markup in a banner slot). |
205 | Creative Filtered - Advertiser Exclusions | The advertiser is on a publisher or deal block list. |
206 | Creative Filtered - App Store ID Exclusions | The promoted app's store ID is excluded. |
207 | Creative Filtered - Not Secure | The creative loads http resources into an https context. |
208 | Creative Filtered - Language Exclusions | Creative language is excluded for this inventory. |
209 | Creative Filtered - Category Exclusions | The creative's category hits bcat or a deal's category blocks. |
210 | Creative Filtered - Creative Attribute Exclusions | A creative attribute (battr) is blocked, such as auto-play or expandable. |
211 | Creative Filtered - Ad Type Exclusions | The ad type is not accepted on this placement. |
212 | Creative Filtered - Animation Too Long | Animation exceeds the publisher's duration limit. |
213 | Creative Filtered - Not Allowed in Deal | The creative violates the terms of the deal it bid into. |
214 | Creative Filtered - Invalid SKAdNetwork | The SKAdNetwork signature or fields failed validation. |
215 | Creative Filtered - App Bundle Exclusions | The promoted app bundle is excluded. |
500+ | Exchange-specific | Private values; the spec says to communicate them with buyers beforehand. |
Reading the ranges
The list is organized in blocks, and the block tells you who owns the fix:
- 1-10: your response is broken. These are hygiene failures the exchange found in the bid itself: unparseable response, missing price, missing markup, a
dealidthat does not exist. Every one of these is fixable unilaterally, and most are visible in the response before it leaves your servers. The bid response anatomy page lists what a well-formed bid carries. - 100-104: auction economics. Floors, higher bids, deal priority, seat blocks. A spike in 100 or 101 means your pricing model and the floor signals disagree; a spike in 103 means open-auction bids are losing to deals and the inventory you want has moved behind PMP.
- 200-215: creative filtering. The bid was fine; the creative was not. Size, format, security, category, attributes, app bundle. These are the codes worth alerting on, because they are systematic: a creative that trips 207 (Not Secure) or 203 (Size Not Allowed) will lose every auction it enters until someone fixes the creative or the targeting.
- 500 and up: exchange-specific. Meaningless without the exchange's documentation. If a partner sends them, get the mapping in writing.
Debugging with loss codes
- Separate the ranges first. A falling win rate from 102 is a pricing problem; the same fall from 200-range codes is an ops problem. Aggregating them into one loss metric hides both.
- Treat 3, 7, 8, 9 as build failures. They mean the exchange received something malformed. Validate the exact response you send: the tester accepts bid responses as well as requests, and flags missing required fields and type mismatches against the spec.
- Cross-check 4 (Invalid Deal ID) against your deal sync. It usually means a deal was paused or renamed on the exchange side while your bidder kept bidding into it.
- Use min-to-win where you can get it. In first-price auctions,
${AUCTION_MIN_TO_WIN}on losses is the cleanest shading signal the protocol offers; it turns 102 from a verdict into a calibration point. See the first-price vs second-price guide for how shading fits in.
FAQ
Is the loss notice (lurl) guaranteed to fire?
No. lurl is optional on both sides: a bidder may omit it, and an exchange is not obligated to call it or to support every loss reason. Support and semantics are an integration-level agreement. If loss telemetry matters to your bidding, confirm with each exchange which codes it actually sends.
What is the difference between loss reason codes and no-bid reason codes (nbr)?
Direction. nbr flows from bidder to exchange and explains why the bidder declined to bid at all. Loss reason codes flow from exchange to bidder and explain why a bid that was made did not win. Both lists live in the OpenRTB 3.0 document, but they are different lists with overlapping numbers, so label them clearly in logs.
Is loss reason 102 related to VAST error 102?
No, coincidence. OpenRTB loss reason 102 means Lost to Higher Bid. VAST error 102 means the player does not support the VAST version of the ad response. They live in different specs at different layers of the stack.
What is AUCTION_MIN_TO_WIN?
A substitution macro that expands to the minimum price the bid would have needed to win (or, on a win, to keep winning). Exchanges that support it, notably Google's exchange, expose it in loss and billing notices so bidders can calibrate bid shading in first-price auctions without guessing.
Validate the bid before the exchange grades it
Codes 1 through 10 are the exchange telling you your response failed validation. rtblint runs the same class of checks locally: paste a bid response into the tester or run rtblint validate response.json via the CLI to catch missing prices, absent markup, and malformed fields before an exchange turns them into loss codes.