Errors & No-Bids
Two things are easy to conflate: a request that failed, and an auction where
nobody bid. A 204 is not an error. At any real volume it is the majority of
what you will see.
HTTP status codes
| Status | Meaning | What to do |
|---|---|---|
200 | A bid. Body carries a BidResponse | Render it, fire the notices |
204 | No bid, empty body | Nothing, this is normal and expected |
400 | Malformed JSON or failed validation | Fix the request, see the codes below |
401 | Unknown or revoked endpoint key | Check the key in the path |
403 | Account suspended, or endpoint not enabled | Contact the integration office |
413 | Body exceeds the 256 KB limit | Trim the request, usually user.data or content |
415 | Wrong Content-Type | Send application/json; charset=utf-8 |
429 | Above your provisioned request rate | Back off, and ask for a higher limit if sustained |
500 | Exchange error | Treat as a no-bid, do not retry the same auction |
503 | Exchange shedding load | Treat as a no-bid, reduce rate briefly |
A healthy integration is mostly 204 with a minority of 200, and essentially
nothing else. Anything above roughly 1% in the 4xx range is a bug in the
request rather than a property of the traffic.
Error responses
A 400 carries a JSON body identifying what failed.
{
"error": {
"code": "MISSING_REQUIRED",
"message": "device.ua is required",
"id": "8f2a1c30-6b4e-4d9a-9f11-2c7e3b5a8d40"
}
}
| Code | Meaning |
|---|---|
MALFORMED_JSON | The body is not valid JSON. Check trailing commas and unescaped quotes. |
MISSING_REQUIRED | A required field is absent. message names it. |
MISSING_UA | device.ua is absent or empty. |
MISSING_IP | Neither device.ip nor device.ipv6 is present. |
MISSING_INVENTORY_ID | Neither site.domain nor app.bundle is present. |
BOTH_SITE_AND_APP | The request carries both site and app. |
NO_IMP_OBJECT | The single imp carries none of banner, video, audio or native. |
MULTI_IMP | More than one entry in imp. |
UNSUPPORTED_VERSION | x-openrtb-version is neither 2.6 nor 2.5. |
UNSUPPORTED_CURRENCY | cur contains no currency we settle in. |
INVALID_FLOOR | bidfloor is negative, or bidfloorcur is not a known currency. |
SCHAIN_INVALID | A supply chain node does not resolve against sellers.json. |
SCHAIN_INCOMPLETE | complete is 0 where a full chain is required. |
RATE_LIMITED | Above your provisioned request rate. |
ACCOUNT_INACTIVE | Account suspended, or not yet enabled for production. |
id on the error is the BidRequest.id you sent, when we were able to parse it.
Quote it in support requests, since we retain full bodies for seven days and can
replay the auction.
No-bid reason codes
On the occasional 200 we send with nbr and no seatbid, the code says why
the auction produced nothing. None of these is an error, and none of them needs
handling beyond treating the response as a no-bid.
| Code | Reason |
|---|---|
0 | Unknown |
1 | Technical error |
2 | Invalid request |
3 | Known web crawler |
4 | Suspected non-human traffic |
5 | Cloud, datacentre or proxy IP |
6 | Unsupported device |
7 | Blocked publisher or site |
8 | Unmatched user |
9 | Daily reader cap met |
10 | Daily domain cap met |
11 | Ads.txt authorisation unavailable |
12 | Ads.txt authorisation violation |
Codes 3, 4 and 5 are worth watching over time. A rising share of them on
one tagid usually means something upstream has changed, and it is easier to
find while the volume is small.
Rate limiting
429 carries a Retry-After header in seconds. Honour it. Requests sent inside
the window are counted against you and returned 429 again.
HTTP/1.1 429 Too Many Requests
Retry-After: 2
Content-Type: application/json
Rate is provisioned per endpoint key. If you are hitting the ceiling on real traffic rather than on a retry loop, ask the integration office for a raise.
Next: Bid Request Samples.