MAX

Setup in MAX

Set up multiple Ad Units per Ad Format in the MAX dashboard. one default ad unit, per ad format (floor price set to zero), and as many as possible additional ad units with a floor price set. These floor prices are determined and provided to you as described in the introduction.

Requesting floor price insights

Via the SDK, you can request specific user floor price insight key-values:

  • User value bid floor price: , calculated_user_floor_price_interstitial, calculated_user_floor_price_rewarded and calculated_user_floor_price_banner.
  • Recommended MAX AdUnit Id: recommended_interstitial_ad_unit_id, recommended_rewarded_ad_unit_id andrecommended_banner_ad_unit_id
NeftaPlugin._instance.GetBehaviourInsight(
  ["recommended_rewarded_ad_unit_id", "calculated_user_floor_price_rewarded"],
  callback: { (insights: [String: Insight]) in
    _recommendedAdUnitId = nil
    _calculatedBidFloor = 0
    if let recommendedAdUnitInsight = insights[AdUnitIdInsightName] {
       _recommendedAdUnitId = recommendedAdUnitInsight._string
    }
    if let floorPriceInsight = insights[FloorPriceInsightName] {
       _calculatedBidFloor = floorPriceInsight._float
    }
}
NeftaPlugin._instance.GetBehaviourInsight(
  new String[] { "recommended_rewarded_ad_unit_id", "calculated_user_floor_price_rewarded" },
  (HashMap<String, Insight> insights) -> {
      _recommendedAdUnitId = null;
      _calculatedBidFloor = 0;
      if (insights.containsKey(AdUnitIdInsightName)) {
         _recommendedAdUnitId = insights.get(AdUnitIdInsightName)._string;
      }
      if (insights.containsKey(FloorPriceInsightName)) {
         _calculatedBidFloor = insights.get(FloorPriceInsightName)._float;
      }
   }
);
  
NeftaAdapterEvents.GetBehaviourInsight(
  new string[] { "recommended_rewarded_ad_unit_id", "calculated_user_floor_price_rewarded"},
  (Dictionary<string, Insight> insights) => {
     _recommendedAdUnitId = null;
     _calculatedBidFloor = 0;
     if (insights.TryGetValue(AdUnitIdInsightName, out var insight))
     {
        _recommendedAdUnitId = insight._string;
     }
     if (insights.TryGetValue(FloorPriceInsightName, out insight))
     {
        _calculatedBidFloor = insight._float;
     }
  }
);

🚧

Validate returned values

You should checks that the returned values are valid. Only proceed with using the value if a valid response is returned.

You are guaranteed to receive the callback in the same thread with all keys that you specified in the request.

Log outcome of ad opportunity

After a partner has received user insights, validated the response and requested an ad in MAX using the recommended_*_ad_unit_id field, the partner should log the outcome of the ad opportunity in order to continuously maximise ad revenue uplift.

When the ad successfully loads, log the response using the following function:

- (void)didLoadAd:(MAAd *)ad {
    [ALNeftaMediationAdapter OnExternalMediationRequestLoad: AdTypeInterstitial recommendedAdUnitId: _recommendedAdUnitId calculatedFloorPrice: _calculatedBidFloor ad: ad];
}
func didLoad(_ ad: MAAd) {
    ALNeftaMediationAdapter.onExternalMediationRequestLoad(.rewarded, recommendedAdUnitId: _recommendedAdUnitId, calculatedFloorPrice: _calculatedBidFloor, ad: ad)
}
@Override
public void onAdLoaded(@NonNull MaxAd ad) {
    NeftaMediationAdapter.OnExternalMediationRequestLoaded(NeftaMediationAdapter.AdType.Rewarded, _recommendedAdUnitId, _calculatedBidFloor, ad);
}
private void OnAdLoadedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
{
    NeftaAdapterEvents.OnExternalMediationRequestLoaded(NeftaAdapterEvents.AdType.Rewarded, _recommendedAdUnitId, _calculatedBidFloor, adInfo);
}

When the ad fails to load, log the response using the following function:

- (void)didFailToLoadAdForAdUnitIdentifier:(NSString *)adUnitIdentifier withError:(MAError *)error {
    [ALNeftaMediationAdapter OnExternalMediationRequestFail: AdTypeInterstitial recommendedAdUnitId: _recommendedAdUnitId calculatedFloorPrice: _calculatedBidFloor adUnitIdentifier: adUnitIdentifier error: error];
}
func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) {
    ALNeftaMediationAdapter.onExternalMediationRequestFail(.rewarded, recommendedAdUnitId: _recommendedAdUnitId, calculatedFloorPrice: _calculatedBidFloor, adUnitIdentifier: adUnitIdentifier, error: error)
}
@Override
    public void onAdLoadFailed(@NonNull String adUnitId, @NonNull MaxError maxError) {
          NeftaMediationAdapter.OnExternalMediationRequestFailed(NeftaMediationAdapter.AdType.Rewarded, _recommendedAdUnitId, _calculatedBidFloor, adUnitId, maxError);
}
private void OnAdFailedEvent(string adUnitId, MaxSdkBase.ErrorInfo errorInfo)
{
    NeftaAdapterEvents.OnExternalMediationRequestFailed(NeftaAdapterEvents.AdType.Rewarded, _recommendedAdUnitId, _calculatedBidFloor, adUnitId, errorInfo);
}

When an ad successfully shows log the impression with this:

- (void)didPayRevenueForAd:(nonnull MAAd *)ad {
    [ALNeftaMediationAdapter OnExternalMediationImpression: ad];
}
func didPayRevenue(for ad: MAAd) {
    ALNeftaMediationAdapter.onExternalMediationImpression(ad)
}
@Override
public void onAdRevenuePaid(final MaxAd ad) {
    NeftaMediationAdapter.OnExternalMediationImpression(ad);
}
// By default this impression is collected automatically in Unity.
// But in case you disable this (with sendAdEvents=false in plugin Init) and send impression manually:
MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent += (adUnitId, info) =>
{
    NeftaAdapterEvents.OnExternalMediationImpression(adUnitId, info);
};

Initial Ad Unit has no fill

If the initially recommended ad unit doesn't return an ad:

  • Re-request the bid floor price and recommended ad unit id: the new floor price takes into account the previous no-fill event and is adjusted in real time. Then request the ad again. You can repeat this process until you get fill. Please note max requires an exponential backoff delay between these retries, specified as 2, 4, 8, 16, 32, 64, 64, 64... seconds. As mentioned in the introduction, Nefta insures your ads are served within time windows you specify and balances fill %, revenue uplift and latency.

Example code

See example optimisation code