Skip to main content
Version: 4.4.0

Integration Guide

Complete guide to integrating the BidMachine Plus Android SDK.

BidMachine Plus Android SDK​

BidMachine Plus exposes two demand paths through one API:

ModeDescription
AdNetworkBidMachine runs as a header-bidding demand source — on its own, or plugged into a third-party mediation
MediationBidMachine acts as the mediation platform itself, running the auction across your demand

The same ad unit classes — BannerAd, InterstitialAd, RewardedAd — work in both modes. The active mode is selected at initialization.

This page covers SDK installation, initialization, and ad loading. Two installation paths are available: automated via AI coding agents, or manual Gradle setup.

Automated Integration with AI Coding Agents​

The BidMachine Plus SDK Agents bundle ships a portable integration skill for Claude Code, Codex, and Gemini. Install it for your runtime:

/plugin marketplace add bidmachine/bidmachine-sdk-agents
/plugin install bidmachine-sdk-agents@bidmachine

Then ask your agent to integrate BidMachine Plus — for example:

Prompt
Integrate BidMachine Plus into my Android app with interstitial, rewarded, and banner ads.

The bundled skill drives the integration: it adds the SDK dependency, initializes in AdNetwork or Mediation mode, wires ad units, and sets privacy flags (GDPR, CCPA), preserving any existing ad setup.

Manual Installation​

Requires Android SDK 23+, Gradle 8.7+, Android Gradle Plugin 8.6+, Kotlin 2.1+, and Java 17 source/target compatibility.

Add the BidMachine Maven repository and the SDK dependency to your app-level build.gradle:

app/build.gradle
repositories {
maven { url "https://artifactory.bidmachine.io/bidmachine" }
}

dependencies {
implementation "io.bidmachine.plus:sdk:0.1.0" // BidMachine Plus SDK
implementation "com.google.android.gms:play-services-ads-identifier:18.2.0" // required for advertising ID
}

Network Security Configuration​

The SDK needs network access — add the INTERNET permission and reference a network security config in AndroidManifest.xml:

AndroidManifest.xml
<manifest>
<uses-permission android:name="android.permission.INTERNET" />
<application android:networkSecurityConfig="@xml/network_security_config" />
</manifest>

Create res/xml/network_security_config.xml:

res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<debug-overrides>
<trust-anchors>
<certificates src="user" />
</trust-anchors>
</debug-overrides>
</network-security-config>

Initialization​

Call BidMachine.initialize once at app startup. Pass the desired mode to select the demand path.

BidMachine.instance returns the SDK instance — keep a reference; you'll pass it to every ad unit constructor.

BidMachine Plus initializes in one of two modes, selected by the IntegrationType you pass to the builder:

AdNetwork Mode​

BidMachine runs as a header-bidding ad network. When it plugs into a third-party mediation, name that platform with withMediator so it can compete in the waterfall.

val sdk = BidMachine.instance(context, "YOUR_APP_KEY")

val config = InitializationConfigBuilder(IntegrationType.AdNetwork)
.withMediator(MediatorName.ADMOB) // the mediation BidMachine plugs into — or LEVEL_PLAY, MAX, a custom String
.withLoggingEnabled(true) // remove before release
.withTestModeEnabled(true) // remove before release
.build()

sdk.initialize(config) { status, error ->
if (error != null) Log.e("BidMachine", "init failed: ${error.message}")
}

Mediation Mode​

BidMachine acts as the mediation platform itself, running the auction across your demand. Pass IntegrationType.Mediation — there's no third-party mediator to declare:

val config = InitializationConfigBuilder(IntegrationType.Mediation)
.withLoggingEnabled(true) // remove before release
.withTestModeEnabled(true) // remove before release
.build()

BidMachine.instance(context, "YOUR_APP_KEY").initialize(config) { status, error -> }

In both modes the ad unit classes expose notifyWin() and notifyLoss(winnerEcpm, networkName) for reporting the outcome of a waterfall round — see Mediation.

Running the auction on your own server instead? See S2S Bidding for the bid-token flow and loading with a bidPayload.

Ad Integration​

All ad lifecycle callbacks deliver an AdInfo describing the winning ad:

PropertyTypeDescription
placementIdStringPlacement ID from the BidMachine dashboard
priceDoubleeCPM ÷ 1000 (e.g. 0.005 = $5 CPM)
precisionRevenuePrecisionOne of Estimated, Exact, Unknown
infoMap<String, String>Network metadata. Known keys: networkName, dsp, ecpm

Read the winning network via adInfo.info["networkName"], and the per-impression revenue via adInfo.price.

adInfo.precision describes how reliable that price is:

ValueMeaning
ExactReal-time auction price — trust for reporting
EstimatedHistorical-data estimate — treat as approximate
UnknownConfidence cannot be determined

All callbacks fire on the main thread, so you can update UI from them directly.

Interstitial Ads​

Full-screen ads. Create once, load, then call show when ready. Reload from onAdClosed to keep an ad ready for the next show.

MainActivity.kt
class MainActivity : AppCompatActivity(), InterstitialListener {
private lateinit var interstitialAd: InterstitialAd

private fun createInterstitialAd() {
interstitialAd = InterstitialAd(sdk, placementId = "YOUR_PLACEMENT_ID")
interstitialAd.listener = this
interstitialAd.load(this)
}

private fun showInterstitialAd() {
if (interstitialAd.isLoaded) {
interstitialAd.show(this)
} else {
Log.w("BidMachine", "Interstitial ad not ready yet")
}
}

override fun onDestroy() {
super.onDestroy()
interstitialAd.destroy()
}

// InterstitialListener callbacks
override fun onAdLoaded(adInfo: AdInfo) {
Log.d("BidMachine", "Interstitial loaded from ${adInfo.info["networkName"]}")
}

override fun onAdLoadFailed(adInfo: AdInfo?, error: BidMachineError) {
Log.e("BidMachine", "Interstitial load failed: ${error.message}")
}

override fun onAdShown(adInfo: AdInfo) {
Log.d("BidMachine", "Interstitial displayed")
}

override fun onAdShowFailed(adInfo: AdInfo?, error: BidMachineError) {
Log.e("BidMachine", "Interstitial show failed: ${error.message}")
}

override fun onAdClicked(adInfo: AdInfo) {
Log.d("BidMachine", "Interstitial clicked")
}

override fun onAdClosed(adInfo: AdInfo) {
Log.d("BidMachine", "Interstitial closed")
interstitialAd.load(this) // reload for next show
}

override fun onAdExpired(adInfo: AdInfo) {
Log.w("BidMachine", "Interstitial expired before show")
}

override fun onAdRevenuePaid(adInfo: AdInfo) {
Log.d("BidMachine", "Interstitial revenue: ${adInfo.price} from ${adInfo.info["networkName"]}")
}
}
CallbackDescription
onAdLoadedAd loaded and ready to show
onAdLoadFailedAd failed to load
onAdShownAd is displayed full-screen
onAdShowFailedAd failed to display
onAdClickedUser tapped the ad
onAdClosedUser dismissed the ad
onAdExpiredAd expired before being shown
onAdRevenuePaidBillable impression recorded

Rewarded Ads​

Same lifecycle as interstitial, plus an onAdRewarded callback when the user completes the ad and earns a reward.

MainActivity.kt
class MainActivity : AppCompatActivity(), RewardedListener {
private lateinit var rewardedAd: RewardedAd

private fun createRewardedAd() {
rewardedAd = RewardedAd(sdk, placementId = "YOUR_PLACEMENT_ID")
rewardedAd.listener = this
rewardedAd.load(this)
}

private fun showRewardedAd() {
if (rewardedAd.isLoaded) {
rewardedAd.show(this)
} else {
Log.w("BidMachine", "Rewarded ad not ready yet")
}
}

override fun onDestroy() {
super.onDestroy()
rewardedAd.destroy()
}

// RewardedListener callbacks
override fun onAdLoaded(adInfo: AdInfo) {
Log.d("BidMachine", "Rewarded loaded from ${adInfo.info["networkName"]}")
}

override fun onAdRewarded(adInfo: AdInfo, reward: Reward?) {
Log.d("BidMachine", "Reward earned")
// grant reward to the user
}

override fun onAdLoadFailed(adInfo: AdInfo?, error: BidMachineError) {
Log.e("BidMachine", "Rewarded load failed: ${error.message}")
}

override fun onAdShown(adInfo: AdInfo) {}
override fun onAdShowFailed(adInfo: AdInfo?, error: BidMachineError) {}
override fun onAdClicked(adInfo: AdInfo) {}

override fun onAdClosed(adInfo: AdInfo) {
rewardedAd.load(this) // reload for next show
}

override fun onAdExpired(adInfo: AdInfo) {}

override fun onAdRevenuePaid(adInfo: AdInfo) {
Log.d("BidMachine", "Rewarded revenue: ${adInfo.price} from ${adInfo.info["networkName"]}")
}
}
CallbackDescription
onAdLoadedAd loaded and ready to show
onAdRewardedUser completed the ad, grant reward
onAdLoadFailedAd failed to load
onAdShownAd is displayed full-screen
onAdShowFailedAd failed to display
onAdClickedUser tapped the ad
onAdClosedUser dismissed the ad
onAdExpiredAd expired before being shown
onAdRevenuePaidBillable impression recorded

The Reward object passed to onAdRewarded exposes label: String and amount: Int.

BannerAd extends ViewGroup. Attach it to your own layout, or call show(position) to display it at a fixed screen position.

SizeDimensionsUse case
Banner320 × 50Standard banner
Leaderboard728 × 90Tablets
MREC300 × 250Medium rectangle
BannerAdSize.adaptive(width, maxHeight)customFluid layout
tip

Use BannerAdSize.getMaxAdaptiveHeight(width) to calculate maxHeight for adaptive banners.

Manual Banner​

Place a container in your layout that will host the banner view:

res/layout/activity_main.xml
<FrameLayout
android:id="@+id/ad_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom" />

Resolve it once in onCreate, then attach the loaded BannerAd to it inside onAdLoaded:

MainActivity.kt
class MainActivity : AppCompatActivity(), BannerListener {
private lateinit var bannerAd: BannerAd
private lateinit var adContainer: FrameLayout

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
adContainer = findViewById(R.id.ad_container)
}

private fun createBannerAd() {
bannerAd = BannerAd(this, sdk, placementId = "YOUR_PLACEMENT_ID", size = BannerAdSize.Banner)
bannerAd.listener = this
bannerAd.load(this)
}

override fun onDestroy() {
super.onDestroy()
bannerAd.destroy()
}

// BannerListener callbacks
override fun onAdLoaded(adInfo: AdInfo) {
Log.d("BidMachine", "Banner loaded from ${adInfo.info["networkName"]}")
adContainer.addView(bannerAd)
}

override fun onAdLoadFailed(adInfo: AdInfo?, error: BidMachineError) {
Log.e("BidMachine", "Banner load failed: ${error.message}")
}

override fun onAdShown(adInfo: AdInfo) {}
override fun onAdShowFailed(adInfo: AdInfo?, error: BidMachineError) {}
override fun onAdClicked(adInfo: AdInfo) {}
override fun onAdExpired(adInfo: AdInfo) {}

override fun onAdRevenuePaid(adInfo: AdInfo) {
Log.d("BidMachine", "Banner revenue: ${adInfo.price} from ${adInfo.info["networkName"]}")
}
}
CallbackDescription
onAdLoadedAd loaded and ready to attach to a container
onAdLoadFailedAd failed to load
onAdShownAd visible on screen, impression tracked
onAdShowFailedAd failed to display
onAdClickedUser tapped the ad
onAdExpiredAd expired before being shown
onAdRevenuePaidBillable impression recorded

Positioned Banner​

Call show(position) on a loaded BannerAd to display it at one of the positions below. Call hide() to remove it from the screen.

PositionConstant
TopBannerPosition.HorizontalTop
BottomBannerPosition.HorizontalBottom
LeftBannerPosition.VerticalLeft
RightBannerPosition.VerticalRight
val bannerAd = BannerAd(this, sdk, placementId = "YOUR_PLACEMENT_ID", size = BannerAdSize.Banner)
bannerAd.listener = object : BannerListener {
override fun onAdLoaded(adInfo: AdInfo) {
bannerAd.show(BannerPosition.HorizontalBottom) // default position
}
override fun onAdLoadFailed(adInfo: AdInfo?, error: BidMachineError) {}
override fun onAdShown(adInfo: AdInfo) {}
override fun onAdShowFailed(adInfo: AdInfo?, error: BidMachineError) {}
override fun onAdClicked(adInfo: AdInfo) {}
override fun onAdExpired(adInfo: AdInfo) {}
override fun onAdRevenuePaid(adInfo: AdInfo) {}
}
bannerAd.load(this)

// Later:
bannerAd.hide() // remove the overlay; show(position) can re-display it
bannerAd.destroy()

Publisher Extras​

Attach arbitrary key → value pairs forwarded with the bid request as publisher extras. The API exists at two levels with the same shape — choose the one that matches the scope you need.

ScopeWhere to callApplies to
SDK-widesdk.addExtraEvery auction from this instance
Per ad unit<AdUnit>.addExtraOnly that placement's auctions

Passing null as the value removes the key. Per-ad-unit extras override SDK-wide values for the same key on that placement.

SDK-wide​

val sdk = BidMachine.instance(context, "YOUR_APP_KEY")

sdk.addExtra("user_segment", "whale")
sdk.addExtra("ab_bucket", "control")

val all: Map<String, String> = sdk.extras
sdk.addExtra("ab_bucket", null) // remove

Per ad unit​

Available on BannerAd, InterstitialAd, and RewardedAd:

interstitialAd.addExtra("placement_context", "level_complete")
bannerAd.addExtra("screen", "main_menu")

val extras: Map<String, String> = interstitialAd.extras