# OpenRTB

Source: https://docs.ezoic.com/docs/prebid-adapter/openrtb/


If you run your own exchange, SSP, or mediation stack, you can call Ezoic as a server-to-server OpenRTB connection. You send a standard OpenRTB 2.5/2.6 bid request and get a standard bid response back, with at most one bid per impression. No Ezoic JavaScript is needed on your pages.

Complete the [onboarding steps](/docs/prebid-adapter/#before-you-start) first. There is no API key: eligibility is checked per registered domain or app bundle, and unregistered inventory gets no bids.

## Endpoint

~~~text
POST https://g.ezoic.net/ezoic/prebid/adapter/ortb
Content-Type: application/json
~~~

- Unknown OpenRTB fields are accepted and passed to demand where relevant.
- Responses are never cacheable (`Cache-Control: no-store`).
- Methods other than POST return 405.

## Request

The minimum is an `id`, at least one `imp`, and site or app identity.

| Field | Required | Notes |
|---|---|---|
| `id` | yes | Echoed back in the response. A missing `id` or empty `imp` returns 400. |
| `imp[].id` | yes | Echoed back as `bid.impid`. |
| `site.page` | yes (web) | Full page URL. If missing, `site.domain` is used as `https://<domain>/`. |
| `app.bundle` | yes (app) | The bundle must be registered with Ezoic. |
| `tmax` | recommended | Milliseconds. Requests with less than about 250 ms left after network time get a no-bid. Send **800–1000**. 1000 is assumed if omitted. |
| `device` | recommended | `ua`, `ip`/`ipv6`, `os`, `devicetype` and `geo.country` drive form factor and geo targeting. Desktop, phone, tablet and CTV are supported. |
| `imp[].banner` | one of banner / video / native | Sizes from `format[]` (preferred) or `w`/`h`. Imps with no banner sizes, video, or native are ignored. |
| `imp[].video` | one of | Standard OpenRTB video object. Adding a sibling `banner` on the same imp keeps display demand as a fallback, which we recommend. |
| `imp[].native` | one of | Standard OpenRTB native request. |
| `imp[].tagid` | optional | Used as the ad unit name in reporting. Defaults to `imp.id`. |
| `imp[].ext.bidder.placementId` | optional | Your own name or ID for the slot (string). Recorded for reporting; doesn't change bidding. |
| `imp[].bidfloor`, `bidfloorcur` | optional | Enforced across Ezoic's demand. Currency defaults to USD. |
| `imp[].instl` | optional | `1` marks interstitial inventory. |
| `imp[].rwdd` | optional | `1` marks rewarded inventory (`imp.ext.prebid.is_rewarded_inventory` also works). |

### Privacy

Standard OpenRTB fields are honored and passed to all demand:

- `user.consent` (or `user.ext.consent`): TCF v2 consent string
- `regs.gdpr` (or `regs.ext.gdpr`): GDPR applies
- `regs.gpp` and `regs.gpp_sid`: GPP
- `regs.ext.us_privacy`: US Privacy

### First-party data and supply chain

`site` / `app` content objects, `user.eids`, `source.schain`, and the `badv` / `bcat` / `bapp` block lists are passed to demand. Block lists are honored.

### User matching

Ezoic matches users through an iframe sync (`https://g.ezoic.net/ezoic/prebid/adapter/usersync-frame`), which we strongly recommend for web inventory. On Prebid Server, see [User sync](/docs/prebid-adapter/prebid-server/#user-sync). For custom stacks, set it up with your Ezoic contact. Requests without a matched user are still bid on, at lower expected prices.

### Example request

~~~json
{
  "id": "auction-8f2c1a",
  "tmax": 1000,
  "site": {
    "page": "https://www.example.com/articles/how-to-grow-tomatoes",
    "domain": "example.com"
  },
  "device": {
    "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1",
    "ip": "203.0.113.42",
    "os": "iOS",
    "devicetype": 1,
    "geo": { "country": "USA" }
  },
  "regs": {
    "gdpr": 0,
    "ext": { "us_privacy": "1---" }
  },
  "source": {
    "schain": {
      "ver": "1.0",
      "complete": 1,
      "nodes": [
        { "asi": "examplepub-exchange.com", "sid": "pub-4711", "hp": 1 }
      ]
    }
  },
  "badv": ["competitor.example"],
  "imp": [
    {
      "id": "1",
      "tagid": "sidebar-top",
      "banner": { "format": [{ "w": 300, "h": 250 }, { "w": 336, "h": 280 }] },
      "bidfloor": 0.25,
      "bidfloorcur": "USD"
    },
    {
      "id": "2",
      "tagid": "page-interstitial",
      "instl": 1,
      "banner": { "format": [{ "w": 320, "h": 480 }, { "w": 300, "h": 600 }] },
      "bidfloor": 0.5,
      "bidfloorcur": "USD"
    }
  ]
}
~~~

`device.geo.country` uses ISO-3166 alpha-3 codes (`USA`), as OpenRTB specifies.

To test, save the request as `request.json` and run:

~~~bash
curl -s -w "\nHTTP %{http_code}\n" \
  -X POST "https://g.ezoic.net/ezoic/prebid/adapter/ortb" \
  -H "Content-Type: application/json" \
  --data-binary @request.json
~~~

Before your domain is approved this returns HTTP 204, which confirms the request parsed and reached Ezoic. `400 {"error":"invalid_json"}` means the body is malformed.

## Response

**200** with a bid:

~~~json
{
  "id": "<your auction id>",
  "cur": "USD",
  "seatbid": [{
    "seat": "ezoic",
    "bid": [{
      "id": "...",
      "impid": "<imp.id>",
      "price": 1.27,
      "mtype": 1,
      "adm": "<self-contained HTML>",
      "crid": "...",
      "w": 300,
      "h": 250
    }]
  }]
}
~~~

- `mtype`: 1 = banner, 2 = video, 4 = native.
- **Banner and native:** `adm` is self-contained markup that includes Ezoic's impression tracking. Render it in a standard iframe. No win notice is needed from your side; Ezoic counts the impression when the markup renders.
- **Video:** `nurl` carries an Ezoic VAST URL. Fetch it as the VAST document for your player when the bid wins. Impression tracking is inside the VAST.
- At most one bid per impression.

**204:** no bid. Causes include no demand, unapproved inventory, invalid traffic, an unsupported form factor, a privacy-restricted request, or too little `tmax`.

**400:** unparseable JSON, or a missing `id` or `imp`.

## Counting and payment

- `price` is a net CPM: you're paid `price / 1000` per rendered, valid impression.
- Ezoic counts impressions with its own tracking: at render for banner and native, and from VAST impression events for video. As with any server-side partner, expect small differences from your ad server's counts; Ezoic's count is the payment basis.
- Reporting and payment schedule are set up with your Ezoic contact during onboarding.

## Go-live checklist

- [ ] Domains / app bundles registered and approved by Ezoic
- [ ] A test request returns 204, not 400
- [ ] `tmax` of 800 ms or more end to end
- [ ] Consent strings populated where they apply
- [ ] Video imps carry a sibling `banner` where display fallback is acceptable
- [ ] Start with low traffic and agree the ramp window with your Ezoic contact

