This document provides comprehensive guidelines for integrating with our ORTB-compatible ad exchange endpoint. It is designed for SDK developers to seamlessly construct requests, send them, and process responses to display ads.


Introduction

Our API endpoint facilitates real-time bidding (RTB) for digital advertising inventory, adhering to the OpenRTB specification. By integrating with this API, your SDK will be able to request bids for ad impressions and receive ad creatives from demand-side platforms (DSPs).


API Endpoint

All requests should be sent via HTTP POST to the following endpoint:

  https://api.nexverse.ai/openrtb2/auction
  

ORTB Compatibility

Our API is compatible with OpenRTB Specification Version 2.5. Familiarity with this specification is recommended for a deeper understanding of the request and response objects.

Reference: OpenRTB 2.5 Specification (PDF)


Request Object (BidRequest)

The request payload is a JSON object representing an OpenRTB BidRequest. This object contains all the necessary information about the ad impression opportunity.

HTTP Method: POST
Content-Type: application/json

Key Parameters

Parameter Type Description Required Sample Value
id string Unique ID of the bid request, generated by the exchange. Yes “8d1217e92b3a”
imp array Array of Impression objects. At least one impression object is required. Each object describes an ad impression opportunity. Yes [{ … }]
device object Device object containing details about the user’s device. Yes { “ua”: “…”, “ip”: “…”, “make”: “…”, “model”: “…”, “os”: “…”, “osv”: “…”, “ifa”: “…” }
app / site object Either an App object (for mobile apps) or a Site object (for web sites). One of these must be present. Contains details about the publishing context. Yes { “id”: “…”, “name”: “…”, “bundle”: “…” } (for app)
user object User object containing details about the end-user. No { “id”: “…”, “buyeruid”: “…”, “geo”: { “lat”: …, “lon”: … } }
test integer Indicator for test mode. A value of 1 indicates a test request, 0 otherwise. Test requests will not result in billable impressions. No 0 (default) or 1
at integer Auction type: 1 for First Price, 2 for Second Price Plus. Our exchange supports 2. No 2 (default)
tmax integer Maximum time in milliseconds to wait for a bid response. Typically, this is set by the exchange. No 120
cur array Array of allowed currencies for bids on this impression. Exchange will convert other currencies. Common values are “USD”. No [“USD”]
bcat array Blocked advertiser categories (IAB content categories). Ads from these categories will not be returned. Example: [“IAB25”, “IAB26”] (Gambling, Illegal Content). No [“IAB9-30”] (Political News)
badv array Blocked advertiser domains. Ads from these domains will not be returned. Example: [“evilads.com”, “malware.net”]. No [“examplebad.com”]

Sample BidRequest JSON

  {
  "id": "1d58fd5a503f9e02decefab159354f85:card-pp1:pp1_1",
  "test": 1,
  "imp": [
    {
      "id": "1",
      "banner": {
        "w": 300,
        "h": 250,
        "mimes": [
          "text/html",
          "image/png",
          "image/gif",
          "image/jpeg",
          "application/javascript",
          "text/javascript"
        ],
        "api": [3, 5, 7]
      },
      "instl": 0,
      "bidfloor": 0.01,
      "secure": 1,
      "ext": {
        "nexverse": {
          "uid": "ff8b7f99e1bc174ba6debc9d9f30867a",
          "pub_id": "24053",
          "pub_epid": "34054",
          "test": 1,
          "configId": "8ce6a9b0-59bd-4f94-865f-e0f1fe3b7f92"
        }
      }
    }
  ],
  "app": {
    "id": "com.eterno",
    "name": "Dailyhunt (Newshunt)- Cricket, News,Videos",
    "domain": "dailyhunt.in",
    "bundle": "com.eterno",
    "publisher": {
      "id": "16164",
      "domain": "dailyhunt.in"
    },
    "storeurl": "https://play.google.com/store/apps/details?id=com.eterno&hl=en"
  },
  "device": {
    "ua": "Mozilla/5.0 (Linux; Android 14; V2311 Build/UP1A.231005.007_MOD1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/136.0.7103.60 Mobile Safari/537.36",
    "ip": "152.59.5.225",
    "geo": {
      "country": "IND",
      "city": "vadodara"
    },
    "dpidmd5": "1cb611b2148bb00e37d3785fd9d84a7f",
    "language": "gu",
    "os": "android",
    "devicetype": 4,
    "ifa": "e877a025-deaf-4608-94f4-2d28028b84c9",
    "mccmnc": "-"
  },
  "user": {
    "id": "e877a025-deaf-4608-94f4-2d28028b84c9",
    "buyeruid": "dh.SExZUnNoU0xzQTQzMDNoZjJkaEtmdz09A"
  },
  "at": 1,
  "tmax": 500,
  "badv": [
    "paytm.com",
    "primevideo.com",
    "goibibo.com",
    "moneyview.in",
    "samsung.com",
    "ajio.com",
    "fancode.com",
    "lg.com",
    "ketto.org",
    "indusind.com",
    "mi.com",
    "donateforhealth.com",
    "amazon.in",
    "in.a23.rummy",
    "betway.com",
    "bigo.tv",
    "dailyhunt.in"
  ],
  "bapp": [
    "com.dreamplug.androidapp"
  ]
}
  

Impression Object (Imp)

The imp array within the BidRequest describes each available ad slot.

Key Parameters

Parameter Type Description Required Sample Value
id string A unique identifier for this impression within the bid request. Yes “1”
banner object A Banner object for display ads. Either banner or video must be present. No { “w”: 320, “h”: 50, “pos”: 7 }
video object A Video object for video ads. Either banner or video must be present. No { “mimes”: [“video/mp4”], “minduration”: 5, “maxduration”: 30 }
bidfloor float Minimum bid price in bidfloorcur currency. No 0.50
bidfloorcur string Currency of the bidfloor. Defaults to “USD” if not specified. No “USD”
secure integer Indicates if the impression supports secure HTTPS creatives (1 for secure, 0 for non-secure). No 1
pmp object PMP (Private Marketplace) object. Indicates direct deals or private auctions. No { “private_auction”: 0, “deals”: […] }
ext.nexverse object Nexverse specific object. Indicates publisher specific uid, publisher ID, test and endpoint ID. Yes “ext”: { “nexverse”: { “uid”: “ff8b7f99e1bc174ba6debc9d9f30867a”, “pub_id”: “24050”, “pub_epid”: “34004”, “test”: 1 } }

Sample Imp JSON (Banner)

  {
  "id": "1",
  "banner": {
    "w": 320,
    "h": 50,
    "pos": 7,
    "format": [
      { "w": 320, "h": 50 },
      { "w": 300, "h": 250 }
    ],
    "topframe": 1
  },
  "bidfloor": 0.75,
  "bidfloorcur": "USD",
  "secure": 1,
  "ext": {
    "nexverse": {
      "uid": "ff8b7f99e1bc174ba6debc9d9f30867a",
      "pub_id": "24053",
      "pub_epid": "34054",
      "test": 1
    }
  }
}
  

Sample Imp JSON (Video)

  {
  "id": "2",
  "video": {
    "mimes": ["video/mp4", "video/webm"],
    "minduration": 5,
    "maxduration": 30,
    "protocols": [2, 3, 5],
    "playbackmethod": [1, 3],
    "delivery": [1],
    "pos": 1
  },
  "bidfloor": 1.50,
  "bidfloorcur": "USD",
  "secure": 1,
  "ext": {
    "nexverse": {
      "uid": "ff8b7f99e1bc174ba6debc9d9f30867a",
      "pub_id": "24053",
      "pub_epid": "34054",
      "test": 1
    }
  }
}
  

Note:

  • Please contact Nexverse team to get uid, pub_id, pub_epid values.
  • Set test to 1 during development phase. For production, remove the field from the object.

Detailed Object Specifications

6.1 Device Object

The Device object provides information about the device on which the ad will be displayed. This information is crucial for targeting and delivery.

Parameter Type Description Required Sample Value
ua string User agent string associated with the device. Yes “Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.58 Mobile Safari/537.36”
ip string IP address of the device. IPv4 (dotted decimal) or IPv6 (hextet). Important: Must be public IP for proper geo-targeting and fraud detection. Yes “192.168.1.100” (example, use public IP) or “2001:0db8:85a3:0000:0000:8a2e:0370:7334”
make string Device manufacturer (e.g., “Apple”, “Samsung”, “Google”). No “Samsung”
model string Device model (e.g., “iPhone X”, “Galaxy S21”, “Pixel 6”). No “Galaxy S21”
os string Device operating system (e.g., “iOS”, “Android”, “Windows Phone”). No “Android”
osv string Version of the operating system (e.g., “15.0” for iOS, “12” for Android). No “10”
ifa string ID for Advertising (IFA). This is the resettable device identifier (e.g., Apple’s IDFA, Android’s Advertising ID). Crucial for audience targeting and frequency capping. SDKs should obtain this via platform-specific APIs. No “a1b2c3d4-e5f6-7890-1234-567890abcdef”
didsha1 string SHA1 hashed version of device ID (e.g., Android ID, IMEI). Deprecated, use ifa instead if possible. No “f3d3b7d1c9e4a8f6b2c0d5e8f3d3b7d1c9e4a8f6”
dpidsha1 string SHA1 hashed device platform ID (e.g., Android ID, ififa is not available). Deprecated, use ifa instead if possible. No “f3d3b7d1c9e4a8f6b2c0d5e8f3d3b7d1c9e4a8f6”
didsmd5 string MD5 hashed version of device ID. Deprecated, use ifa instead if possible. No “e1a4d0c9f8e7a6b5c4d3e2f1a0b9c8d7”
dpidmd5 string MD5 hashed device platform ID. Deprecated, use ifa instead if possible. No “e1a4d0c9f8e7a6b5c4d3e2f1a0b9c8d7”
macsha1 string SHA1 hashed MAC address. Deprecated. No “a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0”
macmd5 string MD5 hashed MAC address. Deprecated. No “a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6”
connectiontype integer Network connection type (refer to OpenRTB 2.6 Table 5.22, e.g., 2 for WiFi, 6 for Cellular Unknown). No 2 (WiFi)
devicetype integer Device type (refer to OpenRTB 2.6 Table 5.21, e.g., 1 for Mobile/Tablet, 2 for Personal Computer). No 1 (Mobile/Tablet)
pxratio float The ratio of physical pixels to device-independent pixels (DIPs) on the device. Typically, values like 1.0, 2.0, 3.0, etc. No 2.75
hwv string Hardware version of the device. No “v1.0”
h integer Physical height of the screen in pixels. No 1920
w integer Physical width of the screen in pixels. No 1080
carrier string Carrier or ISP (e.g., “AT&T”, “Verizon”). This is the mobile carrier name. No “Verizon Wireless”
language string Browser language (e.g., “en”, “en-US”). No “en-US”
geo object A Geo object containing geographical location information derived from device or IP. See Section 6.5 for details. No { “lat”: 34.0522, “lon”: -118.2437, “country”: “USA” }
mccmnc string Mobile Country Code and Mobile Network Code (e.g., “310-004” for AT&T in USA). No “310-004”
controllable integer Indicates if the device is a smart TV that is controllable (e.g., 1 for controllable, 0 otherwise). No 0
ext object Placeholder for device-specific extensions. No { “storage”: “128GB” }

6.2 App Object

The App object should be used when the ad opportunity is within a mobile application. It provides details about the application context.

Parameter Type Description Required Sample Value
id string Exchange-specific app ID. No “54321”
name string Application name (e.g., “My Awesome Game”). No “My Awesome App”
bundle string App bundle ID (e.g., “com.publisher.myawesomeapp” for Android, “com.publisher.myawesomeapp” for iOS). Crucial for app store lookups. Yes “com.myawesomeapp”
domain string Domain of the app (e.g., “http://myawesomeapp.com “). No “myawesomeapp.com”
storeurl string URL of the app’s listing in the app store (e.g., Google Play Store or Apple App Store). No https://play.google.com/store/apps/details?id=com.myawesomeapp"
cat array Array of IAB content categories that describe the application (e.g., [“IAB1”, “IAB9”]). No [“IAB1”, “IAB9-30”] (Arts & Entertainment, Political News)
ver string Application version (e.g., “1.2.3”). No “1.2.3”
privacypolicy integer Indicates if the app has a privacy policy (1 for yes, 0 for no). No 1
paid integer Indicates if the app is a paid version (1 for paid, 0 for free). No 0
publisher object A Publisher object containing details about the application’s publisher. See Section 6.6 for details. No { “id”: “pub123”, “name”: “Awesome Publishers Inc.” }
content object Content object describing the content within the app. No { “id”: “content1”, “title”: “News Article”, “series”: “Daily News” }
keywords string Comma-separated list of keywords describing the app. No “game,puzzle,fun”
ext object Placeholder for app-specific extensions. No { “sdk_version”: “1.0.0” }

6.3 Site Object

The Site object should be used when the ad opportunity is on a mobile-optimized website or desktop website. It provides details about the website context.

Parameter Type Description Required Sample Value
id string Exchange-specific site ID. No “67890”
name string Site name (e.g., “News Daily”). No “Tech News Hub”
domain string Domain of the site (e.g., “http://newsdaily.com “). Crucial for site blocking and categorization. Yes “technewshub.com”
cat array Array of IAB content categories that describe the site (e.g., [“IAB3”, “IAB12”]). No [“IAB3”, “IAB12”] (Business, Science)
page string URL of the page where the impression will be shown. No http://technewshub.com/article/latest-gadgets"
ref string Referrer URL that caused the browser to navigate to the current page. No http://google.com/search?q=latest+tech"
privacypolicy integer Indicates if the site has a privacy policy (1 for yes, 0 for no). No 1
publisher object A Publisher object containing details about the site’s publisher. See Section 6.6 for details. No { “id”: “pub456”, “name”: “Tech Hub Media” }
content object Content object describing the content on the page. No { “id”: “article123”, “title”: “New Gadgets Revealed”, “language”: “en” }
keywords string Comma-separated list of keywords describing the content on the page. No “gadgets,tech,reviews”
mobile integer Indicates if the site is mobile-optimized (1 for yes, 0 for no). No 1
ext object Placeholder for site-specific extensions. No { “ad_placement_type”: “top_banner” }

6.4 User Object

The User object contains information about the end-user. While often sparse due to privacy concerns, any available information can aid in targeting.

Parameter Type Description Required Sample Value
id string Unique consumer ID (preferably but not necessarily hashed); encrypted by exchange. This is a “depersonalized” user identifier useful for frequency capping and audience segmentation by bidders. No “hashed_user_id_12345”
buyeruid string Buyer-specific user ID. This is a user ID previously passed in a creative to the publisher and that the publisher is now passing back to the exchange. This ID is used for DSP to recognize its own users. No “dsp_user_XYZ”
yob integer Year of birth (e.g., 1985). No 1985
gender string Gender (“M” for male, “F” for female, “O” for other). No “M”
keywords string Comma-separated list of keywords, describing the user’s interests or content consumed. No “sports,news,travel”
data array Array of Data objects containing segment data. These typically come from third-party data providers or first-party data. Each Data object can contain an id (provider ID), name (provider name), and segment (array of Segment objects, where each Segment has an id and optionally a name or value). No [{ “id”: “seg1”, “name”: “Segment Provider 1”, “segment”: [{ “id”: “seg_a” }] }]
geo object A Geo object containing geographical location information. This can be user-provided or inferred. See Section 6.5 for details. No { “lat”: 34.0522, “lon”: -118.2437, “country”: “USA” }
consent string Consent string (e.g., IAB TCF 2.0 consent string). Important for privacy regulations like GDPR/CCPA. No “BOO-oKxOO-oKxAA-ABAB”
ext object Placeholder for user-specific extensions. No { “opt_out”: 0 }

6.5 Geo Object

The Geo object describes the geographical location. It can be provided at the Device level (e.g., from GPS) or User level (e.g., inferred from IP, user-provided).

Parameter Type Description Required Sample Value
lat float Latitude (WGS84). No 34.0522
lon float Longitude (WGS84). No -118.2437
country string Country code using ISO 3166-1 Alpha 3 (e.g., “USA”). No “USA”
region string Region code using ISO 3166-2 (e.g., “CA” for California). No “CA”
city string City name (e.g., “Los Angeles”). No “Los Angeles”
zip string Zip or postal code. No “90210”
type integer Source of location data (refer to OpenRTB 2.6 Table 5.23, e.g., 1 for GPS). No 1 (GPS/Location Services)
utcoffset integer Local time as the number of minutes from UTC (e.g., -240 for EST). No -240
ext object Placeholder for geo-specific extensions. No { “accuracy”: 10 }

6.6 Publisher Object

The Publisher object describes the entity that publishes the app or site.

Parameter Type Description Required Sample Value
id string Exchange-specific publisher ID. No “pub123”
name string Publisher name (e.g., “Awesome Publishers Inc.”). No “Awesome Publishers Inc.”
cat array Array of IAB content categories that describe the publisher’s main content. No [“IAB1”, “IAB12”]
domain string Domain of the publisher (e.g., “awesomepublishers.com”). No “awesomepublishers.com”
ext object Placeholder for publisher-specific extensions. No { “revenue_share”: 0.7 }

Response Object (BidResponse)

Upon receiving a BidRequest, our API will return a BidResponse if there are matching bids.

Key Parameters

Parameter Type Description Required Sample Value
id string ID of the bid request to which this is a response. Yes “1234567890”
seatbid array Array of SeatBid objects. Each object contains bids from a specific bidder seat. No [{ … }]
bidid string Unique ID for this bid response. No “abcxyz123”
cur string Currency of all bid prices in this response. Defaults to “USD”. No “USD”
nbr integer No Bid Reason Code. If no bids are returned, this can indicate the reason. No 1 (Unknown Error)

Sample BidResponse JSON

  {
  "id": "1234567890",
  "bidid": "bidresponse_id_123",
  "cur": "USD",
  "seatbid": [
    {
      "seat": "dsp_seat_A",
      "bid": [
        {
          "id": "bid_id_1",
          "impid": "1",
          "price": 1.25,
          "nurl": "https://rtb.nexverse.ai/win_notice?bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&price=${AUCTION_PRICE}",
          "adm": "<div style=\"width:300px; height:250px; background-color:#f0f0f0; border:1px solid #ccc; display:flex; justify-content:center; align-items:center; font-family: 'Inter', sans-serif;\"><h2>Ad Creative!</h2><p>This is a sample ad.</p><button style=\"padding: 8px 16px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer;\">Click Me</button></div>",
          "adomain": ["examplead.com"],
          "iurl": "https://rtb.nexverse.ai/creative_preview.jpg",
          "crid": "creative_id_xyz",
          "w": 300,
          "h": 250,
          "ext": {
            "vasttag": "https://rtb.nexverse.ai/vasttag.xml"
          }
        }
      ]
    }
  ]
}
  

SeatBid Object (SeatBid)

A SeatBid object contains a set of bids from a specific bidder (or “seat”).

Key Parameters

Parameter Type Description Required Sample Value
bid array Array of Bid objects. Each represents a single bid for an impression. Yes [{ … }]
seat string ID of the bidder seat that made the bids. No “dsp_seat_A”

Bid Object (Bid)

A Bid object represents a single bid for an impression opportunity.

Key Parameters

Parameter Type Description Required Sample Value
id string Unique ID for this bid. Yes “bid_id_1”
impid string ID of the impression object from the BidRequest to which this bid applies. Yes “1”
price float Bid price in cur (currency) of the BidResponse. This is the CPM (Cost Per Mille) value. Yes 1.25
nurl string Win notice URL. This URL should be called by the SDK upon rendering the ad to notify the exchange of a successful impression. It supports OpenRTB macros (e.g., ${AUCTION_PRICE}, ${AUCTION_BID_ID}). No https://rtb.nexverse.ai/win_notice?bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&price=${AUCTION_PRICE}"
adm string Ad Markup. This contains the actual HTML or VAST XML for the creative. For banner ads, it’s typically HTML. For video ads, it’s VAST XML. This is what your SDK should render. Yes “<div style="width:300px; height:250px; background-color:#f0f0f0;">… (HTML snippet) or <VAST version="2.0">… (VAST XML)”
adomain array Array of advertiser domains. Used for blocking purposes. No [“advertiser.com”]
iurl string Sample image URL. For display only, not for rendering. No https://rtb.nexverse.ai/sample_image.jpg"
crid string Creative ID. Unique ID for the creative within the bidder’s system. No “creative_123”
w integer Width of the ad creative in pixels. No 300
h integer Height of the ad creative in pixels. No 250
ext object Placeholder for bidder-specific extensions. Can contain additional data like VAST tag URLs for video, specific tracking pixels, etc. SDKs should parse this cautiously. If adm contains VAST XML directly, this might be empty. If it contains a VAST tag URL, then this extension might be used to provide it. Example: { “vasttag”: “https://rtb.nexverse.ai/vast?id=123" } SDK should prioritize adm content. No { “tracking_pixel”: “https://rtb.nexverse.ai/track", “custom_data”: “value” } for banner, or { “vasttag”: “https://rtb.nexverse.ai/vast.xml" } for video (if adm is not full VAST)

Error Handling

HTTP Status Codes

  • 204 No Content: The request was valid, but no bids were found for the impression(s). The nbr field in the BidResponse (if present) may provide additional context.
  • 400 Bad Request: The request was malformed or missing required parameters. Check the request JSON structure against the OpenRTB specification.
  • 500 Internal Server Error: An unexpected error occurred on the server. Retry the request; if the issue persists, contact support.

SDK Integration Steps

  1. Construct BidRequest: Based on the current app/site context, device information, and available impression slots, create a BidRequest JSON object. Ensure all required fields are populated.
  2. Send Request:
    • Perform an HTTP POST request to https://rtb.nexverse.ai/.
    • Set the Content-Type header to application/json.
    • Include the BidRequest JSON as the request body.
    • Set a reasonable timeout for the request (e.g., tmax from the request, or a slightly higher value).
  3. Parse BidResponse:
    • Upon receiving an HTTP 200 OK response, parse the JSON response body into a BidResponse object.
    • Check for the presence of seatbid and bid arrays.
  4. Process and Render Ad:
    • Iterate through the seatbid and bid objects.
    • For each valid bid object:
      • Extract the adm (Ad Markup).
      • Determine the ad type (HTML for banner, VAST XML for video) based on content or imp type from the original request.
      • Render the adm in the designated ad slot.
      • If nurl is present, fire the win notice URL after the ad has been successfully rendered and is visible to the user. Replace ORTB macros (e.g., ${AUCTION_PRICE}, ${AUCTION_BID_ID}, ${AUCTION_IMP_ID}) with actual values.

Code Snippets

Here are examples of how your SDK might interact with the API in JavaScript/Node.js/Python and Java.

JavaScript (Node.js/Browser Fetch API)

  /**
 * Function to construct and send an OpenRTB BidRequest.
 * @param {object} bidRequest The OpenRTB BidRequest object.
 * @returns {Promise<object|null>} A promise that resolves with the BidResponse object or null on error/no bids.
 */
async function sendBidRequest(bidRequest) {
  const endpoint = 'https://rtb.nexverse.ai/';

  try {
    const response = await fetch(endpoint, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      },
      body: JSON.stringify(bidRequest),
      // Consider a timeout for real-time bidding, e.g., using AbortController
      // signal: abortController.signal
    });

    if (response.status === 204) {
      console.log('No bids found for this impression.');
      return null;
    }

    if (!response.ok) {
      const errorText = await response.text();
      console.error(`HTTP error! Status: ${response.status}, Details: ${errorText}`);
      throw new Error(`API request failed with status ${response.status}`);
    }

    const bidResponse = await response.json();
    return bidResponse;

  } catch (error) {
    console.error('Error sending bid request:', error);
    return null;
  }
}

/**
 * Example usage:
 */
const sampleBidRequest = {
  "id": "test_request_123",
  "imp": [{
    "id": "1",
    "banner": {
      "w": 300,
      "h": 250
    },
    "bidfloor": 0.50,
    "bidfloorcur": "USD"
  }],
  "device": {
    "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36",
    "ip": "192.168.1.1",
    "make": "Google",
    "model": "Pixel 6",
    "os": "Android",
    "osv": "12",
    "devicetype": 1 // Mobile
  },
  "app": {
    "id": "com.mytestapp",
    "name": "My Test App",
    "bundle": "com.mytestapp",
    "cat": ["IAB1"]
  },
  "at": 2,
  "tmax": 120
};

// Immediately invoked async function for demonstration
(async () => {
  console.log('Sending sample bid request...');
  const response = await sendBidRequest(sampleBidRequest);

  if (response) {
    console.log('Received BidResponse:', response);
    // Process bids and render ad
    if (response.seatbid && response.seatbid.length > 0) {
      response.seatbid.forEach(seatBid => {
        seatBid.bid.forEach(bid => {
          console.log(`Bid ID: ${bid.id}, Price: ${bid.price}, Impression ID: ${bid.impid}`);
          console.log('Ad Markup (adm):', bid.adm);

          // Example: Render HTML creative (for a banner ad)
          const adContainer = document.getElementById('ad-slot-' + bid.impid);
          if (adContainer && bid.adm) {
            adContainer.innerHTML = bid.adm; // Inject the HTML creative
            console.log(`Ad for imp ID ${bid.impid} rendered.`);

            // Fire win notice if nurl is present
            if (bid.nurl) {
              const winNoticeUrl = bid.nurl
                .replace('${AUCTION_BID_ID}', response.bidid || '')
                .replace('${AUCTION_IMP_ID}', bid.impid)
                .replace('${AUCTION_PRICE}', bid.price);
              console.log('Firing win notice URL:', winNoticeUrl);
              // In a real SDK, you'd make an HTTP GET request to this URL
              fetch(winNoticeUrl, { method: 'GET', mode: 'no-cors' }) // Use no-cors for beaconing
                .then(() => console.log('Win notice fired successfully.'))
                .catch(err => console.error('Error firing win notice:', err));
            }
          }
        });
      });
    } else {
      console.log('BidResponse received, but no bids were found.');
    }
  } else {
    console.log('No BidResponse received.');
  }
})();

// For a browser environment, you would need a div element to inject the ad
/*
<body>
  <div id="ad-slot-1" style="width:300px; height:250px; border:1px solid lightgray; margin: 20px; display: flex; justify-content: center; align-items: center; background-color: #f9f9f9; font-family: 'Inter', sans-serif; border-radius: 8px;">
    Ad will appear here for imp ID 1
  </div>
</body>
*/
  

Python

  import requests
import json

def send_bid_request(bid_request):
    """
    Sends an OpenRTB BidRequest to the endpoint.

    Args:
        bid_request (dict): The OpenRTB BidRequest dictionary.

    Returns:
        dict or None: The BidResponse dictionary if successful, None otherwise.
    """
    endpoint = 'https://rtb.nexverse.ai/'
    headers = {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
    }

    try:
        response = requests.post(endpoint, headers=headers, data=json.dumps(bid_request), timeout=0.150) # 150ms timeout
        response.raise_for_status() # Raises HTTPError for bad responses (4xx or 5xx)

        if response.status_code == 204:
            print("No bids found for this impression.")
            return None
        
        return response.json()

    except requests.exceptions.Timeout:
        print("The request timed out.")
        return None
    except requests.exceptions.HTTPError as err:
        print(f"HTTP error occurred: {err} - {err.response.text}")
        return None
    except requests.exceptions.RequestException as err:
        print(f"An error occurred: {err}")
        return None

def fire_win_notice(nurl, bid_id, imp_id, price):
    """
    Fires the win notice URL after a successful impression.
    """
    if nurl:
        win_notice_url = nurl \
            .replace('${AUCTION_BID_ID}', bid_id) \
            .replace('${AUCTION_IMP_ID}', imp_id) \
            .replace('${AUCTION_PRICE}', str(price)) # Price might need to be stringified

        print(f"Firing win notice URL: {win_notice_url}")
        try:
            # Use a short timeout and don't care about the response for beaconing
            requests.get(win_notice_url, timeout=1, stream=True)
            print("Win notice fired successfully.")
        except requests.exceptions.RequestException as err:
            print(f"Error firing win notice: {err}")

# Example usage:
sample_bid_request = {
  "id": "test_py_request_001",
  "imp": [{
    "id": "1",
    "banner": {
      "w": 300,
      "h": 250
    },
    "bidfloor": 0.50,
    "bidfloorcur": "USD"
  }],
  "device": {
    "ua": "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.58 Mobile Safari/537.36",
    "ip": "192.168.1.100",
    "make": "Python",
    "model": "SDK",
    "os": "Linux",
    "osv": "5.4",
    "devicetype": 1
  },
  "app": {
    "id": "com.pythonsdk.app",
    "name": "Python SDK Test App",
    "bundle": "com.pythonsdk.app",
    "cat": ["IAB1"]
  },
  "at": 2,
  "tmax": 120
}

print("Sending sample bid request from Python...")
bid_response = send_bid_request(sample_bid_request)

if bid_response:
    print("\nReceived BidResponse:")
    print(json.dumps(bid_response, indent=2))

    if 'seatbid' in bid_response and bid_response['seatbid']:
        for seat_bid in bid_response['seatbid']:
            for bid in seat_bid.get('bid', []):
                print(f"\nProcessing Bid ID: {bid.get('id')}")
                print(f"  Impression ID: {bid.get('impid')}")
                print(f"  Price: {bid.get('price')}")
                print(f"  Ad Markup (first 100 chars): {bid.get('adm', '')[:100]}...")

                # In a real SDK, you would parse and render 'adm'
                # For demonstration, we'll just fire the win notice if available.
                
                # Fire win notice if nurl is present
                if bid.get('nurl'):
                    fire_win_notice(
                        bid['nurl'],
                        bid_response.get('bidid', ''), # Use actual bid response ID
                        bid['impid'],
                        bid['price']
                    )
    else:
        print("BidResponse received, but no bids were found.")
else:
    print("No BidResponse received.")
  

Android (Java)

For Android integration, it’s highly recommended to use a networking library like OkHttp for making HTTP requests and a JSON parsing library like Gson or Jackson for serializing/deserializing JSON objects.

Gradle Dependencies (build.gradle)

Add these to your app/build.gradle file:

  dependencies {
    implementation 'com.squareup.okhttp3:okhttp:4.10.0'
    implementation 'com.google.code.gson:gson:2.10.1'
}
  

AndroidManifest.xml

Ensure you have internet permissions:

  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yourapp">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        ...
    </application>
</manifest>
  

Java Code Snippet

  import android.os.AsyncTask;
import android.util.Log;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class OrtbiAdManager {

    private static final String TAG = "OrtbiAdManager";
    private static final String API_ENDPOINT = "https://rtb.nexverse.ai/";
    private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");

    private OkHttpClient httpClient;
    private Gson gson;

    public OrtbiAdManager() {
        // Initialize OkHttpClient with a timeout
        httpClient = new OkHttpClient.Builder()
                .connectTimeout(150, TimeUnit.MILLISECONDS) // Connection timeout
                .readTimeout(150, TimeUnit.MILLISECONDS)    // Read timeout
                .build();
        gson = new Gson();
    }

    /**
     * Represents a simplified OpenRTB BidRequest object for demonstration.
     * In a real SDK, this would be a full class hierarchy matching OpenRTB spec.
     */
    public static class BidRequest {
        @SerializedName("id")
        public String id;
        @SerializedName("imp")
        public List<Impression> imp;
        @SerializedName("device")
        public Device device;
        @SerializedName("app") // Or @SerializedName("site")
        public App app;
        @SerializedName("user")
        public User user;
        @SerializedName("test")
        public int test;
        @SerializedName("at")
        public int at;
        @SerializedName("tmax")
        public int tmax;
        @SerializedName("cur")
        public List<String> cur;
        @SerializedName("bcat")
        public List<String> bcat;
        @SerializedName("badv")
        public List<String> badv;
        @SerializedName("bapp")
        public List<String> bapp;

        // Nested classes for BidRequest components (Impression, Device, App, User, etc.)
        public static class Impression {
            @SerializedName("id") public String id;
            @SerializedName("banner") public Banner banner;
            @SerializedName("video") public Video video;
            @SerializedName("bidfloor") public Double bidfloor;
            @SerializedName("bidfloorcur") public String bidfloorcur;
            @SerializedName("secure") public Integer secure;
            // More fields as per ORTB spec
        }

        public static class Banner {
            @SerializedName("w") public Integer w;
            @SerializedName("h") public Integer h;
            @SerializedName("pos") public Integer pos;
            // More fields as per ORTB spec
        }

        public static class Video {
            @SerializedName("mimes") public List<String> mimes;
            @SerializedName("minduration") public Integer minduration;
            @SerializedName("maxduration") public Integer maxduration;
            // More fields as per ORTB spec
        }

        public static class Device {
            @SerializedName("ua") public String ua;
            @SerializedName("ip") public String ip;
            @SerializedName("make") public String make;
            @SerializedName("model") public String model;
            @SerializedName("os") public String os;
            @SerializedName("osv") public String osv;
            @SerializedName("ifa") public String ifa;
            @SerializedName("connectiontype") public Integer connectiontype;
            @SerializedName("devicetype") public Integer devicetype;
            // More fields
        }

        public static class App {
            @SerializedName("id") public String id;
            @SerializedName("name") public String name;
            @SerializedName("bundle") public String bundle;
            @SerializedName("cat") public List<String> cat;
            public Publisher publisher; // Nested Publisher
        }
        
        public static class Publisher {
            @SerializedName("id") public String id;
            @SerializedName("name") public String name;
        }

        public static class User {
            @SerializedName("id") public String id;
            @SerializedName("buyeruid") public String buyeruid;
            @SerializedName("gender") public String gender;
            @SerializedName("yob") public Integer yob;
            @SerializedName("geo") public Geo geo;
        }

        public static class Geo {
            @SerializedName("lat") public Double lat;
            @SerializedName("lon") public Double lon;
            @SerializedName("country") public String country;
            @SerializedName("region") public String region;
        }
    }

    /**
     * Represents a simplified OpenRTB BidResponse object.
     */
    public static class BidResponse {
        @SerializedName("id")
        public String id;
        @SerializedName("bidid")
        public String bidid;
        @SerializedName("cur")
        public String cur;
        @SerializedName("seatbid")
        public List<SeatBid> seatbid;
        @SerializedName("nbr")
        public Integer nbr;

        // Nested classes for BidResponse components
        public static class SeatBid {
            @SerializedName("seat")
            public String seat;
            @SerializedName("bid")
            public List<Bid> bid;
        }

        public static class Bid {
            @SerializedName("id")
            public String id;
            @SerializedName("impid")
            public String impid;
            @SerializedName("price")
            public Double price;
            @SerializedName("nurl")
            public String nurl;
            @SerializedName("adm")
            public String adm; // Ad Markup (HTML or VAST XML)
            @SerializedName("adomain")
            public List<String> adomain;
            @SerializedName("iurl")
            public String iurl;
            @SerializedName("crid")
            public String crid;
            @SerializedName("w")
            public Integer w;
            @SerializedName("h")
            public Integer h;
            @SerializedName("ext")
            public Object ext; // Generic object for extensions
        }
    }

    /**
     * Asynchronously sends a BidRequest and processes the BidResponse.
     * Use a callback interface to deliver the response to the calling Activity/Fragment.
     */
    public interface AdResponseListener {
        void onAdReceived(BidResponse.Bid bid);
        void onNoAd(String message);
        void onError(String errorMessage);
    }

    public void requestAd(BidRequest bidRequest, AdResponseListener listener) {
        new AsyncTask<BidRequest, Void, BidResponse>() {
            private String errorMessage = null;

            @Override
            protected BidResponse doInBackground(BidRequest... requests) {
                try {
                    String json = gson.toJson(requests[0]);
                    RequestBody body = RequestBody.create(json, JSON);
                    Request request = new Request.Builder()
                            .url(API_ENDPOINT)
                            .post(body)
                            .build();

                    try (Response response = httpClient.newCall(request).execute()) {
                        if (response.code() == 204) {
                            errorMessage = "No bids found for this impression (HTTP 204).";
                            return null;
                        }
                        if (!response.isSuccessful()) {
                            errorMessage = "API request failed: HTTP " + response.code() + " - " + response.message() + " " + response.body().string();
                            Log.e(TAG, errorMessage);
                            return null;
                        }

                        String responseBody = response.body().string();
                        return gson.fromJson(responseBody, BidResponse.class);

                    }
                } catch (IOException e) {
                    errorMessage = "Network error or API call failed: " + e.getMessage();
                    Log.e(TAG, errorMessage, e);
                    return null;
                } catch (Exception e) {
                    errorMessage = "Error parsing response or unexpected error: " + e.getMessage();
                    Log.e(TAG, errorMessage, e);
                    return null;
                }
            }

            @Override
            protected void onPostExecute(BidResponse bidResponse) {
                if (bidResponse != null && bidResponse.seatbid != null && !bidResponse.seatbid.isEmpty()) {
                    // For simplicity, take the first bid from the first seatbid.
                    // A real SDK might have more sophisticated bid selection logic.
                    if (bidResponse.seatbid.get(0).bid != null && !bidResponse.seatbid.get(0).bid.isEmpty()) {
                        BidResponse.Bid winningBid = bidResponse.seatbid.get(0).bid.get(0);
                        listener.onAdReceived(winningBid);
                    } else {
                        listener.onNoAd("BidResponse received, but no bids found in seatbid.");
                    }
                } else if (errorMessage != null) {
                    listener.onError(errorMessage);
                } else {
                    listener.onNoAd("No BidResponse or empty BidResponse received.");
                }
            }
        }.execute(bidRequest);
    }

    /**
     * Fires the win notice URL. This should be called after the ad is displayed.
     * This is a non-blocking call.
     */
    public void fireWinNotice(String nurl, String bidId, String impId, double price) {
        if (nurl == null || nurl.isEmpty()) {
            Log.w(TAG, "Win notice URL is empty. Cannot fire win notice.");
            return;
        }

        final String finalNurl = nurl
                .replace("${AUCTION_BID_ID}", bidId != null ? bidId : "")
                .replace("${AUCTION_IMP_ID}", impId != null ? impId : "")
                .replace("${AUCTION_PRICE}", String.valueOf(price));

        new Thread(() -> {
            try {
                Request request = new Request.Builder()
                        .url(finalNurl)
                        .get()
                        .build();
                // Use a short timeout for beaconing and don't care about the response body
                httpClient.newCall(request).execute().close();
                Log.d(TAG, "Win notice fired successfully: " + finalNurl);
            } catch (IOException e) {
                Log.e(TAG, "Error firing win notice: " + finalNurl, e);
            }
        }).start();
    }

    // --- Example Usage within an Activity/Fragment ---
    // In your MainActivity or a custom AdView class

    private OrtbiAdManager adManager;
    private WebView adWebView; // Assuming you have a WebView in your layout to display ads

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); // Your layout

        adManager = new OrtbiAdManager();
        adWebView = findViewById(R.id.ad_webview); // Assuming you have <WebView android:id="@+id/ad_webview" ... />

        // Configure WebView for displaying HTML ads
        adWebView.getSettings().setJavaScriptEnabled(true);
        adWebView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                // Handle clicks on the ad creative. Typically, you'd open the URL in a browser or custom tab.
                Log.d(TAG, "Ad creative clicked: " + url);
                // For example: startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
                return true; // Return true to indicate you handled the URL
            }
        });

        // Create a sample BidRequest (populate with real device/app info)
        OrtbiAdManager.BidRequest bidRequest = new OrtbiAdManager.BidRequest();
        bidRequest.id = "android_request_" + System.currentTimeMillis();
        
        // Populate Impression
        OrtbiAdManager.BidRequest.Impression imp = new OrtbiAdManager.BidRequest.Impression();
        imp.id = "1";
        OrtbiAdManager.BidRequest.Banner banner = new OrtbiAdManager.BidRequest.Banner();
        banner.w = 320;
        banner.h = 50;
        imp.banner = banner;
        imp.bidfloor = 0.50;
        imp.bidfloorcur = "USD";
        imp.secure = 1;
        bidRequest.imp = Collections.singletonList(imp);

        // Populate Device (example, you'd collect real device data)
        OrtbiAdManager.BidRequest.Device device = new OrtbiAdManager.BidRequest.Device();
        device.ua = System.getProperty("http.agent"); // User agent
        device.ip = "192.168.1.10"; // Local IP or public IP if accessible
        device.make = android.os.Build.MANUFACTURER;
        device.model = android.os.Build.MODEL;
        device.os = "Android";
        device.osv = android.os.Build.VERSION.RELEASE;
        device.ifa = "your_advertising_id_here"; // Get from Google Play Services API
        device.connectiontype = 2; // Wifi
        device.devicetype = 1; // Mobile
        bidRequest.device = device;

        // Populate App (example, use your app's actual package name, etc.)
        OrtbiAdManager.BidRequest.App app = new OrtbiAdManager.BidRequest.App();
        app.id = getPackageName();
        app.name = getApplicationInfo().loadLabel(getPackageManager()).toString();
        app.bundle = getPackageName();
        app.cat = Arrays.asList("IAB1", "IAB9"); // Example categories
        OrtbiAdManager.BidRequest.Publisher publisher = new OrtbiAdManager.BidRequest.Publisher();
        publisher.id = "android_pub_id";
        publisher.name = "My Android App Publisher";
        app.publisher = publisher;
        bidRequest.app = app;
        
        bidRequest.at = 2;
        bidRequest.tmax = 120;
        bidRequest.cur = Collections.singletonList("USD");
        bidRequest.test = 0;


        // Make the ad request
        requestAd(bidRequest, new AdResponseListener() {
            @Override
            public void onAdReceived(BidResponse.Bid bid) {
                Log.d(TAG, "Ad received! Creative ID: " + bid.crid);
                // Load HTML ad into WebView
                adWebView.loadDataWithBaseURL("about:blank", bid.adm, "text/html", "UTF-8", null);

                // Fire win notice after ad is loaded/displayed
                // In a real scenario, you might want to wait for WebView to fully load
                // or for the ad to be deemed 'viewable' before firing.
                adManager.fireWinNotice(bid.nurl, bid.id, bid.impid, bid.price);
            }

            @Override
            public void onNoAd(String message) {
                Log.d(TAG, "No ad received: " + message);
                adWebView.loadDataWithBaseURL("about:blank", "<div style='text-align:center; padding:20px;'>No ad available.</div>", "text/html", "UTF-8", null);
            }

            @Override
            public void onError(String errorMessage) {
                Log.e(TAG, "Ad request error: " + errorMessage);
                adWebView.loadDataWithBaseURL("about:blank", "<div style='text-align:center; padding:20px; color:red;'>Error loading ad: " + errorMessage + "</div>", "text/html", "UTF-8", null);
            }
        });
    }

    // In a real SDK, you would abstract device and app information gathering.
    // Example: Getting Advertising ID (requires Google Play Services dependency)
    private String getAdvertisingId(Context context) {
        try {
            AdvertisingIdClient.Info adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context);
            return adInfo.getId();
        } catch (Exception e) {
            Log.e(TAG, "Could not get Advertising ID: " + e.getMessage());
            return null;
        }
    }
}