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 BidResponse with nbr. If the bidder wants to tell the exchange why it passed, it returns just a BidResponse object with the request id and a reason code in nbr. 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

ValueNameMeaning
0Unknown ErrorSomething failed and the bidder cannot say what.
1Technical ErrorAn internal failure on the bidder side (timeout, crash, dependency down).
2Invalid RequestThe bid request itself was malformed or failed the bidder's validation.
3Known Web CrawlerThe user agent matched a known spider or bot list.
4Suspected Non-Human TrafficIVT filters flagged the request as likely bot traffic.
5Cloud, Data Center, or Proxy IPThe IP resolved to hosting infrastructure, not an end user.
6Unsupported DeviceThe bidder does not buy on this device or OS.
7Blocked Publisher or SiteThe site, app, or publisher is on the bidder's block list.
8Unmatched UserNo usable user ID or cookie match, so the bidder cannot target.
9Daily User Cap MetFrequency cap for this user already reached today.
10Daily Domain Cap MetSpend or impression cap for this domain already reached today.
11Ads.txt Authorization UnavailableThe bidder could not retrieve an ads.txt file to verify the seller.
12Ads.txt Authorization ViolationThe seller is not authorized in the publisher's ads.txt.
13Ads.cert Authentication UnavailableNo ads.cert signature was available to authenticate the request.
14Ads.cert Authentication ViolationThe ads.cert signature failed verification.
15Insufficient Auction TimeNot enough time left inside tmax to run the bidder's own auction.
16Incomplete SupplyChainThe SupplyChain object is missing nodes (complete != 1).
17Blocked SupplyChain NodeA node in the SupplyChain object is on the bidder's block list.
500+Exchange-specificPrivate 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:

  1. nbr 2 (Invalid Request) first. Run the exact bid request you send through a validator. Malformed JSON, a missing imp, a device.devicetype outside 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.
  2. nbr 15 (Insufficient Auction Time). Check your tmax and measured network latency to that bidder. A 120 ms budget with 80 ms of transit leaves little room.
  3. nbr 11, 12, 16, 17. Supply path checks: confirm your ads.txt entries and that source.schain (moved from source.ext.schain in 2.6) is complete.
  4. 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.