Service models

In our standard PC performance data service, the game and benchmark performance metrics are delivered via a simple API. For customers who receive data estimated from the PCs components, a standard API that takes the necessary system configuration information as the input and return the metrics as the output is offered. 

For service models were the PCs are tested in UL or customer facilities with UL checking the results, more customization is usually needed, so data delivery models and input and output formats vary depending on the customer need and service model.

These pages document the standard API that offers estimated performance information based on system specification and aims to address the most common questions about the integration process.

What is offered with the API

In the standard API based service package, UL offers benchmark and game performance data estimated based on the PCs components.

The gaming data covers over 50 games at multiple resolution and graphics fidelity levels. See [this section of the website] for an up-to-date list of games, resolutions and graphics settings supported.

In the standard package only the data is provided, how this data is used on the customers website is up to the customer. Data is offered in a flexible way that allows it to be conveniently used on individual product pages or to power advanced search functionality.

API endpoints

The standard API offers four endpoints. The actual content varies by customer and contract.

EndpointInputOutput
/list-cpu-List of CPUs currently supported by the API
/list-gpu-List of GPUs currently supported by the API
/list-games-List of games currently supported by the API
/estimateSystem configurationEstimated benchmark metrics and game FPS levels

The first three API endpoints provide lists of supported components and games. 

The /estimate endpoint takes the system configuration as an input, using the name strings from the /list-cpu and /list-gpu endpoints, and returns all performance data. 

Input parameters for the /estimate endpoint

cpuName

The name of the CPU (as it appears in the /list-cpu output)

gpuName

The name of the GPU (as it appears in the /list-gpu output)

cpuOC

This is a multiplier for the CPU frequency, which is used for pre-overclocked CPUs.

For stock clocks, set the value to 1.

gpuNumber

The number of identical GPUs in a multi-GPU configuration, i.e. SLI or Crossfire

gpuType

This parameter defines the type of GPU and is used to distinguish parts with the same name but different performance levels. The possible values are desktop, notebook and maxq, used with NVIDIA's Max-Q notebook GPUs. The /list-gpu endpoint details which options are available for each GPU.

memchannels

The number of memory channels

memfrequency

Memory frequency in MHz i.e. 2400, 2666, 3200


DDR5 memory has two 32-bit memory channels instead of the one 64-bit channel used by DDR4. The API expects the number of memory channels as an input. For one DD5 module, the correct input is two memory channels. With two modules, the API input should be four memory channels.

Input example

For the /estimate endpoint, the system configuration details are POSTed in JSON format.

{
  "cpuName": "Intel Core i7-8700K Processor",
  "gpuName": "Nvidia GeForce GTX 1050 Ti",
  "cpuOC": 1.1,
  "gpuNumber": 1,
  "gpuType": "desktop",
  "memchannels": 2,
  "memfrequency": 3200
}

Authorization Header

The API is protected with HTTP request signing with HMAC-SHA1 algorithm. This approach is widely used and provides a moderate level of API protection. As the result of the signing procedure, the API expects an Authorization header in each request. Some clear examples can be seen in here.

You need an API key and secret that we provide to you along with the API.

The signing procedure parameters:

1. HTTP method, for example ‘POST’ , ‘GET’

2. URL path , for example in Javascript it can be obtained with the following code:

const uri = new URL('https://ul-benchmarks.appspot.com/api/estimate');
const urlPath = uri.pathname;

3. API key

4. Secret

See below for sample implementations in JavaScript and PHP.

Trial API

Prospective customers can request a free trial API that is populated with a small set of components and sample game data. Apart from the limited content, the trial API offers all the same functionality and uses the same endpoint URLs and authorization mechanisms giving prospective customers full transparency of the implementation process necessary.

Please contact our sales team for details. 

Implementation tips and FAQ

How can I make my implementation reliable and responsive to the customer?

Since the data for one PC rarely changes, we recommend you cache the data locally instead of querying the API for each website visitor.

How often should I refresh the data?

The performance estimates for a component rarely change, usually only a few times across the component’s entire life cycle. Games are retested periodically, but any one game is not expected to receive new data more often than once per quarter. In practice, refreshing the data more often than once per day is not necessary, refreshing the data weekly is recommended.


Sample code

Authorization header implementation in JavaScript

The example depends on crypto package "crypto-browserify", so please have it in your application dependency:

npm install crypto-browserify – save

const CryptoJS = require('crypto');   
 
    calculateRFC2104HMAC(text, key){
        return CryptoJS.createHmac('sha1', key)
        .update(text)
        .digest('hex');     
    }
 
    getAuthHeader(method, uriStr, apikey, secret){
        if(!uriStr || !apikey || !secret || !method){
          return;
        }
 
        const date = (new Date().getTime() / 1000).toString().split(".")[0]; // unix stamp seconds
        const uri = new URL(uriStr);
        const signatureString = 
           `(request-target): ${method} ${uri.pathname} date: ${date.toString()}`.toLowerCase();
        const signature = this.calculateRFC2104HMAC(signatureString, secret);
        // NOTE: for debugging
        // console.log(`signature: ${signature}, apikey: ${apikey}, secret: ${secret}`);
        return `Signature keyId="${apikey}",algorithm="hmac-sha1",headers="(request-target) date",nonce="${date.toString()}",signature="${signature}"`; 
    }

Authorization header implementation in PHP

The following example is a /list-gpu API call.

<?php 
    $apiKey = '<YOUR_API_KEY>';
    $apiSecret = '<YOUR_SECRET>';
    $fullURLString = 'https://ul-benchmarks.appspot.com/api/list-gpu';
    $options = array(
        'key' => $apiSecret,
        'keyId' => $apiKey,
        'algorithm' => 'hmac-sha1');
    $options['headers'] = array('(request-target) date', 'nonce');
    $forNonce = date_timestamp_get(new Datetime("now"));
    $headers['nonce'] = $forNonce;
    $sign = array();
    $data = join("\n", $sign);
    $signatureString = "(request-target): get /api/list-gpu date: " . strval($forNonce);
    $signature = hash_hmac('sha1', $signatureString, $options['key']);
    $final = 'Signature keyId="' . $options['keyId'] . '",algorithm="hmac-sha1"' . ',headers="(request-target) date"' . ',nonce="' . strval($forNonce) . '",signature="' . $signature . '"';
    /*echo "Sample authorization: " . 'Signature keyId="623dc04f515644d89ea9b3944488cd03",algorithm="hmac-sha1",headers="(request-target) date",nonce="1619103273",signature="9da04b2be4de94de02150d3b60ce488c6a30d42f"' . "</br>";
    echo "Sent signatureString: " . $signatureString . "</br>";
    echo "Sent signature: " . $signature . "</br>";
    echo "Sent authorization: " . $final . "</br>";*/ 
    $headers['authorization'] = $final;
    $http_headers = array();
    foreach ($headers as $k => $v) {
        $http_headers[] = "$k: $v";
    }
    $http_headers[] = "accept: application/json";
    $http_headers[] = "X-Api-Key: $apiKey";
    $ch = curl_init($fullURLString);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $http_headers);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $response = curl_exec($ch);
    $err = curl_error($ch);
    if ($err) {
        echo "<p>" . $err . "</p>";    
    }
    echo "<p>" . $response . "</p>";    
    curl_close($ch);
?>

The output looks like the structure below.

[
  {
    "name": "2x AMD Radeon RX 460",
    "type": "desktop"
  },
...
]

The following example is an /estimate API call.

<?php 
    $apiKey = '<YOUR_API_KEY>';
    $apiSecret = '<YOUR_SECRET>';
    $fullURLString = 'https://ul-benchmarks.appspot.com/api/estimate';
    $options = array(
        'key' => $apiSecret,
        'keyId' => $apiKey,
        'algorithm' => 'hmac-sha1');
    $forNonce = date_timestamp_get(new Datetime("now"));
    $headers['nonce'] = $forNonce;
   
    $sign = array();
    $data = join("\n", $sign);
    $signatureString = "(request-target): post /api/estimate date: " . strval($forNonce);
    $signature = hash_hmac('sha1', $signatureString, $options['key']);
   
    $final = 'Signature keyId="' . $options['keyId'] . '",algorithm="hmac-sha1"' . ',headers="(request-target) date"' . ',nonce="' . strval($forNonce) . '",signature="' . $signature . '"';
    /*echo "Sample authorization: " . 'Signature keyId="623dc04f515644d89ea9b3944488cd03",algorithm="hmac-sha1",headers="(request-target) date",nonce="1619103273",signature="9da04b2be4de94de02150d3b60ce488c6a30d42f"' . "</br>";
    echo "Sent signatureString: " . $signatureString . "</br>";
    echo "Sent signature: " . $signature . "</br>";
    echo "Sent authorization: " . $final . "</br>";*/ 
    $headers['authorization'] = $final;
    $http_headers = array();
    foreach ($headers as $k => $v) {
        $http_headers[] = "$k: $v";
    }
    $http_headers[] = "Content-Type: application/json";
    $http_headers[] = "X-Api-Key: $apiKey";
    $request = [
        "cpuName" => "2x Intel Xeon Processor E5-2640 v4",
        "gpuName" => "2x AMD Radeon RX 460",
        "cpuOC" => "1",
        "gpuNumber" => "1",
        "gpuType" => "desktop",
        "memchannels" => 2,
        "memfrequency" => 2400 
    ];
    $postdata = json_encode($request);
    $ch = curl_init($fullURLString);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $http_headers);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_POST, 1);
    $response = curl_exec($ch);
    $err = curl_error($ch);
    if ($err) {
        echo "<p>" . $err . "</p>";    
    }
    echo "<p>" . $response . "</p>";    
    curl_close($ch);
?>

The output looks like the structure below.

{
  "timeSpyScoreGPU": 3436,
  "timeSpyScoreCPU": 6300,
  "timeSpyOverallScore": 3687,
  "gameTitles": {
    "Cyberpunk 2077": {
      "fullHdFps": "35",
      "quadHdFps": "less than 20"
    },
    "Fortnite": {
      "fullHdFps": "145",
      "quadHdFps": "40"
    },
  }
}