Ortb Integration Guide
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
- 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.
- 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).
- Perform an HTTP POST request to
- 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.
- 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;
}
}
}