Bid protocol
Live · Version 1.0.0
Overview
When a screen is about to play an ad slot, the network asks every partner assigned to that screen whether it wants to fill the slot. You answer with the creative you want to play and the price you pay. The highest price wins. After the creative has played, the network tells you, and that call is what you are billed for.
This page describes the calls the network makes to your server. You host three endpoints, or two if you skip the optional win notice. The calls you make to us (inventory, creative upload, status, reconciliation) are on the Partner API page, and you need them before you can bid. A creative must be uploaded and approved there before it can be used in a bid.
Player Network (SSP) Your server
| slot request | |
|------------------>| |
| | 1. bidRequest (POST, all partners |
| | assigned to the screen, in |
| | parallel) |
| |-------------------------------------->|
| | 200 bid / 204 no bid |
| |<--------------------------------------|
| | choose the highest valid price |
| | 2. winNotice (GET, winner only) |
| |-------------------------------------->|
| creative + nurl | |
|<------------------| |
| plays the creative |
| reports the play | |
|------------------>| |
| | 3. confirmation (GET nurl, |
| | only after a verified play) |
| |-------------------------------------->|
Timing
The network sends the bid request to all partners of the screen at the same moment and waits until every one has answered or timed out. Then it chooses. A slow answer delays the slot for everybody, so answer as fast as you can.
| Limit | Value |
|---|---|
| Bid request, total time | 5 seconds |
| Bid request, time to connect | 2 seconds |
| Retries | None. A request that fails or times out is a no-bid for that slot. |
The win notice and the confirmation have no limit of their own and use a default of 30 seconds. They are sent while the player is waiting for its answer, so answer them in well under a second. Neither call is repeated.
What you must implement
- A bid endpoint. It accepts
POSTwith a JSON body (bidRequest) and answers200with a bid or204without a body. Its address is agreed at onboarding. - A confirmation endpoint. It accepts
GETon thenurlyou returned in the bid (confirmation). It must be on the same host as your bid endpoint. - Optionally, a win notice endpoint (
winNotice). Tell us its address as a URI template. If you do not give one, no win notice is sent.
Requests from us carry the header X-Auth-Token with a secret that we agree with you at onboarding. Check it on all three endpoints. Without it, anybody who knows an address could create billable events. Use HTTPS.
Auction rules
- Every partner assigned to the screen is asked. The auction is first price: the partner with the highest
pricewins and is billed exactly that price. - A bid is valid only if the creative named in
video_idisconfirmed, belongs to you and is assigned to this screen. Otherwise the bid is dropped as if you had not bid. The creative is matched by theexternal_idyou chose at upload, not by our numericid. - If two valid bids have the same price, the winner is chosen by our internal partner order. Do not rely on it.
min_priceis the floor for the slot. Send a price at or abovemin_price. Bids below the floor or without a price may be dropped.- There is no currency field. All prices, including
min_price, are in the currency of your contract and are amounts for one play, not CPM. - The winning price is recorded with two decimal places. Prices of 1,000,000 or more cannot be recorded.
- Only the winner receives the win notice. Only a verified play produces a confirmation. A win that is never played is not billed.
Billing
The confirmation (nurl) is the billable event. It is sent once for each play after the player has reported the playback and the report has been verified. If your confirmation endpoint is down at that moment, the call is not repeated, the play is not marked as confirmed, and it does not appear in GET /{partner}/reconciliation/impressions. A win notice that fails does not cancel the win.
Reconciliation lists the plays whose confirmation call succeeded. In a search by date a play is listed only after it has been matched with the playback log of the player, which usually takes a few hours. Search again later, or search by unique_id. Compare reconciliation with the confirmations you received. The key of a play is the request_id.
Test checklist
Before you go live, check that your bidder:
- rejects a request without the right
X-Auth-Tokenwith401or403; - answers
204with an empty body when it does not want the slot; - answers
200withContent-Type: application/jsonand a JSON object (not an array) when it bids; - echoes
request_idexactly as received; - returns a
video_idthat is theexternal_idof aconfirmedcreative that is assigned to the screen; - returns
priceas a number at or abovemin_price, with at most two decimals; - returns a
nurlon the same host as the bid endpoint, and serves it withGETand2xxwithout any extra parameters; - handles
duration_msandmin_pricebeingnull; - handles
screen.gidandscreen.operator_idas strings; - answers within a second under normal load, and never needs more than the 5-second limit;
- treats a repeated confirmation of the same
request_idas one play.
Versioning
Changes to this protocol are additive: new optional request fields and new optional response fields. Read request fields by name and ignore the ones you do not know. Unknown fields in your response are ignored by us.
Calls we make to your server
These are requests our platform sends to a URL you provide. Your server answers them as described.
Slot request#
Sent to your bid address when a screen assigned to you is about to play a slot. One request per slot and partner. Answer 200 with a bid or 204 with no body for no bid.
Anything other than 200 or 204 (a 4xx, a 5xx, a timeout, an invalid body) is treated as no bid. A 200 whose body is not a JSON object is also no bid.
Fields of the answer:
request_id: echo the value from the request. We store it as the key of the play. If you leave it out we invent one, and you will not be able to match your records with reconciliation.video_id: theexternal_idyou gave the creative at upload.creative_idis accepted as another name for the same field. Our numeric creativeidis not accepted here.price: what you pay for this play.bidis accepted as another name.nurl: the address we call after a verified play. See the confirmation call.
If video_id is missing, or names a creative that is not usable on this screen, the bid is dropped.
min_price is the floor for this slot. It can be null when the screen has no floor. duration_ms is null when the screen owner has not set the slot length. timestamp_ms is the moment the slot is scheduled to be shown.
Body we send · application/json
| Field | Type | Description | Example |
|---|---|---|---|
| request_idrequired | string | Identifier of this request. Unique for each request. Treat it as an opaque string and echo it in your answer. |
req_8f3a1c92e7 |
| timestamp_msrequired | integer | null | The moment the slot is scheduled to be shown, in Unix milliseconds, UTC. |
1791454530000 |
| duration_msrequired | integer | null | Length of the slot in milliseconds. Null when the screen owner has not set it. |
10000 |
| min_pricerequired | number | null | Floor price for one play, in the currency of your contract. Null when the screen has no floor. Send a price at or above it. |
120 |
| screenrequired | object | The screen that offers the slot. |
|
| screen.gidrequired | string | Screen id, the same number as |
173 |
| screen.operator_idrequired | string | Id of the network that owns the screen, written as a string. |
7 |
{
"request_id": "req_8f3a1c92e7",
"timestamp_ms": 1791454530000,
"duration_ms": 10000,
"min_price": 120,
"screen": {
"gid": "173",
"operator_id": "7"
}
}
Expected responses
A bid. The body must be a JSON object.
Response fields
| Field | Type | Description | Example |
|---|---|---|---|
| request_idrequired | string | The |
req_8f3a1c92e7 |
| video_idrequired | string | The |
spring_2026_001 |
| pricerequired | numbermin 0 | The price you pay for this play, in the currency of your contract, at or above |
150 |
| nurlrequired | string (uri) | The address we call after a verified play. It must be on the same host as your bid address. It is called without changes. |
https://bidder.example.com/confirm?rid=req_8f3a1c92e7 |
{
"request_id": "req_8f3a1c92e7",
"video_id": "spring_2026_001",
"price": 150,
"nurl": "https://bidder.example.com/confirm?rid=req_8f3a1c92e7"
} No bid. The body must be empty.
Win notice#
Sent right after the auction, only to the winner, and only if you gave us an acceptance URL. It uses GET and the same X-Auth-Token header as the bid request. A 2xx answer is expected, and the body is not read.
You give us the address as a URI template (RFC 6570), for example:
https://bidder.example.com/win{?uniqueId,playerId,price,videoExternalId}
Use {name} to insert a value, {+name} to insert a value without escaping, and {?a,b} to add a query string. Variables with no value are left out.
| Variable | Value |
|---|---|
uniqueId |
The request_id of your bid. |
playerId |
Screen id (gid). |
operatorId |
Network id (screen.operator_id). |
price |
The price of your bid. |
timestampMs |
Scheduled show time, Unix milliseconds. |
durationMs |
Slot length in milliseconds. |
videoExternalId |
Your external_id of the creative that won. |
videoId |
Our numeric id of the creative. |
nurl |
The nurl from your bid. |
If this call fails, the error is only logged. The win stands and the creative is still sent to the screen. Do not use the win notice for billing, use the confirmation.
Expected responses
The notice was received. Any 2xx status is accepted.
Confirmation of a verified play#
Sent when the player has reported that your creative was played and the report has been verified. This is the billable event.
The call is a GET to the nurl exactly as you returned it in the bid. We do not substitute macros, we do not add parameters and we do not change the path. Put into the nurl everything you need to recognise the play, for example the request_id.
The call is made only if the host of the nurl equals the host of your bid address. A nurl on any other host is never called, and the play is not confirmed. The call uses the same X-Auth-Token header. A 2xx status means you accepted it. Any other status, or a timeout, counts as a failure.
The call is not repeated. A play whose confirmation failed is not confirmed and does not appear in reconciliation. The confirmation can arrive minutes after the win, or not at all when the player is offline.
Expected responses
The confirmation was received. Any 2xx status is accepted.