Answers

Why is my fill rate low?

Short answer

Low fill means bidders are getting your requests and passing, so the question is which filter they hit. Check in this order: floors set above the market clearing price; missing or malformed required fields, which get a request dropped before it is ever priced; stale media-type signalling, above all video.placement without video.plcmt; privacy signals that are absent or self-contradictory, which make buyers drop EEA traffic; a tmax too tight for the path; and blocked categories, advertisers, or seats. Validate one real request before reading any dashboard.

Split the funnel before changing anything

"Low fill" describes an outcome, not a cause. Four different failures produce the same number, and each has a different fix:

  1. The request never reaches bidders, or reaches them malformed and gets discarded before decisioning.
  2. Bidders receive it and pass. They responded in time and chose not to bid.
  3. Bidders bid and lose, to a floor, to a deal, or to another buyer.
  4. A bid wins and the impression never renders, so it is never counted.

Bid rate separates stage one from stage two. No-bid reason codes separate two from three. Discrepancy between your win count and the buyer's impression count points at four. Everything below assumes you have split the funnel first.

The order to check things

CheckSymptomWhere to look
Payload validityOne partner reports errors while your logs look cleanValidate the exact bytes you send, after serialization and compression, in the tester
Required fieldsSystematic no-bids across every partnerid, imp[], an imp.id per impression, a media type object on every impression
Media-type signallingVideo and CTV fill far below displayvideo.plcmt, mimes, protocols, minduration and maxduration, pod fields
Privacy signalsFill collapses on EEA or specific US state trafficregs.gdpr with user.consent, regs.gpp with regs.gpp_sid, regs.coppa
FloorsHealthy bid rate, low win rate, bids clustered under the floorimp.bidfloor, bidfloorcur, deal floors, durfloors on video and audio
TimeoutsResponses arrive but arrive latetmax against real round-trip latency per bidder
BlocklistsSpecific advertisers or categories never appearbcat, badv, bapp, wseat, bseat
Supply chainOne path fills, an identical path does notsource.schain versus ads.txt and sellers.json

The failures that hide as demand problems

Some breakage produces no error anywhere. OpenRTB parsers are required to ignore fields they do not recognise, so a field in the wrong place is not rejected: it is silently dropped, and the request continues without the signal. Three examples that cost real fill:

  • Privacy in the old path. regs.ext.gdpr and user.ext.consent were promoted to first-class fields in 2.6. A buyer reading only the new paths sees consent as absent and stops bidding, while your logs show a perfectly well-formed request.
  • Deprecated video placement. video.placement without video.plcmt. Covered in placement versus plcmt.
  • Type drift. "at": "1" as a string instead of an integer, a boolean where the spec says integer, a single object where an array is required. Strict parsers reject the request; lenient ones coerce it unpredictably.

These are exactly what a validator finds in seconds and what dashboards never show. The catalogue is in common OpenRTB mistakes.

Ask the bidders why

A no-bid can carry a reason. BidResponse.nbr holds a code such as 2 (invalid request), 6 (unsupported device), 8 (unmatched user), 12 (ads.txt authorization violation), or 16 (incomplete supply chain), and the full table is in no-bid reason codes. Not every bidder populates it, but the ones that do turn a guessing exercise into a lookup. A concentration of code 2 across partners is a payload problem, not a pricing one.

A ten-minute diagnostic

  1. Capture one real bid request for an affected placement, exactly as sent.
  2. Run it through the tester against the OpenRTB version your partners actually use, and fix every error before reading anything else.
  3. Compare the request against a known-good example of the same media type, field by field, looking for what is absent rather than what is wrong.
  4. Segment fill by bidder, by media type, by geo, and by device to localise the drop.
  5. Only then move floors, and move them one variable at a time.
  6. Put the fixed request in CI as a fixture so the regression cannot come back: validation in CI.

Sources