BiddingStack Android SDK Integration Guide
BiddingStack is a lightweight header bidding SDK for Android built on OpenRTB 2.5. It enables publishers to fetch real-time bids and integrate them with Google Ad Manager (GAM) or render them directly. View Changelog.
| Component | Minimum Version |
|---|---|
| Android Min SDK | 23 (Android 6.0) |
| Compile SDK | 35 |
| Java | 17 |
| Kotlin | 2.1.0+ |
Add the biddingstack-core-release.aar and biddingstack-gam-adapter-release.aar to your project.
dependencies {
implementation fileTree(dir: 'libs', include: ['*.aar'])
implementation 'com.google.android.gms:play-services-ads:25.3.0'
implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.39.0'
implementation "androidx.media3:media3-exoplayer:1.9.4"
implementation "androidx.media3:media3-ui:1.9.4"
implementation "androidx.media3:media3-exoplayer-ima:1.9.4"
implementation 'androidx.core:core-ktx:1.12.0'
}
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application ...>
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-XXXXXXXXXXXXXXXX~XXXXXXXXXX"/>
</application>
import com.biddingstack.core.BiddingStack
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
BiddingStack.shared.initialize(
context = this,
accountId = "YOUR_ACCOUNT_ID",
enableTestMode = true
) {
// SDK initialized, config loaded
}
}
}
| Parameter | Type | Default | Description |
|---|---|---|---|
context | Context | required | Application context |
accountId | String | required | Your BiddingStack Account ID |
timeout | Int | 2000 | Bid request timeout in ms |
enableTestMode | Boolean | false | Enable debug logging and test mode |
enableCMP | Boolean | true | Auto-read IAB CMP strings from SharedPreferences |
completion | (() -> Unit)? | null | Called after remote config is loaded |
Note: App bundle, name, store URL, and GAM ad unit paths are resolved automatically from the remote config.
// User data
BiddingStack.shared.userId = "user_123"
BiddingStack.shared.userGender = "M"
BiddingStack.shared.userYob = 1990
// Privacy
BiddingStack.shared.coppa = false
BiddingStack.shared.gdprConsentString = "TCF_consent_string"
BiddingStack.shared.gdprApplies = true
BiddingStack.shared.usPrivacyString = "1YNN"
BiddingStack.shared.gppString = "gpp_string"
BiddingStack.shared.gppSid = listOf(2)
// App metadata
BiddingStack.shared.appStoreUrl = "https://play.google.com/store/apps/details?id=com.example"
BiddingStack.shared.appCategories = listOf("IAB1", "IAB2")
// Supply chain
BiddingStack.shared.schainObject = "{...}"
BiddingStack.shared.externalUserIds = mapOf("partner" to "uid_abc")
BiddingStack.shared.buyerUid = "buyer_123"
BiddingStack.shared.userLatLng = Pair(37.7749, -122.4194)
Pass the web URL of the content the user is currently viewing so it is included in every bid request as app.content.url. This is important for brand safety and yield: it gives bidders page-level context to verify where their ads run, which increases bid rates and CPMs. Set it before loading ads and update it on each screen change; set null to clear.
BiddingStack.shared.contentUrl = "https://www.example.com/article/12345"
Available since SDK 26.7.1.
val bannerAdUnit = BiddingStackBannerAdUnit(
configId = "YOUR_CONFIG_ID",
gamAdUnitId = "YOUR_GAM_AD_UNIT_ID", // optional fallback if config API is unavailable
adSizes = listOf(BiddingStackAdSize(300, 250))
)
BiddingStack.shared.fetchDemand(bannerAdUnit) { result ->
runOnUiThread {
val builder = AdManagerAdRequest.Builder()
result.onSuccess { keywords ->
BiddingStackGAMUtils.apply(keywords, builder)
}
val gamView = AdManagerAdView(context)
gamView.adUnitId = bannerAdUnit.resolvedGamAdUnitId
gamView.setAdSizes(AdSize.MEDIUM_RECTANGLE)
gamView.loadAd(builder.build())
}
}
val interstitialAdUnit = BiddingStackInterstitialAdUnit(
configId = "YOUR_CONFIG_ID"
)
BiddingStack.shared.fetchDemand(interstitialAdUnit) { result ->
runOnUiThread {
val builder = AdManagerAdRequest.Builder()
result.onSuccess { keywords ->
BiddingStackGAMUtils.apply(keywords, builder)
}
AdManagerInterstitialAd.load(
context, interstitialAdUnit.resolvedGamAdUnitId,
builder.build(),
object : AdManagerInterstitialAdLoadCallback() {
override fun onAdLoaded(ad: AdManagerInterstitialAd) {
ad.show(activity)
}
}
)
}
}
App open ads show at natural app-entry points, such as launch or returning from the background. Preload the ad with BiddingStackInterstitialBanner, then show it when the app becomes active.
object AppOpenAdManager : BiddingStackInterstitialDelegate {
private var interstitial: BiddingStackInterstitialBanner? = null
private var isLoading = false
private var appContext: Context? = null
fun loadAd(context: Context) {
appContext = context.applicationContext
if (isLoading || interstitial?.isAdReady() == true) return
isLoading = true
// Google interstitial size: 320x480 phone, 768x1024 tablet
val isTablet = context.resources.configuration.smallestScreenWidthDp >= 600
val interstitialSize = if (isTablet) BiddingStackAdSize(768, 1024) else BiddingStackAdSize(320, 480)
val adUnit = BiddingStackBannerAdUnit(
configId = "YOUR_CONFIG_ID",
gamAdUnitId = "YOUR_GAM_AD_UNIT_ID",
adSizes = listOf(interstitialSize, BiddingStackAdSize(300, 250)),
autoRefreshEnabled = false
)
val banner = BiddingStackInterstitialBanner(context, adUnit)
banner.delegate = this
banner.setAdServerProvider(GAMBannerProvider(context, adUnit.resolvedGamAdUnitId, adUnit.adSizes))
interstitial = banner
banner.loadAd()
}
fun showAdIfAvailable(activity: Activity) {
val banner = interstitial
if (banner == null || !banner.isAdReady()) {
loadAd(activity)
return
}
banner.show(activity)
}
override fun interstitialDidReceiveAd() { isLoading = false }
override fun interstitialDidFailToReceiveAd(error: Exception) {
isLoading = false
interstitial = null
}
override fun interstitialWillPresent() {}
override fun interstitialDidDismiss() {
interstitial = null
appContext?.let { loadAd(it) } // Preload the next one
}
}
Show it when the app comes to the foreground, e.g. in your main activity:
override fun onStart() {
super.onStart()
AppOpenAdManager.showAdIfAvailable(this)
}
val rewardedAdUnit = BiddingStackRewardedAdUnit(
configId = "YOUR_CONFIG_ID"
)
BiddingStack.shared.fetchDemand(rewardedAdUnit) { result ->
val builder = AdManagerAdRequest.Builder()
result.onSuccess { keywords -> BiddingStackGAMUtils.apply(keywords, builder) }
RewardedAd.load(context, rewardedAdUnit.resolvedGamAdUnitId,
builder.build(), object : RewardedAdLoadCallback() {
override fun onAdLoaded(ad: RewardedAd) {
ad.show(activity) { reward -> /* Grant reward */ }
}
})
}
val nativeAdUnit = BiddingStackNativeAdUnit(
configId = "YOUR_CONFIG_ID"
)
BiddingStack.shared.loadNativeAd(nativeAdUnit) { ad, error ->
if (ad != null) {
myTitleTextView.text = ad.title
myBodyTextView.text = ad.body
ad.registerView(myContainer, listOf(myCtaButton))
}
}
| Property | Type | Description |
|---|---|---|
title | String? | Ad headline |
body | String? | Ad body text |
callToAction | String? | CTA button text |
sponsoredBy | String? | Sponsor/advertiser name |
iconUrl | String? | Icon image URL |
mainImageUrl | String? | Main image URL |
clickUrl | String? | Click-through URL |
val videoAdUnit = BiddingStackBannerAdUnit(
configId = "VIDEO_CONFIG_ID",
adSizes = listOf(BiddingStackAdSize(300, 250)),
formats = listOf(BiddingStackFormat.VIDEO)
)
val videoView = BiddingStackBannerView(context)
videoView.adUnit = videoAdUnit
videoView.videoRenderer = BiddingStackIMAVideoRenderer(context)
videoView.loadAd()
BiddingStack.shared.preloadBid(bannerAdUnit)
// Later, fetchDemand returns the cached bid if valid (30s TTL)
BiddingStack.shared.addTargetingKV("interest", "tech")
BiddingStack.shared.addTargetingKV("interest", listOf("tech", "gaming"))
BiddingStack.shared.removeTargetingKey("interest")
BiddingStack.shared.clearTargetingKV()
// Per ad unit
bannerAdUnit.addContextKeyword("section", "sports")
val deal = BiddingStackDeal(id = "deal_123", bidfloor = 5.0, bidfloorcur = "USD")
bannerAdUnit.deals = listOf(deal)
The SDK automatically reads IAB consent strings from SharedPreferences:
IABTCF_TCStringIABUSPrivacy_StringIABGPP_HDR_GppStringBiddingStack.shared.initialize(..., enableTestMode = true)
// Or: BSLogger.isEnabled = true
Debug logs can be enabled at runtime on any device — including production/release builds — without code changes:
# Lifecycle, auction results, and GAM targeting
adb shell setprop log.tag.BiddingStack DEBUG
# Additionally dumps full bid request/response JSON
adb shell setprop log.tag.BiddingStack VERBOSE
# View logs
adb logcat -s BiddingStack
The property takes effect immediately and clears on device reboot.