Guides
OpenRTB no-bid reason codes (nbr)
A bidder that passes on an impression can answer with an empty HTTP 204, or return a BidResponse whose nbr field carries a no-bid reason code: 0 Unknown Error, 1 Technical Error, 2 Invalid Request, and so on up to 17, with 500 and above reserved for exchange-specific values. The full table is below.
Three ways to say no-bid
The OpenRTB 2.6 spec gives a bidder these options when it does not want the impression:
- HTTP 204 with an empty body. The spec says calls returning no content in response to valid requests should return HTTP 204. This is the most bandwidth-friendly form and what most production bidders send.
- An empty or bid-less response. A response with no actual bids (an empty
seatbid, or a body the exchange cannot parse) is also interpreted as a no-bid. Malformed bodies count as no-bid too, which is why broken responses fail silently. - A
BidResponsewithnbr. If the bidder wants to tell the exchange why it passed, it returns just aBidResponseobject with the requestidand a reason code innbr. The spec encourages this because it makes no-bids debuggable.
A minimal reasoned no-bid looks like this:
{
"id": "9d2f2b1e-7a41-4b8c",
"nbr": 2
}One versioning quirk: OpenRTB 2.6 defines the nbr field but points to the No-Bid Reason Codes list maintained in the OpenRTB 3.0 document, the same place its other enums point to AdCOM 1.0. The values below come from that list. See the bid response anatomy page for where nbr sits relative to seatbid.
The no-bid reason code table
| Value | Name | Meaning |
|---|---|---|
0 | Unknown Error | Something failed and the bidder cannot say what. |
1 | Technical Error | An internal failure on the bidder side (timeout, crash, dependency down). |
2 | Invalid Request | The bid request itself was malformed or failed the bidder's validation. |
3 | Known Web Crawler | The user agent matched a known spider or bot list. |
4 | Suspected Non-Human Traffic | IVT filters flagged the request as likely bot traffic. |
5 | Cloud, Data Center, or Proxy IP | The IP resolved to hosting infrastructure, not an end user. |
6 | Unsupported Device | The bidder does not buy on this device or OS. |
7 | Blocked Publisher or Site | The site, app, or publisher is on the bidder's block list. |
8 | Unmatched User | No usable user ID or cookie match, so the bidder cannot target. |
9 | Daily User Cap Met | Frequency cap for this user already reached today. |
10 | Daily Domain Cap Met | Spend or impression cap for this domain already reached today. |
11 | Ads.txt Authorization Unavailable | The bidder could not retrieve an ads.txt file to verify the seller. |
12 | Ads.txt Authorization Violation | The seller is not authorized in the publisher's ads.txt. |
13 | Ads.cert Authentication Unavailable | No ads.cert signature was available to authenticate the request. |
14 | Ads.cert Authentication Violation | The ads.cert signature failed verification. |
15 | Insufficient Auction Time | Not enough time left inside tmax to run the bidder's own auction. |
16 | Incomplete SupplyChain | The SupplyChain object is missing nodes (complete != 1). |
17 | Blocked SupplyChain Node | A node in the SupplyChain object is on the bidder's block list. |
500+ | Exchange-specific | Private values; the spec says to communicate them with buyers beforehand. |
Codes 9 and 10 (the daily caps) were added in the OpenRTB 2.5 era; 11 through 17 cover ads.txt, ads.cert, auction timing, and SupplyChain checks. Anything at 500 or above is private to the exchange and only meaningful if it was agreed with the buyer beforehand.
Debugging systematic no-bids
A bidder that never bids is usually not a demand problem. Work through the codes in order of likelihood:
- nbr 2 (Invalid Request) first. Run the exact bid request you send through a validator. Malformed JSON, a missing
imp, adevice.devicetypeoutside the documented list, or a field left at a pre-2.6 path can all trip a bidder's request parser. The validate a bid request guide walks through this. - nbr 15 (Insufficient Auction Time). Check your
tmaxand measured network latency to that bidder. A 120 ms budget with 80 ms of transit leaves little room. - nbr 11, 12, 16, 17. Supply path checks: confirm your ads.txt entries and that
source.schain(moved fromsource.ext.schainin 2.6) is complete. - nbr 8 (Unmatched User). Expected on cookieless traffic; a high rate on web traffic suggests a broken cookie sync.
If the bidder only sends 204s and no nbr, you are debugging blind. Ask the integration team to enable reason codes in test, then validate on your side anyway: most nbr 2 cases are visible in the request before it ever leaves your servers.
Loss reason codes are the other half
No-bid reasons flow bidder to exchange. The reverse channel exists too: when a bid was made but lost, the exchange can fire the bid's lurl loss notice and substitute a Loss Reason Code into the ${AUCTION_LOSS} macro. That list (0 Bid Won, 100 Bid was Below Auction Floor, 102 Lost to Higher Bid, 200-range creative filtering, and more) also lives in the OpenRTB 3.0 document. Different list, different direction; do not mix the two when logging.
FAQ
Is nbr required in a no-bid response?
No. nbr is optional. The most common no-bid signal is an empty HTTP 204 response with no BidResponse at all. A bidder only sends a BidResponse with nbr when it wants to tell the exchange why it passed, which mainly matters during integration and debugging.
What does nbr 2 (Invalid Request) mean?
The bidder rejected the bid request itself: malformed JSON, a missing required field, a wrong type, or a value outside the documented enums. If one bidder returns nbr 2 systematically, validate the requests you send it against the OpenRTB spec before digging into that bidder's docs.
Why do bidders answer with HTTP 204 instead of a body?
Bandwidth and simplicity. The OpenRTB spec says calls returning no content in response to valid requests should return HTTP 204, and an empty response is the most bandwidth-friendly form of a no-bid. At billions of requests per day, skipping the JSON body is a real saving.
Validate before you debug
Since nbr 2 is the code you can fix unilaterally, start there. Paste a bid request into the bid request tester and rtblint will flag malformed JSON (openrtb.payload.invalid_json), undefined fields (openrtb.field.undefined), and fields at moved or deprecated paths (openrtb.field.moved, openrtb.field.deprecated) against the OpenRTB 2.6 spec. The same checks run in CI via the CLI: rtblint validate request.json.