Integration Guide
Complete guide to integrating the BidMachine Plus Android SDK.
BidMachine Plus Android SDK
BidMachine Plus exposes two demand paths through one API:
| Mode | Description |
|---|---|
| AdNetwork | BidMachine runs as a header-bidding demand source — on its own, or plugged into a third-party mediation |
| Mediation | BidMachine 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:
- Claude Code
- Gemini CLI
- npx (any agent)
/plugin marketplace add bidmachine/bidmachine-sdk-agents
/plugin install bidmachine-sdk-agents@bidmachine
gemini extensions install https://github.com/bidmachine/bidmachine-sdk-agents
npx skills add bidmachine/bidmachine-sdk-agents
Then ask your agent to integrate BidMachine Plus — for example:
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:
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:
<manifest>
<uses-permission android:name="android.permission.INTERNET" />
<application android:networkSecurityConfig="@xml/network_security_config" />
</manifest>
Create 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:
| Property | Type | Description |
|---|---|---|
placementId | String | Placement ID from the BidMachine dashboard |
price | Double | eCPM ÷ 1000 (e.g. 0.005 = $5 CPM) |
precision | RevenuePrecision | One of Estimated, Exact, Unknown |
info | Map<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:
| Value | Meaning |
|---|---|
Exact | Real-time auction price — trust for reporting |
Estimated | Historical-data estimate — treat as approximate |
Unknown | Confidence 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.
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"]}")
}
}
| Callback | Description |
|---|---|
onAdLoaded | Ad loaded and ready to show |
onAdLoadFailed | Ad failed to load |
onAdShown | Ad is displayed full-screen |
onAdShowFailed | Ad failed to display |
onAdClicked | User tapped the ad |
onAdClosed | User dismissed the ad |
onAdExpired | Ad expired before being shown |
onAdRevenuePaid | Billable impression recorded |
Rewarded Ads
Same lifecycle as interstitial, plus an onAdRewarded callback when the user completes the ad and earns a reward.
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"]}")
}
}
| Callback | Description |
|---|---|
onAdLoaded | Ad loaded and ready to show |
onAdRewarded | User completed the ad, grant reward |
onAdLoadFailed | Ad failed to load |
onAdShown | Ad is displayed full-screen |
onAdShowFailed | Ad failed to display |
onAdClicked | User tapped the ad |
onAdClosed | User dismissed the ad |
onAdExpired | Ad expired before being shown |
onAdRevenuePaid | Billable impression recorded |
The Reward object passed to onAdRewarded exposes label: String and amount: Int.
Banner Ads
BannerAd extends ViewGroup. Attach it to your own layout, or call show(position) to display it at a fixed screen position.
| Size | Dimensions | Use case |
|---|---|---|
Banner | 320 × 50 | Standard banner |
Leaderboard | 728 × 90 | Tablets |
MREC | 300 × 250 | Medium rectangle |
BannerAdSize.adaptive(width, maxHeight) | custom | Fluid layout |
Use BannerAdSize.getMaxAdaptiveHeight(width) to calculate maxHeight for adaptive banners.
Manual Banner
Place a container in your layout that will host the banner view:
<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:
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"]}")
}
}
| Callback | Description |
|---|---|
onAdLoaded | Ad loaded and ready to attach to a container |
onAdLoadFailed | Ad failed to load |
onAdShown | Ad visible on screen, impression tracked |
onAdShowFailed | Ad failed to display |
onAdClicked | User tapped the ad |
onAdExpired | Ad expired before being shown |
onAdRevenuePaid | Billable 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.
| Position | Constant |
|---|---|
| Top | BannerPosition.HorizontalTop |
| Bottom | BannerPosition.HorizontalBottom |
| Left | BannerPosition.VerticalLeft |
| Right | BannerPosition.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.
| Scope | Where to call | Applies to |
|---|---|---|
| SDK-wide | sdk.addExtra | Every auction from this instance |
| Per ad unit | <AdUnit>.addExtra | Only 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