Appearance
Opera Ads iOS SDK - Server Bidding (S2S)
Which integration is this page for?
This page describes how to collect a bid token from the Opera Ads SDK and run the auction on your own server.
If you integrate Opera Ads through a mediation platform (TopOn, TradPlus, AppLovin MAX, AdMob or LevelPlay), Opera Ads serves through that platform's waterfall and you do not need any of the APIs on this page. See the Mediation guides instead.
Overview
Server-to-Server (S2S) Bidding, also known as Server-Side Bidding or Header Bidding, lets your own server collect an Opera Ads bid and run the auction against your other demand sources. The auction is yours — Opera Ads is one of the bidders in it.
Benefits of Server Bidding
- Centralized Auction Logic: Manage bidding on your server
- Reduced Client Complexity: Less code in your app
- Flexibility: Easy to add/remove ad networks
- Cross-Platform Consistency: Same auction logic for iOS and Android
- Better Analytics: Centralized bid tracking and reporting
How It Works
┌─────────────────────────────────────────────────────────┐
│ 1. App requests bid token from Opera Ads SDK │
│ ┌──────────────┐ │
│ │ getBidToken │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ Bid Token (compressed signals) │
│ │ │
│ ▼ │
│ 2. Send bid token to your auction server │
│ ┌──────────────┐ │
│ │ POST /auction│ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ 3. Server runs auction with Opera and other networks │
│ ┌──────────────┐ │
│ │Opera: $1.50 │ │
│ │Net A: $1.20 │ │
│ │Net B: $1.80 │ ← Winner │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ 4. Server returns bid response (if Opera wins) │
│ ┌──────────────┐ │
│ │Bid Response │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ 5. App loads ad with bid response │
│ ┌──────────────┐ │
│ │ loadRtbAd │ │
│ └──────────────┘ │
└─────────────────────────────────────────────────────────┘Prerequisites
Before implementing server bidding, ensure you have:
Opera Ads SDK Integration
- SDK version 2.1.0 or higher
- Basic integration completed
Server Infrastructure
- Your own auction server
- Ability to process bid tokens
- API endpoints for auction requests/responses
Understanding of S2S Bidding
- Bid token format and usage
- Auction server communication
- Bid response handling
Implementation Steps
Step 1: Request Bid Token
Request a bid token from the Opera Ads SDK. This token contains encrypted bidding signals.
Swift Example:
swift
import OpAdxSdk
class ServerBiddingManager {
func requestBidToken(for adFormat: AdFormat, completion: @escaping (String?) -> Void) {
// Create bid token request
let request = BidTokenRequest.Builder(adMediation: .admob) // Or .max, .topon, .tradplus, .levelplay, .pangle
.placementId("s14198264979520")
.adFormat(adFormat)
.adSize(.MEDIUM_RECTANGLE) // Optional: For banner ads
.build()
// Create callback
class Callback: BidTokenCallback {
let completion: (String?) -> Void
init(completion: @escaping (String?) -> Void) {
self.completion = completion
}
func onSuccess(bidToken: String) {
print("Bid token received: \(bidToken.prefix(50))...")
completion(bidToken)
}
func onError(error: OpAdxAdError) {
print("Bid token request failed: \(error.message)")
completion(nil)
}
}
// Request bid token
OpAdxSDK.getBidToken(request, callback: Callback(completion: completion))
}
}Objective-C Example:
objective-c
#import <OpAdxSdk/OpAdxSdk.h>
// Implement the OpAdxBidTokenCallback protocol
@interface BidTokenCallbackImpl : NSObject <OpAdxBidTokenCallback>
@property (nonatomic, copy) void (^completion)(NSString * _Nullable);
@end
@implementation BidTokenCallbackImpl
- (void)onSuccessWithBidToken:(NSString *)bidToken {
NSLog(@"Bid token received: %@...", [bidToken substringToIndex:MIN(50, bidToken.length)]);
if (self.completion) self.completion(bidToken);
}
- (void)onErrorWithError:(OpAdxAdError *)error {
NSLog(@"Bid token request failed: %@", error.message);
if (self.completion) self.completion(nil);
}
@end
@implementation ServerBiddingManager
- (void)requestBidTokenForAdFormat:(AdFormat)adFormat
completion:(void (^)(NSString * _Nullable))completion {
// Create bid token request
OpAdxBidTokenRequestBuilder *builder = [[OpAdxBidTokenRequestBuilder alloc]
initWithAdMediation:@"admob"];
[builder placementId:@"s14198264979520"];
[builder adFormat:[self adFormatString:adFormat]];
if (adFormat == AdFormatBanner) {
[builder adSize:[OpAdxAdSize MEDIUM_RECTANGLE]];
}
OpAdxBidTokenRequest *request = [builder build];
// Request bid token using protocol callback
BidTokenCallbackImpl *callback = [[BidTokenCallbackImpl alloc] init];
callback.completion = completion;
[OpAdxSDK getBidTokenWithRequest:request callback:callback];
}
@endStep 2: Send Bid Token to Server
Send the bid token to your auction server along with other auction parameters.
Swift:
swift
import Foundation
class AuctionClient {
func sendAuctionRequest(bidToken: String,
adFormat: String,
placementId: String,
completion: @escaping (AuctionResponse?) -> Void) {
// Prepare auction request
let requestBody: [String: Any] = [
"bid_token": bidToken,
"ad_format": adFormat,
"placement_id": placementId,
"device_id": UIDevice.current.identifierForVendor?.uuidString ?? "",
"app_version": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
]
guard let url = URL(string: "https://your-server.com/api/auction"),
let jsonData = try? JSONSerialization.data(withJSONObject: requestBody) else {
completion(nil)
return
}
// Send request
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data,
error == nil,
let auctionResponse = try? JSONDecoder().decode(AuctionResponse.self, from: data) else {
completion(nil)
return
}
completion(auctionResponse)
}.resume()
}
}
struct AuctionResponse: Codable {
let winner: String // "opera" or other network name
let bidResponse: String? // Opera bid response (if Opera won)
let ecpm: Double
let placementId: String
}Objective-C:
objective-c
@implementation AuctionClient
- (void)sendAuctionRequestWithBidToken:(NSString *)bidToken
adFormat:(NSString *)adFormat
placementId:(NSString *)placementId
completion:(void (^)(NSDictionary * _Nullable))completion {
// Prepare auction request
NSDictionary *requestBody = @{
@"bid_token": bidToken,
@"ad_format": adFormat,
@"placement_id": placementId,
@"device_id": [UIDevice currentDevice].identifierForVendor.UUIDString ?: @"",
@"app_version": [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] ?: @""
};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:requestBody
options:0
error:&error];
if (error || !jsonData) {
completion(nil);
return;
}
// Send request
NSURL *url = [NSURL URLWithString:@"https://your-server.com/api/auction"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = jsonData;
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error || !data) {
completion(nil);
return;
}
NSDictionary *auctionResponse = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
completion(auctionResponse);
}] resume];
}
@endStep 3: Load Ad with Bid Response
If Opera Ads wins the server auction, load the ad using the bid response.
Swift:
swift
class ServerBiddingManager {
func handleAuctionResponse(_ response: AuctionResponse) {
guard response.winner == "opera",
let bidResponse = response.bidResponse else {
print("Opera Ads did not win - show other network's ad")
showOtherNetworkAd()
return
}
print("✅ Opera Ads won with eCPM: $\(response.ecpm)")
loadOperaAd(with: bidResponse, placementId: response.placementId)
}
func loadOperaAd(with bidResponse: String, placementId: String) {
let interstitialAd = OpAdxInterstitialAd(
placementId: placementId,
auctionType: .serverBidding // ← Important: Use serverBidding
)
let loadListener = OpAdxInterstitialAdLoadListenerImp(
onAdLoaded: { ad in
print("✅ Opera ad loaded from bid response")
self.showOperaAd(interstitialAd)
},
onAdFailedToLoad: { error in
print("❌ Opera ad failed to load: \(error.message)")
}
)
// Load ad with bid response
interstitialAd.loadRtb(
placementId: placementId,
bidResponse: bidResponse,
listener: loadListener
)
}
func showOperaAd(_ ad: OpAdxInterstitialAd) {
let interactionListener = OpAdxInterstitialAdInteractionListenerImp(
onAdClicked: {
print("Ad clicked")
},
onAdDisplayed: {
print("Ad displayed")
},
onAdDismissed: {
print("Ad dismissed")
},
onAdFailedToShow: { error in
print("Ad failed to show: \(error.message)")
}
)
ad.show(on: self.viewController, listener: interactionListener)
}
}Objective-C:
objective-c
@implementation ServerBiddingManager
- (void)handleAuctionResponse:(NSDictionary *)response {
NSString *winner = response[@"winner"];
NSString *bidResponse = response[@"bid_response"];
if (![winner isEqualToString:@"opera"] || !bidResponse) {
NSLog(@"Opera Ads did not win - show other network's ad");
[self showOtherNetworkAd];
return;
}
double ecpm = [response[@"ecpm"] doubleValue];
NSLog(@"✅ Opera Ads won with eCPM: $%.4f", ecpm);
[self loadOperaAdWithBidResponse:bidResponse placementId:response[@"placement_id"]];
}
- (void)loadOperaAdWithBidResponse:(NSString *)bidResponse placementId:(NSString *)placementId {
OpAdxInterstitialAdBridge *interstitialAd = [[OpAdxInterstitialAdBridge alloc]
initWithPlacementId:placementId
auctionType:AdAuctionTypeServerBidding]; // ← Important
interstitialAd.delegate = self;
// Load ad with bid response
[interstitialAd loadRtbWithPlacementId:placementId bidResponse:bidResponse];
}
#pragma mark - OpAdxInterstitialAdDelegate
- (void)interstitialAdDidLoad:(OpAdxInterstitialAdBridge *)interstitialAd {
NSLog(@"✅ Opera ad loaded from bid response");
[self showOperaAd:interstitialAd];
}
- (void)interstitialAd:(OpAdxInterstitialAdBridge *)interstitialAd
didFailWithError:(OpAdxAdError *)error {
NSLog(@"❌ Opera ad failed to load: %@", error.message);
}
@endAd Format Implementations
Banner Ads
Swift:
swift
func loadBannerWithServerBidding(bidResponse: String) {
let bannerAd = OpAdxBannerAdView()
bannerAd.setPlacementId("s14170965187264")
bannerAd.setAdSize(.BANNER_MREC)
let listener = OpAdxBannerAdListenerImp(
onAdLoaded: { [weak self] bannerInfo in
print("Banner loaded from bid response")
self?.showBanner(bannerAd)
},
onAdFailedToLoad: { error in
print("Banner failed: \(error.message)")
},
onAdImpression: {
print("Banner impression")
},
onAdClicked: {
print("Banner clicked")
}
)
// Load banner with bid response
bannerAd.loadRtbAd(bidResponse: bidResponse, listener: listener)
}
func showBanner(_ banner: OpAdxBannerAdView) {
view.addSubview(banner)
banner.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
banner.centerXAnchor.constraint(equalTo: view.centerXAnchor),
banner.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
banner.widthAnchor.constraint(equalToConstant: 300),
banner.heightAnchor.constraint(equalToConstant: 250)
])
}Objective-C:
objective-c
- (void)loadBannerWithServerBidding:(NSString *)bidResponse {
OpAdxBannerAdBridge *bannerAd = [[OpAdxBannerAdBridge alloc]
initWithPlacementId:@"s14170965187264"
adSize:OpAdxAdSize.BANNER_MREC];
bannerAd.delegate = self;
// Load banner with bid response
[bannerAd loadRtbAdWithBidResponse:bidResponse];
}
- (void)bannerAdDidLoad:(OpAdxBannerAdBridge *)bannerAd {
NSLog(@"Banner loaded from bid response");
[self showBanner:bannerAd];
}Native Ads
Swift:
swift
func loadNativeWithServerBidding(bidResponse: String) {
let nativeAd = OpAdxNativeAd(
placementId: "s14198263063424",
auctionType: .serverBidding
)
let listener = OpAdxNativeAdListenerImp(
onAdLoaded: { [weak self] ad in
guard let self = self, let nativeAd = ad as? OpAdxNativeAd else { return }
print("Native ad loaded from bid response")
self.renderNativeAd(nativeAd)
},
onAdFailedToLoad: { error in
print("Native ad failed: \(error.message)")
},
onAdImpression: {
print("Native ad impression")
},
onAdClicked: {
print("Native ad clicked")
}
)
// Load native ad with bid response
nativeAd.loadRtb(bidResponse: bidResponse, listener: listener)
}
func renderNativeAd(_ ad: OpAdxNativeAd) {
let nativeAdView = OpAdxNativeAdView(frame: CGRect(x: 0, y: 0, width: 300, height: 400))
nativeAdView.configure(with: ad)
let rootView = OpAdxNativeAdRootView(root: nativeAdView)
ad.registerInteractionViews(
container: rootView,
interactionViews: nativeAdView.interactionViews,
adChoicePosition: .topRight
)
adContainer.addSubview(nativeAdView)
}Rewarded Ads
Swift:
swift
func loadRewardedWithServerBidding(bidResponse: String) {
let rewardedAd = OpAdxRewardedAd(
placementId: "s14198592226752",
auctionType: .serverBidding
)
let loadListener = OpAdxRewardedAdLoadListenerImp(
onAdLoaded: { [weak self] ad in
print("Rewarded ad loaded from bid response")
self?.showRewardedAd(rewardedAd)
},
onAdFailedToLoad: { error in
print("Rewarded ad failed: \(error.message)")
}
)
// Load rewarded ad with bid response
rewardedAd.loadRtb(placementId: "s14198592226752", bidResponse: bidResponse, listener: loadListener)
}App Open Ads
Swift:
swift
func loadAppOpenWithServerBidding(bidResponse: String) {
let appOpenAd = OpAdxAppOpenAd(
placementId: "s14496438551808",
auctionType: .serverBidding
)
let loadListener = OpAdxAppOpenAdLoadListenerImp(
onAdLoaded: { [weak self] ad in
print("App open ad loaded from bid response")
self?.showAppOpenAd(appOpenAd)
},
onAdFailedToLoad: { error in
print("App open ad failed: \(error.message)")
}
)
// Load app open ad with bid response
appOpenAd.loadRtb(context: self, placementId: "s14496438551808", bidResponse: bidResponse, listener: loadListener)
}Bid Token Request Configuration
Ad Mediation Label
BidTokenRequest.Builder requires an adMediation value. It is a reporting label only: the SDK forwards it to the Opera ad server as an attribution tag so Opera can group your S2S traffic in reporting. It does not change the bid, the auction or the ad returned.
This does not enable bidding on a mediation platform
Passing .topon, .max, .admob, .tradplus, .levelplay or .pangle does not make Opera Ads bid inside that platform's auction. Opera Ads is not available as an official bidding network on any mediation platform.
If you serve Opera Ads through a mediation platform, follow the Mediation guides — Opera Ads runs in that platform's waterfall as a custom network and none of the bid token APIs on this page are involved.
Choose the value that matches the stack your own auction result is passed to, or the closest match if you run a fully custom stack.
| Value | Swift | Objective-C |
|---|---|---|
| AdMob | .admob | @"admob" |
| AppLovin MAX | .max | @"max" |
| TopOn | .topon | @"topon" |
| TradPlus | .tradplus | @"tradplus" |
| LevelPlay | .levelplay | @"levelplay" |
| Pangle | .pangle | @"pangle" |
Swift:
swift
let request = BidTokenRequest.Builder(adMediation: .admob)
.placementId("s14198264979520")
.adFormat(.interstitial)
.build()Objective-C:
objective-c
OpAdxBidTokenRequestBuilder *builder = [[OpAdxBidTokenRequestBuilder alloc]
initWithAdMediation:@"admob"];Ad Formats
Specify the ad format in your bid token request:
Swift:
swift
// Banner
let request = BidTokenRequest.Builder(adMediation: .admob)
.placementId("s14170965187264")
.adFormat(.banner)
.adSize(.BANNER_MREC) // Optional: Specify banner size
.build()
// Interstitial
let request = BidTokenRequest.Builder(adMediation: .admob)
.placementId("s14198264979520")
.adFormat(.interstitial)
.build()
// Rewarded
let request = BidTokenRequest.Builder(adMediation: .admob)
.placementId("s14198592226752")
.adFormat(.rewarded)
.build()
// Native
let request = BidTokenRequest.Builder(adMediation: .admob)
.placementId("s14198263063424")
.adFormat(.native)
.build()
// App Open
let request = BidTokenRequest.Builder(adMediation: .admob)
.placementId("s14496438551808")
.adFormat(.appOpen)
.build()Banner Sizes
For banner ads, specify the ad size:
Swift:
swift
// Medium Rectangle (300x250)
.adSize(.BANNER_MREC)
// Standard Banner (320x50)
.adSize(.BANNER)
// Large Banner (320x100)
.adSize(.LARGE_BANNER)
// Leaderboard (728x90)
.adSize(.LEADERBOARD)Complete Workflow Example
Here's a complete implementation of server bidding:
Swift:
swift
import OpAdxSdk
class CompleteServerBiddingExample {
// MARK: - Step 1: Request Bid Token
func startServerBidding(for adFormat: AdFormat, placementId: String) {
print("Step 1: Requesting bid token...")
let request = BidTokenRequest.Builder(adMediation: .admob)
.placementId(placementId)
.adFormat(adFormat)
.build()
class Callback: BidTokenCallback {
weak var manager: CompleteServerBiddingExample?
let adFormat: AdFormat
let placementId: String
init(manager: CompleteServerBiddingExample, adFormat: AdFormat, placementId: String) {
self.manager = manager
self.adFormat = adFormat
self.placementId = placementId
}
func onSuccess(bidToken: String) {
print("Bid token received")
manager?.sendToAuctionServer(
bidToken: bidToken,
adFormat: adFormat,
placementId: placementId
)
}
func onError(error: OpAdxAdError) {
print("Bid token failed: \(error.message)")
}
}
OpAdxSDK.getBidToken(request, callback: Callback(
manager: self, adFormat: adFormat, placementId: placementId
))
}
// MARK: - Step 2: Send to Auction Server
func sendToAuctionServer(bidToken: String, adFormat: AdFormat, placementId: String) {
print("Step 2: Sending to auction server...")
let requestBody: [String: Any] = [
"bid_token": bidToken,
"ad_format": adFormat.rawValue,
"placement_id": placementId
]
guard let url = URL(string: "https://your-server.com/api/auction"),
let jsonData = try? JSONSerialization.data(withJSONObject: requestBody) else {
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
guard let data = data,
error == nil,
let auctionResponse = try? JSONDecoder().decode(AuctionResponse.self, from: data) else {
print("❌ Auction request failed")
return
}
DispatchQueue.main.async {
self?.handleAuctionResponse(auctionResponse, adFormat: adFormat)
}
}.resume()
}
// MARK: - Step 3: Handle Auction Response
func handleAuctionResponse(_ response: AuctionResponse, adFormat: AdFormat) {
print("Step 3: Handling auction response...")
print("Winner: \(response.winner) with eCPM: $\(response.ecpm)")
guard response.winner == "opera",
let bidResponse = response.bidResponse else {
print("Opera Ads did not win - showing other network")
return
}
loadOperaAd(
bidResponse: bidResponse,
adFormat: adFormat,
placementId: response.placementId
)
}
// MARK: - Step 4: Load Ad with Bid Response
func loadOperaAd(bidResponse: String, adFormat: AdFormat, placementId: String) {
print("Step 4: Loading Opera ad with bid response...")
switch adFormat {
case .banner:
loadBanner(bidResponse: bidResponse, placementId: placementId)
case .interstitial:
loadInterstitial(bidResponse: bidResponse, placementId: placementId)
case .rewarded:
loadRewarded(bidResponse: bidResponse, placementId: placementId)
case .native:
loadNative(bidResponse: bidResponse, placementId: placementId)
case .appOpen:
loadAppOpen(bidResponse: bidResponse, placementId: placementId)
}
}
func loadInterstitial(bidResponse: String, placementId: String) {
let ad = OpAdxInterstitialAd(
placementId: placementId,
auctionType: .serverBidding
)
let listener = OpAdxInterstitialAdLoadListenerImp(
onAdLoaded: { [weak self] _ in
print("✅ Interstitial loaded - ready to show")
self?.showInterstitial(ad)
},
onAdFailedToLoad: { error in
print("❌ Interstitial failed: \(error.message)")
}
)
ad.loadRtb(placementId: placementId, bidResponse: bidResponse, listener: listener)
}
func showInterstitial(_ ad: OpAdxInterstitialAd) {
let listener = OpAdxInterstitialAdInteractionListenerImp(
onAdClicked: {
print("Interstitial clicked")
},
onAdDisplayed: {
print("Interstitial displayed")
},
onAdDismissed: {
print("Interstitial dismissed")
},
onAdFailedToShow: { error in
print("Failed to show: \(error.message)")
}
)
guard let viewController = UIApplication.shared.windows.first?.rootViewController else {
return
}
ad.show(on: viewController, listener: listener)
}
}
// Response model
struct AuctionResponse: Codable {
let winner: String
let bidResponse: String?
let ecpm: Double
let placementId: String
}Server-Side Implementation Guide
Auction Server Requirements
Your auction server should:
Receive Bid Token
jsonPOST /api/auction { "bid_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "ad_format": "interstitial", "placement_id": "s14198264979520", "device_id": "ABC123...", "app_version": "1.0.0" }Forward to Opera Ads Auction Endpoint
POST https://auction.opera.com/v1/bid Headers: Authorization: Bearer YOUR_API_KEY Content-Type: application/json Body: { "bid_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "floor_price": 0.50 }Receive Opera Bid Response
json{ "status": "success", "ecpm": 1.50, "bid_response": "base64_encoded_bid_response_data...", "currency": "USD" }Run Auction with Other Networks
javascriptconst bids = [ { network: "opera", ecpm: 1.50, bidResponse: "..." }, { network: "network_a", ecpm: 1.20, bidResponse: "..." }, { network: "network_b", ecpm: 1.80, bidResponse: "..." } ]; const winner = bids.sort((a, b) => b.ecpm - a.ecpm)[0];Return Winner to App
json{ "winner": "opera", "bid_response": "base64_encoded_bid_response_data...", "ecpm": 1.50, "placement_id": "s14198264979520" }
Sample Server Code (Node.js)
javascript
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
app.post('/api/auction', async (req, res) => {
const { bid_token, ad_format, placement_id } = req.body;
try {
// Step 1: Get Opera Ads bid
const operaBid = await axios.post('https://auction.opera.com/v1/bid', {
bid_token: bid_token,
floor_price: 0.50
}, {
headers: {
'Authorization': `Bearer ${process.env.OPERA_API_KEY}`,
'Content-Type': 'application/json'
}
});
// Step 2: Get bids from other networks (parallel)
const [networkA, networkB] = await Promise.all([
getNetworkABid(ad_format, placement_id),
getNetworkBBid(ad_format, placement_id)
]);
// Step 3: Run auction
const bids = [
{ network: 'opera', ecpm: operaBid.data.ecpm, bidResponse: operaBid.data.bid_response },
{ network: 'network_a', ecpm: networkA.ecpm, bidResponse: networkA.bidResponse },
{ network: 'network_b', ecpm: networkB.ecpm, bidResponse: networkB.bidResponse }
];
const winner = bids.sort((a, b) => b.ecpm - a.ecpm)[0];
// Step 4: Return winner
res.json({
winner: winner.network,
bid_response: winner.bidResponse,
ecpm: winner.ecpm,
placement_id: placement_id
});
} catch (error) {
console.error('Auction error:', error);
res.status(500).json({ error: 'Auction failed' });
}
});
app.listen(3000, () => console.log('Auction server running on port 3000'));Best Practices
1. Caching Bid Tokens
Don't cache bid tokens - request fresh tokens for each auction:
swift
// ✅ Good: Fresh token for each auction
func loadAd() {
requestBidToken { token in
self.sendToServer(token)
}
}
// ❌ Bad: Reusing old tokens
var cachedToken: String?
func loadAd() {
if let token = cachedToken {
sendToServer(token) // May be expired!
}
}2. Timeout Handling
Set reasonable timeouts for auction requests:
swift
var request = URLRequest(url: url)
request.timeoutInterval = 5.0 // 5 seconds
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error as NSError?, error.code == NSURLErrorTimedOut {
print("Auction timeout - using fallback")
self.showFallbackAd()
}
}.resume()3. Error Handling
Always handle auction failures gracefully:
swift
func handleAuctionResponse(_ response: AuctionResponse?) {
guard let response = response else {
// Auction failed - show fallback
showFallbackAd()
return
}
if response.winner == "opera" {
loadOperaAd(bidResponse: response.bidResponse)
} else {
showOtherNetworkAd()
}
}4. Thread Safety
Always update UI on main thread:
swift
URLSession.shared.dataTask(with: request) { data, response, error in
// Parse response on background thread
let auctionResponse = parseResponse(data)
// Update UI on main thread
DispatchQueue.main.async {
self.handleAuctionResponse(auctionResponse)
}
}.resume()Troubleshooting
Issue: Bid Token Request Fails
Cause: SDK not initialized or invalid parameters
Solution:
swift
// Ensure SDK is initialized first
OpAdxSDK.initialize(withConfig: config) {
// Now request bid token
self.requestBidToken()
}
// Verify request parameters
let request = BidTokenRequest.Builder(adMediation: .admob)
.placementId("s14198264979520") // Valid placement ID
.adFormat(.interstitial) // Valid ad format
.build()Issue: Bid Response Loading Fails
Cause: Invalid bid response or expired token
Solution:
- Don't cache bid responses
- Request fresh bid token for each auction
- Check bid response format from server
- Verify placement ID matches
Issue: Low Fill Rate
Cause: Floor price too high or limited demand
Solution:
- Lower floor price on server
- Check bid token includes correct signals
- Verify placement ID is active
- Test with different geos
Support
Document Version: 2.12.0 Last Updated: 2026-08-03 SDK Version: 2.12.0
