Request & response templates

Exactly what travels on the wire, in both directions and in every format we support — OpenRTB, XML and JSON. Copy a block, swap the credentials, and you have a working integration.

Overview

Two directions, two protocols each. As a supply partner you call us and we answer with a price and a click URL. As a demand partner we call you and read a price and a destination out of your response.

DirectionProtocolRouteBodies
Supply → usFeedGET /feed/bidderQuery in · XML or JSON out
Supply → usOpenRTB 2.5POST /rtb/bidderJSON in · JSON out
Us → demandFeedGET your URLQuery out · XML or JSON back
Us → demandOpenRTB 2.5POST your URLJSON out · JSON back

Host: https://bid.searchplan.co. Every zone is identified by zid and authorised by ckey, both issued by us.

Feed supply → us

Endpoint URL

Use the endpoint URL we provide. The example below carries a placeholder zone and key — it will not serve traffic.

https://bid.searchplan.co/feed/bidder?zid=YOUR_ZID&ckey=YOUR_KEY&type=popup&ip=82.131.34.62&referrer=http%3A%2F%2Fexample.com&ua=Mozilla%2F5.0+%28Macintosh%3B+Intel+Mac+OS+X+10_14_2%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F71.0.3578.98+Safari%2F537.36

Endpoint GET-parameters

  • zidRequired— Zone ID. Issued by us.
  • ckeyRequired— Zone key. Issued by us; a wrong key returns 403.
  • typeRequired— Feed type: popup, banner, ctv. pop and video are accepted as aliases.
  • ipRequired— IP address of the visitor.
  • uaRequired— User-Agent header from the visitor's HTTP request. Must be in URL-encoded format.
  • pageOptional— URL of the page where the ad will be displayed. Must be in URL-encoded format.
  • referrerOptional— URL of the page the visitor came from. Must be in URL-encoded format.
  • refidOptional— Reference ID (subid). Passed through to reporting. subid and uid are accepted as aliases.
  • countryOptional but highly recommended— Visitor country, ISO-2. Without it we resolve the country from the connection, which on server-to-server calls is your server's location, not the visitor's.
  • qOptional— Search term, when the traffic carries one. Some zones are configured to require it.
  • langOptional— Language of the visitor, two-letter code.
  • w, hOptional— Slot size in pixels. Send it for banner: it is forwarded to demand and echoed on the bid.
  • pmodelOptional— Pricing model. 1 — CPM, 2 — CPC. Defaults to the zone setting.
  • curOptional— Overrides bid currency. ISO-4217 alpha code. Default USD.
  • formatOptional— Response format:
    • xml — default
    • json

Popup XML-response example

<?xml version="1.0" encoding="UTF-8"?>
<result>
    <clickUrl>https://bid.searchplan.co/c/eyJkIjoiNTgwOTkxMCIsInMi</clickUrl>
    <idad>486213905</idad>
    <bid>0.138540</bid>
    <cur>USD</cur>
</result>

Popup JSON-response example

{
    "clickUrl": "https://bid.searchplan.co/c/eyJkIjoiNTgwOTkxMCIsInMi",
    "idad": 486213905,
    "bid": 0.138540,
    "cur": "USD"
}

Response fields

  • clickUrl— Where to send the visitor. Redirects to the landing page and settles the win on its own.
  • idad— Identifier of the served ad, stable for that response.
  • bid— Price in the unit your zone is paid in: CPC by default, CPM if agreed or if pmodel=1.
  • cur— Currency of bid.

Response HTTP-codes

  • 200— Successful response
  • 204— Empty response. No ads were found
  • 400— Required parameters are missing or incorrect
  • 403— Wrong zone key
  • 404— Zone is unknown or inactive

No bid is a 204 with an empty body — not a zero-bid payload and not an error.

OpenRTB supply → us

OpenRTB 2.5 over POST, credentials in the query string: POST /rtb/bidder?zid=YOUR_ZID&ckey=YOUR_KEY with Content-Type: application/json. We honour tmax as the deadline and never bid below imp.bidfloor.

BidRequest you send
{
  "id": "a3d9-supply-req-001",
  "at": 1,
  "cur": ["USD"],
  "tmax": 400,
  "imp": [{
    "id": "1",
    "banner": { "w": 300, "h": 250 },   // or "instl": 1 for pop
    "bidfloor": 0.01,
    "bidfloorcur": "USD",
    "tagid": "your-placement"
  }],
  "site": {
    "domain": "example-publisher.com",
    "page":   "https://example-publisher.com/article"
  },
  "device": {
    "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) …",
    "ip": "88.20.14.7",
    "geo": { "country": "USA" }
  },
  "user": { "id": "end-user-id" }
}
BidResponse 200 · x-openrtb-version: 2.5
{
  "id": "a3d9-supply-req-001",
  "cur": "USD",
  "seatbid": [{
    "seat": "bidtags",
    "bid": [{
      "id": "b0f1c2d3-4e5a-6b7c-8d9e-0f1a2b3c4d5e",
      "impid": "1",
      "price": 0.13854,
      "adm": "https://bid.searchplan.co/c/eyJkIjoiNTgwOTkxMCIsInMi",
      "crid": "5809910",
      "nurl": "https://bid.searchplan.co/n/eyJkIjoiNTgwOTkxMCIsInMi",
      "adomain": ["advertiser.com"],
      "w": 300,
      "h": 250
    }]
  }]
}

price is always CPM here. w and h are echoed from your imp.banner. Firing nurl is optional — the click URL settles the win by itself.

adm payload

What travels in adm is set per zone. Tell us which shape your tag expects:

url default
https://bid.searchplan.co/c/TOKEN
popunder_xml
<?xml version="1.0"?>
<ad><popunderAd><url><![CDATA[https://bid.searchplan.co/c/TOKEN]]></url></popunderAd></ad>
html
<a href="https://bid.searchplan.co/c/TOKEN" target="_blank"></a>

We return a destination, not creative markup. The bid tells you where to send the user, so this fits formats that open a landing page rather than render a creative we supply.

Feed us → demand

Give us a URL template with macros and we call it with GET. We read two values out of your response — the price and the destination — so your existing shape works unchanged.

Template what you give us
https://your-endpoint.com/search
  ?q={q}&ip={ip}&ua={ua}&country={country}
  &subid={supply}&url={url}&device={device}
Call GET · macros resolved
https://your-endpoint.com/search
  ?q=car%20insurance&ip=88.20.14.7&ua=Mozilla%2F5.0%20(Windows%20NT%2010.0…)
  &country=US&subid=164759
  &url=https%3A%2F%2Fexample-publisher.com%2Farticle&device=desktop

Response — XML

Either attributes or text nodes; CDATA is unwrapped for you.

Attributes you return
<?xml version="1.0"?>
<result>
  <listing bid="0.4200" url="https://land.example.com/o?c=1"/>
</result>
Paths we read
bid_path = result.0.listing.0.@bid    // → 0.4200
url_path = result.0.listing.0.@url    // → https://land.example.com/o?c=1
Text nodes you return
<response>
  <ad>
    <price>0.42</price>
    <link><![CDATA[https://land.example.com/o?c=1&x=2]]></link>
  </ad>
</response>
Paths we read
bid_path = response.0.ad.0.price.0    // → 0.42
url_path = response.0.ad.0.link.0     // → https://land.example.com/o?c=1&x=2

How XML paths are built. Every element becomes a list, because a tag can repeat — so each step carries an index: result.0.listing.0. Attributes are prefixed with @, and a bare text node is read straight from the element. You don't have to work this out yourself: send us one sample response and we set the paths.

Response — JSON

You return
{
  "listings": [
    { "bid": 0.42, "url": "https://land.example.com/o?c=1" }
  ]
}
Paths we read
bid_path = listings.0.bid
url_path = listings.0.url

Response fields we read

  • bidRequired— Your price. Tell us whether it is cpc or cpm; zero or less counts as no bid.
  • urlRequired— Destination we send the visitor to. Must be an absolute http or https URL.

Both are located by path, so the field names above are only a convention — yours can be called anything, at any depth.

Response HTTP-codes we accept

  • 200— Parsed for a bid. An empty body, a zero bid or a missing destination is treated as no bid.
  • 204— No bid. The expected way to say "nothing this time".
  • Anything else— Logged as an error for that request and skipped. It never blocks the auction.

OpenRTB us → demand

We POST a BidRequest built to your spec. The two templates below are the ones we send today — banner and pop — generated by the bidder itself, so this is literally what arrives at your endpoint.

BidRequest banner
{
  "id": "6f2a1c94-3d17-4b8e-9c02-1a5f7e0d4b33",
  "imp": [{
    "id": "1",
    "banner": { "w": 300, "h": 250 },
    "tagid": "pub-4471"
  }],
  "at": 1,
  "cur": ["USD"],
  "tmax": 370,
  "device": {
    "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 …",
    "ip": "88.20.14.7",
    "devicetype": 2,
    "geo": { "country": "USA", "type": 2 },
    "language": "en",
    "os": "Windows",
    "dnt": 0,
    "lmt": 0
  },
  "user": { "id": "ccf3cd95b1e10060d75d1a233fdde8f63ca5be81" },
  "site": {
    "id": "example-publisher.com",
    "domain": "example-publisher.com",
    "page": "https://example-publisher.com/article",
    "ref": "https://google.com/",
    "keywords": "car insurance",
    "publisher": { "id": "pub-4471" },
    "cat": ["IAB24"]
  },
  "source": {},
  "ext": { "sub": "164759" }
}
BidRequest pop — same shape, interstitial imp
  "imp": [{
    "id": "1",
    "instl": 1,          // no banner object for pop
    "tagid": "pub-4471"
  }],
BidResponse what we expect back
{
  "id": "6f2a1c94-3d17-4b8e-9c02-1a5f7e0d4b33",
  "cur": "USD",
  "seatbid": [{
    "bid": [{
      "id": "1",
      "impid": "1",
      "price": 0.55,
      "adm": "https://land.example.com/o?c=1",
      "nurl": "https://your-endpoint.com/win?p=${AUCTION_PRICE}",
      "adomain": ["advertiser.com"]
    }]
  }]
}

We read the first bid of the first seat. The destination comes from adm by default — a bare URL, or extracted from CDATA or an href if your adm is markup. If it lives in nurl instead, say so and we read it there.

SettingValue
Formatpop, banner or ctv — we only send what you are set up for.
Timeout502000 ms, default 250. tmax is 30 ms lower, to leave room for the network.
GeoISO-2 allowlist. Empty means every country.
DevicesAny of desktop, mobile, tablet.
Pricecpc or cpm.
QPSAgreed per partner, enforced on our side.

Answer within the timeout. One deadline covers every partner in the auction — a late bid is discarded even if it would have won. ${AUCTION_PRICE}, ${AUCTION_CURRENCY} and ${AUCTION_IMP_ID} are filled in when we fire your nurl; any other ${AUCTION_*} macro is emptied rather than sent unresolved.

Macros

For demand feed templates. A macro we don't recognise, or one with no value for that request, resolves to an empty string — a template never goes out with {something} still in it. {ua}, {url} and {ref} are URL-encoded for you; the rest are substituted raw.

MacroValueAlso accepted as
{q}Search term{query} {keyword} {kw} {search} {term}
{ip}End user IP{user_ip} {clientip} {client_ip} {remote_ip} {visitor_ip}
{ua}User agent{useragent} {user_agent}
{url}Page URL{page} {page_url} {pageurl}
{ref}Referrer{referer} {referrer}
{subid}Sub-id from the supply{sub} {sub1} {subid1} {pubid} {refid}
{country}ISO-2 country{geo} {cc} {country_code}
{lang}Two-letter language{language} {accept_lang} {accept_language}
{device}desktop · mobile · tablet{device_type} {devicetype}
{os}OS from the user agent
{supply}Our id for the traffic source
{w} {h}Slot size, when sent

{count}, {num}, {limit} and {results} always resolve to 1 — we take a single listing per request.

Status codes

CodeMeaningWhat to do
204No bid.Nothing — no demand won this auction.
400Missing parameter or invalid JSON body.The message names it, e.g. ip parameter is required.
403Wrong ckey.Check the key issued for that zone.
404Unknown or paused zone.Confirm the zid and that the zone is live.
405OpenRTB called without POST.Use POST.
410Click URL older than one hour.Expected for stale clicks.

Feed errors carry a body in the zone's format — <result><error>…</error></result> or {"error": "…"}. OpenRTB errors return the status with an empty body.