Overview

Adverteks OpenRTB Integration Guide

Adverteks is a real-time bidding exchange supporting OpenRTB 2.5. This guide covers everything external DSPs and SSPs need to connect, submit bids, receive win notices, and track ad events.

Adverteks runs a second-price auction — winners pay second-highest bid + $0.01. Budget pacing is enforced hourly (linear). External DSP bids compete alongside internal campaigns in a unified auction.

Base URL

Production
https://adverteks.polsia.app
Live auction traffic
Custom Domain
https://adverteks.com
DNS propagation may vary

Authentication

All partner management endpoints require an API key passed in the Authorization: Bearer {api_key} header. RTB bid endpoints are public — register as a partner first to receive your partner_id and api_key.

RTB endpoints (/api/rtb/bid, /api/rtb/win, /api/rtb/event) are public — use your partner_id inside request payloads as the identifier.

Rate Limits

EndpointLimitWindow
POST /api/rtb/bid100 req/minPer IP
GET /api/ad/serve/:zoneKey1,000 req/minPer zone key
POST /api/auth/*3 req/hrPer IP
General API30 req/minPer IP

Rate limit exceeded → HTTP 429 Too Many Requests with Retry-After header.


OpenRTB 2.5

Bid Request

DSPs can submit bids directly to Adverteks via the inbound bid endpoint. Adverteks runs a unified second-price auction combining your bid with internal campaigns. Hard timeout: 200ms.

POST /api/rtb/bid Public

Submit an OpenRTB 2.5 bid request. Returns a full bid response on match, HTTP 204 on no-fill.

200 — Bid accepted 204 — No bid / no fill 400 — Invalid request 429 — Rate limited

Bid Request Object

FieldTypeRequiredDescription
idstring Required Unique auction ID. Echoed in response and used in win notice as ${AUCTION_ID}.
imparray Required Impression objects. At least one required.
imp[].idstring Required Impression identifier within this request.
imp[].bannerobject Optional Banner object with w and h. Supported: 728×90, 300×250, 320×50.
imp[].bidfloorfloat Optional Minimum CPM in USD. Default: 0.0. Zone floor pricing may override.
site.idstring Optional Zone key. Used to route bid to the correct publisher inventory.
device.geo.lat/lonfloat Optional User coordinates. Enables geofence bid multipliers (0.8×–2.0×).
device.ipstring Optional User IP. Used for geo fallback and fraud detection.
atinteger Optional Auction type. 2 = Second price (always used).
tmaxinteger Optional Timeout in ms. Adverteks enforces 200ms hard limit regardless of this value.

Bid Response Object

FieldTypeDescription
idstringEchoes bid request id.
seatbid[].bid[].idstringUnique bid ID — use as ${AUCTION_BID_ID} in win notice.
seatbid[].bid[].pricefloatClearing price (CPM USD). What you'll be charged on win.
seatbid[].bid[].admstringAd markup HTML with impression tracker and click URL.
seatbid[].bid[].nurlstringWin notice URL with macro placeholders. Fire on win.
seatbid[].seatstringSeat ID — use as ${AUCTION_SEAT_ID}.
curstringAlways "USD".
Sample Bid Request
{
  "id": "8a3f5d2b-1c47-4e89-b0a2-6f9e7d4c1b83",
  "at": 2,
  "tmax": 150,
  "imp": [
    {
      "id": "1",
      "banner": { "w": 300, "h": 250, "pos": 1 },
      "bidfloor": 0.50,
      "bidfloorcur": "USD",
      "secure": 1
    }
  ],
  "site": {
    "id": "zone_abc123",
    "page": "https://example.com/article/sports",
    "domain": "example.com",
    "cat": ["IAB17"]
  },
  "device": {
    "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)...",
    "ip": "203.0.113.42",
    "geo": { "lat": 40.7128, "lon": -74.0060, "country": "USA" },
    "devicetype": 4
  },
  "user": { "id": "usr_9c2e7f1a" }
}
Sample Bid Response
{
  "id": "8a3f5d2b-1c47-4e89-b0a2-6f9e7d4c1b83",
  "cur": "USD",
  "seatbid": [
    {
      "seat": "seat_adverteks_001",
      "bid": [
        {
          "id": "bid_7e2a9f4d",
          "impid": "1",
          "price": 1.82,
          "adid": "creative_445",
          "adm": "<div class=\"ad-unit\">...</div>",
          "nurl": "https://adverteks.polsia.app/api/rtb/win?auction_id=${AUCTION_ID}&price=${AUCTION_PRICE}&bid_id=${AUCTION_BID_ID}",
          "w": 300,
          "h": 250
        }
      ]
    }
  ]
}

No-Bid Handling

When no campaign matches the zone or no bid exceeds the floor, Adverteks returns:

HTTP 204 No Content (empty body)

DSPs should treat 204 as a no-fill and serve a passback or house ad.


Win Notices & Events

Win Notice

Fire the win notice URL from the bid response after winning an auction. This confirms the impression, triggers budget deduction, and credits the publisher's 70% revenue share.

POST /api/rtb/win Public

Fire win notice via POST body or GET (pixel nURL). Must be fired within 30 seconds of auction. Late notices are rejected and the impression is not billed.

200 — Win recorded 400 — Invalid or expired auction ID
ParameterTypeRequiredDescription
auction_idstring Required Original bid request id. Substitute ${AUCTION_ID}.
pricefloat Required Clearing price CPM in USD. Substitute ${AUCTION_PRICE}.
bid_idstring Optional Bid ID from response. Substitute ${AUCTION_BID_ID}.
seat_idstring Optional Seat ID. Substitute ${AUCTION_SEAT_ID}.

Macro Substitution

MacroExample ValueDescription
${AUCTION_PRICE} 1.82 Clearing CPM in USD. Use for budget deduction.
${AUCTION_ID} 8a3f5d2b-1c47-... Auction/bid request ID. Links win to original request.
${AUCTION_BID_ID} bid_7e2a9f4d Bid ID from response seatbid[].bid[].id.
${AUCTION_SEAT_ID} seat_001 Seat ID for multi-seat DSP routing.
${AUCTION_IMP_ID} 1 Impression ID from the bid request.
${AUCTION_AD_ID} creative_445 Ad creative ID selected for this impression.
nURL Example (before → after substitution)
# Before substitution (from bid response):
https://adverteks.polsia.app/api/rtb/win?auction_id=${AUCTION_ID}&price=${AUCTION_PRICE}&bid_id=${AUCTION_BID_ID}

# After macro substitution (what actually fires):
https://adverteks.polsia.app/api/rtb/win?auction_id=8a3f5d2b-1c47-4e89-b0a2-6f9e7d4c1b83&price=1.82&bid_id=bid_7e2a9f4d
Win Notices & Events

Event Tracking

Report impression, click, and billing events. Events update publisher earnings, campaign performance dashboards, and fraud scoring.

POST /api/rtb/event Public

Report an ad event. All event types share this endpoint — differentiate via event_type.

200 — Event recorded 400 — Invalid event type or missing fields
FieldTypeRequiredDescription
event_typestring Required One of: impression, click, billing.
auction_idstring Required Original auction ID. Links event to impression and auction records.
imp_idstring Optional Impression ID. Required for click events.
pricefloat Optional Clearing price. Required for billing events. Must match win notice price.
timestampinteger Optional Unix epoch milliseconds. Defaults to server time if omitted.
Event Payloads
// Impression event
{ "event_type": "impression", "auction_id": "8a3f5d2b-...", "imp_id": "1" }

// Click event
{ "event_type": "click", "auction_id": "8a3f5d2b-...", "imp_id": "1" }

// Billing confirmation
{ "event_type": "billing", "auction_id": "8a3f5d2b-...", "price": 1.82 }

Ad Serving

Ad Serving API

Publisher ad tags call this endpoint to retrieve a winning ad. Adverteks runs the unified auction (internal + external DSP bids) and returns the winning creative in under 200ms.

GET /api/ad/serve/:zoneKey Public

Fetch a winning ad for the given publisher zone. Returns ad markup, impression tracker, and click URL on match. HTTP 204 on no-fill.

200 — Ad served 204 — No fill 400 — Invalid zone key

Path Parameters

ParameterTypeRequiredDescription
zoneKeystring Required Publisher ad zone key. Found in Dashboard → Ad Zones.

Query Parameters

ParameterTypeDescription
latfloatUser latitude. Enables geofence bid multipliers.
lonfloatUser longitude. Paired with lat.
formatstringForce format: 728x90, 300x250, 320x50.

Response Format

200 Response
{
  "success": true,
  "ad": {
    "html": "<div class=\"adverteks-ad\">...</div>",
    "width": 300,
    "height": 250,
    "impression_url": "https://adverteks.polsia.app/api/rtb/event?event_type=impression&auction_id=...",
    "click_url": "https://adverteks.polsia.app/api/ad/click/imp_8f3a2c1d",
    "creative_id": "creative_445",
    "cpm": 1.82
  },
  "auction_id": "8a3f5d2b-1c47-4e89-b0a2-6f9e7d4c1b83"
}

Click Tracking

GET /api/ad/click/:impressionId Public

Track a click on a served ad. HTTP 302 redirect to the advertiser's destination after logging the click. impressionId is returned in ad.click_url from the serve response.

302 — Redirect to destination 400 — Invalid impression ID

Ad Tag Embed

HTML Embed (paste before </body>)
<div id="adverteks-ad-zone_abc123"></div>
<script src="https://adverteks.polsia.app/ad.js"
        data-zone="zone_abc123"
        data-container="adverteks-ad-zone_abc123"
        async>
</script>

Partner Management

DSP / SSP Registration

External partners must be registered before traffic is exchanged. Registration is managed via the SuperAdmin panel or Admin API.

Registration Steps

1
Contact Adverteks — Email partnerships@adverteks.com or sign up at /advertiser/signup.
2
Provide your endpoint URL — For outbound DSPs: the URL Adverteks sends OpenRTB requests to. For inbound SSPs: the URL you POST bid requests to.
3
Receive credentials — You'll get a partner_id and api_key. Include partner_id in all bid request payloads.
4
Run test mode validation — Bids accepted, full responses returned, zero billing. Validate your JSON structure and win notice flow.
5
Go live — After test validation, account is activated for live traffic. Monitor performance via the RTB Partners dashboard.

Test Mode

  • Bids validated — Your requests are received and validated but excluded from live auctions.
  • No billing — Zero budget deduction. Test impressions are flagged in bid logs.
  • Full responses — Bid responses returned with all fields populated for integration testing.
  • Win notice simulation — Fire win notice URLs; events are logged but not charged.
Adverteks auto-suspends partners with response rates below 5% or error rates above 20% over any 1-hour window. You'll receive an email notification. Re-enable via the Admin API or SuperAdmin panel.

Performance Monitoring

GET /api/admin/rtb-partners/:partnerId/stats Admin Auth

Partner performance stats: win rate, average CPM, fill rate, total spend, error rate.

Stats Response
{
  "partner_id": "dsp_acme_001",
  "status": "active",
  "period": "24h",
  "stats": {
    "bid_requests_sent": 45230,
    "bids_received": 38970,
    "bid_rate": 0.862,
    "wins": 1204,
    "win_rate": 0.031,
    "avg_clearing_cpm": 1.94,
    "total_spend_usd": 2337.76,
    "impressions": 1204,
    "clicks": 47,
    "ctr": 0.039,
    "errors": 12,
    "avg_response_time_ms": 84
  }
}
GET /api/admin/rtb-partners/:partnerId/bid-logs Admin Auth

Detailed bid request/response logs for debugging. Full JSON payloads, response times, auction outcomes.

Query ParamTypeDescription
limitintegerMax records. Default: 50, Max: 500.
sincestringISO 8601 timestamp filter.
outcomestringFilter: win, loss, nobid, error.

Code Examples

Sending a Bid Request

Complete examples for submitting an OpenRTB 2.5 bid request to Adverteks.

curl -X POST https://adverteks.polsia.app/api/rtb/bid \
  -H "Content-Type: application/json" \
  -d '{
    "id": "8a3f5d2b-1c47-4e89-b0a2-6f9e7d4c1b83",
    "at": 2,
    "tmax": 150,
    "imp": [{"id":"1","banner":{"w":300,"h":250},"bidfloor":0.50}],
    "site": {"id":"zone_abc123","page":"https://example.com/article"},
    "device": {"ip":"203.0.113.42","geo":{"lat":40.7128,"lon":-74.0060}}
  }'
const { randomUUID } = require('crypto');

async function sendBidRequest(zoneKey, geo = {}) {
  const bidRequest = {
    id: randomUUID(),
    at: 2,
    tmax: 150,
    imp: [{ id: '1', banner: { w: 300, h: 250 }, bidfloor: 0.50, secure: 1 }],
    site: { id: zoneKey, page: 'https://example.com/article' },
    device: {
      ip: geo.ip || '0.0.0.0',
      geo: geo.lat ? { lat: geo.lat, lon: geo.lon, country: 'USA' } : undefined
    }
  };

  const resp = await fetch('https://adverteks.polsia.app/api/rtb/bid', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(bidRequest),
    signal: AbortSignal.timeout(200)
  });

  if (resp.status === 204) { console.log('No fill'); return null; }

  const data = await resp.json();
  const bid = data.seatbid?.[0]?.bid?.[0];

  if (bid) {
    console.log(`Won at $${bid.price} CPM`);
    // Substitute macros and fire win notice
    const winUrl = bid.nurl
      .replace('${AUCTION_PRICE}', bid.price)
      .replace('${AUCTION_ID}', bidRequest.id)
      .replace('${AUCTION_BID_ID}', bid.id);
    await fetch(winUrl, { method: 'POST' });
  }

  return data;
}

sendBidRequest('zone_abc123', { ip: '203.0.113.42', lat: 40.7128, lon: -74.0060 });
import uuid
import httpx  # pip install httpx

BASE = "https://adverteks.polsia.app"

def send_bid_request(zone_key: str, geo: dict = None) -> dict | None:
    payload = {
        "id": str(uuid.uuid4()),
        "at": 2, "tmax": 150,
        "imp": [{"id": "1", "banner": {"w": 300, "h": 250}, "bidfloor": 0.50}],
        "site": {"id": zone_key, "page": "https://example.com"},
    }
    if geo:
        payload["device"] = {
            "ip": geo.get("ip", "0.0.0.0"),
            "geo": {"lat": geo["lat"], "lon": geo["lon"], "country": "USA"},
        }

    with httpx.Client(timeout=0.2) as c:
        r = c.post(f"{BASE}/api/rtb/bid", json=payload)

    if r.status_code == 204:
        print("No fill")
        return None

    r.raise_for_status()
    data = r.json()
    bid = data.get("seatbid", [{}])[0].get("bid", [None])[0]

    if bid:
        price = bid["price"]
        print(f"Won at ${price:.4f} CPM")
        win_url = (bid["nurl"]
            .replace("${AUCTION_PRICE}", str(price))
            .replace("${AUCTION_ID}", payload["id"])
            .replace("${AUCTION_BID_ID}", bid["id"]))
        httpx.post(win_url)

    return data

send_bid_request("zone_abc123", geo={"ip": "203.0.113.42", "lat": 40.7128, "lon": -74.0060})

Firing a Win Notice

Fire win notices immediately after auction wins to confirm impressions.

# Via GET (pixel / nURL)
curl "https://adverteks.polsia.app/api/rtb/win?auction_id=8a3f5d2b-1c47&price=1.82&bid_id=bid_7e2a9f4d"

# Via POST body
curl -X POST https://adverteks.polsia.app/api/rtb/win \
  -H "Content-Type: application/json" \
  -d '{"auction_id":"8a3f5d2b-1c47-4e89-b0a2-6f9e7d4c1b83","price":1.82,"bid_id":"bid_7e2a9f4d"}'
async function fireWinNotice({ auctionId, price, bidId, seatId }) {
  const resp = await fetch('https://adverteks.polsia.app/api/rtb/win', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ auction_id: auctionId, price, bid_id: bidId, seat_id: seatId })
  });
  return resp.ok;
}

// Or substitute macros in the nURL from bid response:
async function fireNurl(nurlTemplate, { price, auctionId, bidId }) {
  const url = nurlTemplate
    .replace('${AUCTION_PRICE}', price)
    .replace('${AUCTION_ID}', auctionId)
    .replace('${AUCTION_BID_ID}', bidId);
  await fetch(url); // GET or POST both work
}
import httpx

def fire_win_notice(auction_id: str, price: float, bid_id: str):
    resp = httpx.post(
        "https://adverteks.polsia.app/api/rtb/win",
        json={"auction_id": auction_id, "price": price, "bid_id": bid_id},
    )
    if resp.status_code == 200:
        print(f"Win recorded — auction {auction_id} at ${price:.4f}")
    else:
        print(f"Win notice failed: {resp.status_code}")

Ad Serving & Click Tracking

Fetch ads programmatically or embed the Adverteks tag on any publisher page.

# Serve an ad
curl "https://adverteks.polsia.app/api/ad/serve/zone_abc123?lat=40.7128&lon=-74.0060"

# Force 728x90 leaderboard
curl "https://adverteks.polsia.app/api/ad/serve/zone_abc123?format=728x90"

# Track a click (follow redirects)
curl -L "https://adverteks.polsia.app/api/ad/click/imp_8f3a2c1d"
async function loadAd(zoneKey, container, geo = {}) {
  const params = new URLSearchParams(
    Object.fromEntries(Object.entries(geo).filter(([,v]) => v != null))
  );
  const resp = await fetch(`https://adverteks.polsia.app/api/ad/serve/${zoneKey}?${params}`);

  if (resp.status === 204) {
    container.innerHTML = ''; // no fill — serve passback
    return;
  }

  const { ad } = await resp.json();
  container.innerHTML = ad.html;

  // Fire impression pixel
  new Image().src = ad.impression_url;

  // Wrap clicks through Adverteks tracker
  container.querySelectorAll('a').forEach(a => { a.href = ad.click_url; });
}

loadAd('zone_abc123', document.getElementById('ad-slot'), { lat: 40.7128, lon: -74.0060 });
import httpx

def fetch_ad(zone_key: str, lat: float = None, lon: float = None) -> dict | None:
    params = {}
    if lat is not None:
        params.update({"lat": lat, "lon": lon})

    r = httpx.get(f"https://adverteks.polsia.app/api/ad/serve/{zone_key}", params=params)

    if r.status_code == 204:
        print("No fill — serving passback")
        return None

    r.raise_for_status()
    ad = r.json()["ad"]
    print(f"Served {ad['width']}x{ad['height']} at ${ad['cpm']:.4f} CPM")
    return ad

ad = fetch_ad("zone_abc123", lat=40.7128, lon=-74.0060)
Questions? Email partnerships@adverteks.com with your DSP/SSP tech stack and estimated QPS. Typical response: 24 hours.