
OpenRTB Adopts Structured User Agents
In version 2.6 of the OpenRTB specification (issued April 2022), the RTB Project has added a description of a “structured user agent” as part of the user agent object (section 3.2.29). For the Advertising industry, this structured user agent combines information found in another new data format called User-Agent Client Hints. WURFL’s API is already updated to accept User-Agent Client Hints. With the sample code outlined in this blog, WURFL users can transform structured user agents (received from an RTB transaction) into User-Agent Client Hints and feed them into the WURFL device detection process.
The Advent of User-Agent Client Hints
For years, online advertising has leveraged device detection for analytics, targeting, and ensuring that ads can effectively display on devices.
The key source of device information came from analyzing the user-agent string found in the HTTP request that ad publishers receive. You can learn more about the user-agent string and the device detection process here.
The era of the user-agent string as the primary source for device detection started to end a few years ago. Google proposed a new mechanism called Client Hints. Essentially, Client Hints is a negotiation mechanism whereby the client browser and server request and receive information. Google has started to use the Client Hints mechanism to convey much of the same information found in the User-Agent string. Several new User-Agent Client Hints are in the process of rolling out. During this rollout process, User-Agents strings will be frozen, meaning they could be conveying potentially inaccurate information. Instead, users should opt to use the User-Agent Client Hints. For more information, watch this brief webinar where we answer most of your critical questions about User-Agent Client Hints.
WURFL has already adapted its device detection processes to incorporate User-Agent Client Hints and reconcile them with existing User Agent strings. WURFL’s process ensures continued accuracy.
What is a Structured User Agent?
In the advertising ecosystem, the real-time bidding process needed a way to communicate these User-Agent Client Hints. The IAB’s OpenRTB specification version 2.6 (section 3.2.29) has outlined a new “structured user agent” or SUA (also referred to as “device.sua”). Essentially, the structured user agent combines the parameters of the User-Agent Client Hints and passes it as one of the OpenRTB data objects.
The OpenRTB specification section 3.2.18 also refers to the SUA while outlining parameters for the device object. Here is a blog that outlines how WURFL can populate most of the parameters found in the device object.
The parameters listed in the OpenRTB device object are a small subset of what WURFL can actually determine. Advertisers, Supply-Side Platforms (SSP), and Demand-Side Platforms (DSP) rely on WURFL to enrich the core device object with more WURFL device capabilities that are useful for device and technographic targeting. For example, WURFL can provide the price of the device (release_msrp) and the age of the device (release_date).
Receiving and Using the Structured User Agent
If an advertising publisher passes the SUA in a OpenRTB transaction, then you can use the code below to break the SUA into its component User-Agent Client Hints and feed it into WURFL’s device matching process. This means that WURFL users receiving a SUA can expect the same accuracy as before. In addition, they can continue to enrich their device information beyond just the narrow sliver of info included in the SUA.
Feeding Structured User Agents into WURFL
Below is sample code written in Java that will transform a sample SUA object into UA-CHs that WURFL can consume. The sample code will make use of the WURFL OnSite API for Java to perform the device detection.
It is a good practice to forward both the User-Agent string (or the ua OpenRTB object) and the SUA where available. The WURFL API has the intelligence to determine which will provide better detection accuracy.
Please note that the sua object is fairly recent and has been mocked-up to show the properties of the object as per the OpenRTB v2.6 spec.
{ "sua": { "browsers": [ { "Not A;Brand": "99.0.0.0" }, { "Chromium": "99.0.4844.88" }, { "Google Chrome": "99.0.4844.88" } ], "platform": { "Android": "12" }, "mobile": "1", "architecture": "arm", "bitness": "64", "model": "Pixel 6" } }
Next, the code will parse the parameters of the SUA and rewrite them into the format in which User-Agent Client Hints are typically sent.
// If the ua object is available, always pass it to the WURFL API in addition to the sua object String ua = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Mobile Safari/537.36"; // JSON encoded sua String sua_json = "{\"sua\":{\"browsers\":[{\"Not A;Brand\":\"99.0.0.0\"},{\"Chromium\":\"99.0.4844.88\"},{\"Google Chrome\":\"99.0.4844.88\"}],\"platform\":{\"Android\":\"12\"},\"mobile\":\"1\",\"architecture\":\"arm\",\"bitness\":\"64\",\"model\":\"Pixel 6\"}}"; // Let's decode the entire JSON object // We’ll use the popular Gson library to do this JsonObject sua_object = new Gson().fromJson(sua_json, JsonObject.class); // Get the sua object sua_object = sua_object.getAsJsonObject("sua"); // Let's parse the openrtb_sua_browsers brand version array into the User-Agent Client Hint standard format for brands String brands = ""; JsonArray browsers_array = sua_object.getAsJsonArray("browsers"); for (int i = 0; i <browsers_array.size(); i++) { JsonObject browser_object = browsers_array.get(i).getAsJsonObject(); Set<Map.Entry<String, JsonElement>> entries = browser_object.entrySet(); for(Map.Entry<String, JsonElement> entry: entries) { brands += "\""; brands +=entry.getKey(); brands += "\";v=\""; brands += browser_object.get(entry.getKey()).getAsString(); brands += "\","; } } // Remove the trailing comma brands = brands.substring(0, brands.length() -1); // Let's parse the platform value now String platform = ""; String platform_version = ""; JsonObject platform_object = sua_object.getAsJsonObject("platform"); Set<Map.Entry<String, JsonElement>> entries = platform_object.entrySet(); for(Map.Entry<String, JsonElement> entry: entries) { platform = entry.getKey(); platform_version= platform_object.get(entry.getKey()).getAsString(); } // Let us get the rest of the UA-CH values now String model = sua_object.get("model").getAsString(); String bitness = sua_object.get("bitness").getAsString(); String architecture = sua_object.get("architecture").getAsString(); String mobile = sua_object.get("mobile").getAsString();
Finally, the UA-CHs are assembled into a HTTP Request format and fed into the WURFL device detection process.
// Construct and assemble a HTTP request Map<String,String> headers = new HashMap<>(); headers.put("User-Agent", ua); headers.put("Sec-Ch-Ua", brands); headers.put("Sec-Ch-Ua-Full-Version-List", brands); headers.put("Sec-Ch-Ua-Platform", platform); headers.put("Sec-Ch-Ua-Platform-Version", platform_version); headers.put("Sec-Ch-Ua-Model", model); headers.put("Sec-Ch-Ua-Architecture", architecture); headers.put("Sec-Ch-Ua-Bitness", bitness); headers.put("Sec-Ch-Ua-Mobile", mobile); // Perform the device detection using the WURFL API now System.out.println("Running WURFL Java API " + WURFLEngine.API_VERSION); String wurflRootPath = "wurfl.zip"; WURFLEngine engine = new GeneralWURFLEngine(wurflRootPath); engine.load(); WURFLRequest req = new DefaultWURFLRequest(headers); Device device = engine.getDeviceForRequest(req); System.out.println("Device id: " + device.getId()); System.out.println("Header Quality: " + engine.headerQuality(req)); System.out.println("Complete Device Name: " + device.getVirtualCapability("complete_device_name")); System.out.println("Advertised Device OS: " + device.getVirtualCapability("advertised_device_os")); System.out.println("Advertised Device OS Version: " + device.getVirtualCapability("advertised_device_os_version")); System.out.println("Advertised Browser: " + device.getVirtualCapability("advertised_browser")); System.out.println("Advertised Browser Version: " + device.getVirtualCapability("advertised_browser_version"));
When you run the code and feed it into WURFL, you should see device detection results like this:
Running WURFL Java API 1.12.5.1 Device id: google_pixel_6_ver1 Header Quality: HEADER_QUALITY_FULL Advertised Device OS: Android Advertised Device OS Version: 12 Advertised Browser: Chrome Mobile Advertised Browser Version: 99.0.4844.88