AppLovin recommends that you pass your app content URL when you make ad requests. It allows for buy-side contextualization or review. Some DSP buyers may require it. You pass this URL in the bid request.
The following code snippets show how you pass your app content URL when you make an ad request:
self.bannerAd = [[MAAdView alloc] initWithAdUnitIdentifier: @"«ad-unit-ID»"];
[self.bannerAd setExtraParameterForKey: @"content_url" value: @"«value»"];
self.interstitialAd = [[MAInterstitialAd alloc] initWithAdUnitIdentifier: @"«ad-unit-ID»"];
[self.interstitialAd setExtraParameterForKey: @"content_url" value: @"«value»"];
self.mrecAd = [[MAAdView alloc] initWithAdUnitIdentifier: @"«ad-unit-ID»" adFormat: MAAdFormat.mrec ];
[self.mrecAd setExtraParameterForKey: @"content_url" value: @"«value»"];
self.nativeAdLoader = [[MANativeAdLoader alloc] initWithAdUnitIdentifier: @"«ad-unit-ID»"];
[self.nativeAdLoader setExtraParameterForKey: @"content_url" value: @"«value»"];
self.rewardedAd = [MARewardedAd sharedWithAdUnitIdentifier: @"«ad-unit-ID»"];
[self.rewardedAd setExtraParameterForKey: @"content_url" value: @"«value»"];
let bannerAd = MAAdView(adUnitIdentifier: "«ad-unit-ID»")
bannerAd.setExtraParameterForKey("content_url", value: "«value»")
let interstitialAd = MAInterstitialAd(adUnitIdentifier: "«ad-unit-ID»")
interstitialAd.setExtraParameterForKey("content_url", value: "«value»")
let mrecAd = MAAdView(adUnitIdentifier: "«ad-unit-ID»", adFormat: MAAdFormat.mrec)
mrecAd.setExtraParameterForKey("content_url", value: "«value»")
let nativeAdLoader = MANativeAdLoader(adUnitIdentifier: "«ad-unit-ID»")
nativeAdLoader.setExtraParameterForKey("content_url", value: "«value»")
let rewardedAd = MARewardedAd.shared(withAdUnitIdentifier: "«ad-unit-ID»")
rewardedAd.setExtraParameterForKey("content_url", value: "«value»")
You can pass UID2 tokens in the bid stream. Buyers use these tokens to target and accurately bid on app inventory. You are responsible for tokenizing and for passing the token to AppLovin. The code examples below show how you pass the tokens:
ALSdkSettings *settings = [ALSdk shared].settings;
[settings setExtraParameterForKey: @"uid2_token" value: @"«value»"];
let settings = ALSdk.shared()?.settings
settings?.setExtraParameterForKey("uid2_token", value: "«value»")
AppLovin recommends that you pass user email addresses, phone numbers, and Publisher User IDs for attribution and matching purposes.
The SDK automatically hashes email addresses and phone numbers with SHA-256 on the device, and sends the email domain in plaintext. It never transmits the plaintext email address or phone number. The SDK sends the Publisher User ID as you provide it, so do not include other personal or third-party identifiers in that field.
Before you pass additional user identifiers, make sure that you have all rights and permissions necessary to share it with AppLovin, including any end-user consent that applicable privacy laws require, and that your privacy policy discloses this sharing.
ALUserData *userData = [[ALUserData alloc] init];
userData.email = @"user@example.com";
userData.phone = @"+12125550123";
userData.userId = @"your_user_id";
[ALSdk shared].userData = userData;
let userData = ALUserData()
userData.email = "user@example.com"
userData.phone = "+12125550123"
userData.userId = "your_user_id"
ALSdk.shared().userData = userData
Set every field that is available to you; each one is optional.
Note the following when you pass additional user identifiers:
AppLovin hashes plaintext email addresses and phone numbers that you provide.
emailFor gmail.com and googlemail.com only, first remove all periods before
the @, and remove the first plus sign and everything after it. Leave the
domain unchanged. If nothing remains before the @, produce an empty value.
phone1 for United States phone numbers).
Then hash via SHA256.
For example (for email addresses):
| Input | Normalized | SHA256 |
|---|---|---|
Alice@Example.COM | alice@example.com | ff8d9819fc0e12bf0d24892e45987e249a28dce836a85cad60e28eaaa8c6d976 |
J..A.NE+work.more+extra@GMAIL.COM | jane@gmail.com | 988b074286b20c503e3015c2076533f3bf4ce5ca6f8a507ab52c2e0f98d620b7 |
Jane.Doe+Work@GoogleMail.com | janedoe@googlemail.com | 338abf9ef1c8793cadc7bcf51ed595338eb727ed9e06ce3d91d566d60b975937 |
Reference implementations follow. Each one prints the hash for the second example above; run it and confirm you get the same value.
#include <algorithm>
#include <cctype>
#include <iostream>
#include <regex>
#include <string>
#include <openssl/sha.h>
std::string normalizeAndHash(std::string email)
{
email.erase(0, email.find_first_not_of(" \t\n\r\f\v"));
email.erase(email.find_last_not_of(" \t\n\r\f\v") + 1);
std::transform(email.begin(), email.end(), email.begin(),
[](unsigned char c) { return std::tolower(c); });
static const std::regex valid("^[!-?A-~]+@[!-?A-~]+$");
if (!std::regex_match(email, valid))
return "";
const auto at = email.find('@');
std::string local = email.substr(0, at);
const std::string domain = email.substr(at + 1);
if (domain == "gmail.com" || domain == "googlemail.com")
{
local = local.substr(0, local.find('+'));
local.erase(std::remove(local.begin(), local.end(), '.'), local.end());
if (local.empty())
return "";
}
const std::string normalized = local + "@" + domain;
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256(reinterpret_cast<const unsigned char*>(normalized.data()),
normalized.size(), hash);
static constexpr char digits[] = "0123456789abcdef";
std::string out;
for (const unsigned char value : hash)
{
out += digits[value >> 4];
out += digits[value & 15];
}
return out;
}
int main()
{
std::cout << normalizeAndHash(" J..A.NE+work.more+extra@GMAIL.COM ") << "\n";
}
using System;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
class Program
{
static readonly Regex Valid = new Regex("^[!-?A-~]+@[!-?A-~]+$");
static string NormalizeAndHash(string email)
{
string trimmed = email.Trim(' ', '\t', '\n', '\r', '\f', '\v').ToLowerInvariant();
if (!Valid.IsMatch(trimmed))
return "";
int at = trimmed.IndexOf('@');
string local = trimmed.Substring(0, at);
string domain = trimmed.Substring(at + 1);
if (domain == "gmail.com" || domain == "googlemail.com")
local = local.Split('+')[0].Replace(".", "");
if (local.Length == 0)
return "";
using (SHA256 sha256 = SHA256.Create())
{
byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(local + "@" + domain));
StringBuilder output = new StringBuilder(hash.Length * 2);
foreach (byte value in hash)
output.Append(value.ToString("x2"));
return output.ToString();
}
}
static void Main()
{
Console.WriteLine(NormalizeAndHash(" J..A.NE+work.more+extra@GMAIL.COM "));
}
}
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
import java.util.regex.Pattern;
public final class EmailHash
{
private static final Pattern VALID = Pattern.compile( "^[!-?A-~]+@[!-?A-~]+$" );
static String normalizeAndHash( final String email ) throws NoSuchAlgorithmException
{
final String trimmed = email.trim().toLowerCase();
if ( !VALID.matcher( trimmed ).matches() )
{
return "";
}
final int at = trimmed.indexOf( '@' );
final String domain = trimmed.substring( at + 1 );
String local = trimmed.substring( 0, at );
if ( domain.equals( "gmail.com" ) || domain.equals( "googlemail.com" ) )
{
local = local.split( "\\+", 2 )[0].replace( ".", "" );
if ( local.isEmpty() )
{
return "";
}
}
return HexFormat.of().formatHex( MessageDigest.getInstance( "SHA-256" )
.digest( ( local + "@" + domain ).getBytes( StandardCharsets.UTF_8 ) ) );
}
public static void main( final String[] args ) throws NoSuchAlgorithmException
{
System.out.println( normalizeAndHash( " J..A.NE+work.more+extra@GMAIL.COM " ) );
}
}
import { createHash } from "crypto";
function normalizeAndHash(email) {
const trimmed = email.replace(/^[ \t\n\r\f\v]+|[ \t\n\r\f\v]+$/g, "").toLowerCase();
if (!/^[!-?A-~]+@[!-?A-~]+$/.test(trimmed)) {
return "";
}
const at = trimmed.indexOf("@");
let local = trimmed.slice(0, at);
const domain = trimmed.slice(at + 1);
if (domain === "gmail.com" || domain === "googlemail.com") {
local = local.split("+")[0].replaceAll(".", "");
}
return local ? createHash("sha256").update(`${local}@${domain}`).digest("hex") : "";
}
console.log(normalizeAndHash(" J..A.NE+work.more+extra@GMAIL.COM "));
import java.security.MessageDigest
private val VALID = Regex("^[!-?A-~]+@[!-?A-~]+$")
fun normalizeAndHash(email: String): String {
val trimmed = email.trim(' ', '\t', '\n', '\r', '\u000B', '\u000C').lowercase()
if (!VALID.matches(trimmed)) return ""
val (rawLocal, domain) = trimmed.split('@')
val local = if (domain == "gmail.com" || domain == "googlemail.com") {
rawLocal.substringBefore('+').replace(".", "").ifEmpty { return "" }
} else rawLocal
return MessageDigest.getInstance("SHA-256")
.digest("$local@$domain".toByteArray())
.joinToString("") { "%02x".format(it) }
}
fun main() {
println(normalizeAndHash(" J..A.NE+work.more+extra@GMAIL.COM "))
}
import hashlib
import re
VALID = re.compile(r"^[!-?A-~]+@[!-?A-~]+$")
def normalize_and_hash(email):
email = email.strip(" \t\n\r\f\v").lower()
if not VALID.match(email):
return ""
local, _, domain = email.partition("@")
if domain in ("gmail.com", "googlemail.com"):
local = local.split("+", 1)[0].replace(".", "")
return hashlib.sha256(f"{local}@{domain}".encode()).hexdigest() if local else ""
print(normalize_and_hash(" J..A.NE+work.more+extra@GMAIL.COM "))
import CryptoKit
import Foundation
func normalizeAndHash(_ email: String) -> String {
let trimmed = email.trimmingCharacters(
in: CharacterSet(charactersIn: " \t\n\r\u{0b}\u{0c}")).lowercased()
guard trimmed.range(of: "^[!-?A-~]+@[!-?A-~]+$", options: .regularExpression) != nil
else { return "" }
let parts = trimmed.split(separator: "@")
let domain = String(parts[1])
var local = String(parts[0])
if domain == "gmail.com" || domain == "googlemail.com" {
local = local.prefix { $0 != "+" }.filter { $0 != "." }
}
guard !local.isEmpty else { return "" }
return SHA256.hash(data: Data("\(local)@\(domain)".utf8))
.map { String(format: "%02x", $0) }.joined()
}
print(normalizeAndHash(" J..A.NE+work.more+extra@GMAIL.COM "))
988b074286b20c503e3015c2076533f3bf4ce5ca6f8a507ab52c2e0f98d620b7