This guide covers installation, initialization, ad formats, privacy, testing, and mediated networks. The complete Lua API is documented in the API reference.
If you are upgrading from 1.x, start with the migration guide.
Other native extensions can subscribe directly to the MAX callbacks without replacing the AppLovin listeners. Include this extension as a dependency and register before loading ads. Both callbacks run synchronously on the platform’s UI thread, so keep handlers brief. Display events cover interstitial and rewarded ads; revenue events cover banner, leader, MREC, interstitial, and rewarded ads. The existing Lua/Defold callbacks continue to fire. If an analytics extension reports an impression through this native listener, avoid reporting the same impression again from Lua.
On Android, implement com.defold.applovin.MaxAdEventBus.Listener and use
MaxAdEventBus.addListener(listener) / removeListener(listener). Each method
receives the SDK’s com.applovin.mediation.MaxAd, including revenue, ad unit,
network, and placement data. The bus retains Java listeners until removed.
On iOS, import MADefoldAdEvents.h from extension-applovin/include, implement
MADefoldAdEventListener, and use [MADefoldAdEvents addListener:listener] /
removeListener:. Implement onMaxAdDisplayed: and/or
onMaxAdRevenuePaid:. Each receives the SDK’s MAAd. The registry holds iOS
listeners weakly, so the subscribing extension must retain its listener.
| Component | Minimum |
|---|---|
| Defold | 1.13.0 |
| Android | API 24 |
| iOS | iOS 15 |
| AppLovin MAX | 13.6.4 |
Open game.project in the Defold editor and select Project in the left
sidebar. Under Dependencies, click + and paste:
https://github.com/defold/extension-applovin/archive/refs/tags/2.0.0.zip
Then select Project > Fetch Libraries from the main menu.
In the game.project editor, also configure:
24Register your Android package and iOS bundle identifier in MAX. Use the SDK key from Account > General > Keys, and create separate ad units for each platform and format.
The extension is not available on desktop or HTML5. Guard shared code:
if applovin then
-- Android or iOS
end
MAX initializes asynchronously:
initialize() once.OnSdkInitializedEvent.local interstitial_id = "YOUR_INTERSTITIAL_AD_UNIT_ID"
local function max_callback(self, event, data)
if event == "OnSdkInitializedEvent" then
applovin.load_interstitial(interstitial_id)
elseif event == "OnInterstitialAdLoadFailedEvent" then
print("MAX load failed", data.code, data.message)
elseif event == "OnInterstitialAdRevenuePaidEvent" then
print("MAX revenue", data.revenue, data.revenuePrecision)
end
end
function init(self)
applovin.set_callback(max_callback)
-- Values supplied by your consent UI or CMP:
applovin.set_has_user_consent(user_has_consented)
applovin.set_do_not_sell(user_opted_out)
applovin.initialize("YOUR_APPLOVIN_SDK_KEY")
end
function final(self)
applovin.set_callback(nil)
end
is_initialized() becomes true after initialization completes. Handle
OnSdkInitializedEvent when loading the first ads instead of polling it.
Your application is responsible for choosing the correct disclosures and
consent flow. Apply set_has_user_consent() and set_do_not_sell() before
initialization and persist the user’s choice in your own system. Their getters
return booleans and cannot distinguish “unset” from false.
Follow AppLovin’s current privacy guide. Do not initialize AppLovin for users or apps prohibited by that policy.
The MAX Terms and Privacy Policy Flow is optional. Before enabling it:
Configure the flow before initialization:
applovin.set_terms_and_privacy_policy_flow_enabled(true)
applovin.set_privacy_policy_url("https://example.com/privacy")
applovin.set_terms_of_service_url("https://example.com/terms") -- optional
applovin.initialize("YOUR_APPLOVIN_SDK_KEY")
Android builds include Google UMP 4.0.0. If another extension also declares UMP, do not force an older version. On iOS, enable the appropriate Google adapter and app ID when required by your consent setup.
After initialization, has_supported_cmp() tells you whether
show_cmp_for_existing_user() can present a Manage privacy settings
screen. Completion is reported through OnCmpCompletedEvent.
After the consent flow completes, read the current stored parameters on Android or iOS:
local parameters = applovin.get_ump_dma_parameters()
-- parameters.adPersonalization: boolean
-- parameters.adUserData: boolean
-- parameters.adjustConsent: boolean
The function reads IABTCF_AddtlConsent and IABTCF_PurposeConsents from
Android’s default shared preferences or iOS’s standard user defaults:
adjustConsent is true when Adjust’s ATP ID 2822 is in the Additional
Consent consented-provider list. A disclosed provider alone does not count.adUserData requires Adjust consent and TCF purpose 1.adPersonalization requires Adjust consent and TCF purposes 1, 2, and 4.Missing or unreadable values default to false. Without Adjust consent, all
three fields are false. These are the Adjust-specific DMA parameters, not a
general replacement for has_user_consent() or a signal that ads can load.
Call again after consent changes, such as a successful OnCmpCompletedEvent.
The function reads stored values; it does not show a form or forward consent
to another SDK. Additional Consent parsing follows Google’s
format specification.
Review the final application—not only the base extension—for permissions, privacy declarations, and mediated SDK data collection:
The extension includes AppLovin’s current list automatically. Enabling an iOS adapter also includes that network’s current identifiers. App-specific entries from another manifest are merged with these lists.
Set test-device IDs before initialization:
applovin.set_test_device_advertising_ids({
"YOUR_IDFA_OR_GAID",
})
Use only identifiers from your QA devices and remove them from production configuration.
After initialization, call show_mediation_debugger() from a visible QA
button. Use it to verify the SDK key, adapters, privacy state, test mode, CMP,
and ad units. Enable verbose logging and the Creative Debugger only in
development builds.
All ad calls require an ad-unit ID for the current package or bundle identifier.
applovin.load_interstitial(ad_unit_id)
-- After OnInterstitialAdLoadedEvent:
if applovin.is_interstitial_ready(ad_unit_id) then
applovin.show_interstitial(ad_unit_id, "level_complete")
end
Load the next ad after the previous one is hidden or fails.
applovin.load_rewarded_ad(ad_unit_id)
-- After OnRewardedAdLoadedEvent:
if applovin.is_rewarded_ad_ready(ad_unit_id) then
applovin.show_rewarded_ad(ad_unit_id, "daily_reward")
end
Grant rewards only from OnRewardedAdReceivedRewardEvent, using its label
and amount.
applovin.set_banner_placement(ad_unit_id, "main_menu")
applovin.create_banner(ad_unit_id, "bottom_center")
applovin.set_banner_background_color(ad_unit_id, "#000000")
applovin.hide_banner(ad_unit_id)
applovin.show_banner(ad_unit_id)
applovin.update_banner_position(ad_unit_id, "top_center")
applovin.stop_banner_auto_refresh(ad_unit_id)
applovin.start_banner_auto_refresh(ad_unit_id)
applovin.destroy_banner(ad_unit_id)
Set placement before creation so it is included in the first load. Destroy the view when its screen no longer needs it.
applovin.set_mrec_placement(ad_unit_id, "store")
applovin.create_mrec(ad_unit_id, "centered")
applovin.hide_mrec(ad_unit_id)
applovin.show_mrec(ad_unit_id)
applovin.update_mrec_position(ad_unit_id, "bottom_center")
applovin.stop_mrec_auto_refresh(ad_unit_id)
applovin.start_mrec_auto_refresh(ad_unit_id)
applovin.destroy_mrec(ad_unit_id)
MREC event names use MRec, including OnMRecAdExpandedEvent.
Each format has its own extra-parameter function:
applovin.set_interstitial_extra_parameter(ad_unit_id, key, value)
applovin.set_rewarded_ad_extra_parameter(ad_unit_id, key, value)
applovin.set_banner_extra_parameter(ad_unit_id, key, value)
applovin.set_mrec_extra_parameter(ad_unit_id, key, value)
Only use keys documented by AppLovin or the mediated network.
The callback signature is:
function(self, event_name, params)
Callbacks run from the Defold update thread. Keep them short. The complete event and constant list is in the API reference.
Ad payloads can include:
adUnitIdentifier, format, placementnetworkName, networkPlacement, creativeIdentifierrevenue, revenuePrecisionrequestLatencyMillisdspName, dspIdentifierSome mediated networks omit fields they cannot provide. Load failures contain
code, message, and request latency. Display failures can also contain
mediatedNetworkErrorCode and mediatedNetworkErrorMessage. See the official
error reference.
The base extension includes MAX but no optional third-party adapters. Enable only the networks used by your MAX account:
[applovin]
meta_android = 1
meta_ios = 1
Android and iOS switches are independent. Available property names and pinned
versions are listed in
updater/adapters.json.
MAX 13.6.4 removed VK Ad Network (MyTarget) adapter detection on both
platforms. The legacy vk_android and vk_ios switches retain their last
published adapter versions for existing projects; do not enable them for new
integrations.
Google also requires an application ID:
[applovin]
google_android = 1
google_android_app_id = ca-app-pub-0000000000000000~0000000000
google_ios = 1
google_ios_app_id = ca-app-pub-0000000000000000~0000000000
Follow the official mediated-network guide for account setup, permissions, manifests, privacy disclosures, and any extra initialization. Re-run the Mediation Debugger after changing adapters.
track_event() takes a non-empty event name and a JSON object:
applovin.track_event("level_complete", json.encode({
level = 12,
difficulty = "hard",
}))
Invalid JSON is logged and sent as an empty parameter map.
The repository example includes a demo SDK key:
applovin.demo_sdk_key in the bundled
game.projectc.is_*_ready()
immediately before showing.For repository builds and releases, see DEVELOPMENT.md.