Help Center/Details/

Reporting API 2.0

Date Feature
2020/12/01
  • Supports T+0 “date*placement_id*country” level estimated revenue data. (Note that estimated revenue data might take a few days to stabilize) 
  • Supports UTC+0 estimated revenue data (Available from 2020/12/01. Note that billing is still based on UTC+8 data)
  • Returns app package name
  • Updated signing method
2020/12/20
  • Updated request demo
2021/01/12
  • “Region“ has become an optional request parameter
2021/03/16
  • Added response fields:media_name,code_name,os.
  • Updated request sample and code sample.
2021/07/05
  • Updated endpoints
2021/07/12
  • Modified the caliber of fill_rate.
  • New metrics release : ad_requestresponsead_fill_rate and ad_impression_rate. But these added metrics do not have data before March 15 2021.

Update

Pangle has launched the new endpoints for reporting API on 5th July 2021.

  • open-api.pangle.cn which only allows publishers to pull out revenue generated from the Chinese Mainland region
  • open-api.pangleglobal.com which only allows publishers to pull out revenue generated from the Non-Chinese Mainland regions

To avoid potentially missing data from the reporting API 1.0, we strongly recommend you update your settings to the reporting API 2.0 as soon as possible.


Request

Request Params Type Mandatory Allowed Value Default Value Note
user_id int Yes - - user_id is your Pangle account id
role_id int Yes - - Role_id can be found adjacent to your security key. The api will only return data of the apps the role has access to. To retrieve complete data, please make sure you have permission for all the apps.
reporting api.png
timestamp int Yes - - In unix time format. Eg:1095379200;Timestamp is the current timestamp. When the offset between timestamp and the current time is greater than 10 minutes, a parameter error will be returned to prevent request replay
version string Yes 2.0 - Version of this reporting api, please fill in 2.0
date string Yes - - YYYY-MM-DD, e.g. '2020-10-10'
time_zone int No 0 or 8 8 8 for utc8; 0 for utc0; Please note that Pangle's billing data is still based on UTC8
currency string No usd or cny cny
region string No Two digit country/region code under ISO3166-1, eg:cn
sign string Yes - - 1)A string in hexadecimal that should be generated with the method provided below.
2)Sign Sample:7ff19ec1961d8c2b7c7b3845d974d22e
sign_type string Yes MD5 -


Request Sample



Pull out the data generated from the Chinese Mainland region
https://open-api.pangle.cn/union_pangle/open/api/rt/income?currency=cny&date=2021-01-12&role_id=459&sign_type=MD5&time_zone=8&user_id=459&version=2.0×tamp=1615778032&sign=598b733d2eb8d5ef7fb326fc2061dfa1


Pull out the data generated from the Non-Chinese Mainland regions
https://open-api.pangleglobal.com/union_pangle/open/api/rt/income?currency=cny&date=2021-01-12&role_id=459&sign_type=MD5&time_zone=8&user_id=459&version=2.0×tamp=1615778032&sign=598b733d2eb8d5ef7fb326fc2061dfa1

How is the param "sign" generated?

To get the right sign value, you should:

  1. Sort your request keys by alphabetical order
  2. Sign your request keys and values with your security key (Security key can be found in the Pangle platform - SDK Integration - Data API)
  3. sign=md5(k1=v1&k2=v2...security-key)

Sign - Python Code Sample

# coding=utf-8
import hashlib


class PangleMediaUtil:
    user_id = 459
    role_id = 459
    secure_key = "xxxxsecure_key"

    version = "2.0"
    sign_type_md5 = "MD5"
    KEY_USER_ID = "user_id"
    KEY_ROLE_ID = "role_id"
    KEY_VERSION = "version"
    KEY_SIGN    = "sign"
    KEY_SIGN_TYPE = "sign_type"
    PANGLE_HOST = "https://www.pangle.cn"

    @classmethod
    def sign_gen(self, params):
        """Fetches sign .
        Args:
        params: a dict need to sign
        secure_key: string

        Returns:
        A dict. For example:

        {'url': 'a=1&sign_type=MD5&t=2&z=a&sign=7ff19ec1961d8c2b7c7b3845d974d22e',
        'sign': '7ff19ec1961d8c2b7c7b3845d974d22e'}
        """
        result = {
            "sign": "",
            "url": "",
        }
        try:
            if not isinstance(params, dict):
                print("invalid params: ", params)
                return result

            if self.user_id != "":
                params[self.KEY_USER_ID] = self.user_id

            if self.role_id != "":
                params[self.KEY_ROLE_ID] = self.role_id

            params[self.KEY_VERSION] = self.version
            params[self.KEY_SIGN_TYPE] = self.sign_type_md5

            param_orders = sorted(params.items(), key=lambda x: x[0], reverse=False)
            raw_str = ""
            for k, v in param_orders:
                raw_str += (str(k) + "=" + str(v) + "&")
                print("raw sign_str: ", raw_str)
                if len(raw_str) == 0:
                    return ""
                sign_str = raw_str[0:-1] + self.secure_key
                print("raw sign_str: ", sign_str)

            sign = hashlib.md5(sign_str.encode()).hexdigest()
            result[self.KEY_SIGN] = sign
            result["url"] = raw_str + "sign=" + sign
            return result
        except Exception as err:
            print("invalid Exception", err)
            return result

    @classmethod
    def get_signed_url(self, params):
        return self.sign_gen(params).get("url", "")

    @classmethod
    def get_media_rt_income(self, params):
        result = self.get_signed_url(params)
        if result == "":
            return ""
        return self.PANGLE_HOST + "/union_pangle/open/api/rt/income?" + result


# Code Sample
params = {
    "currency": "usd",
    "time_zone": 0,
    "date": "2020-12-18",
    "region": "jp",
    "timestamp":"1615778032",
}
PangleMediaUtil.user_id = 459  # Replace with pangle account id
PangleMediaUtil.role_id = 459  # Replace with pangle role_id
PangleMediaUtil.secure_key = "xxxxsecure_key"
print(PangleMediaUtil.get_media_rt_income(params))


Sign - PHP Code Sample


<?php

final class pangle_media_util{
    
    private static $user_id = 459;                # Replace with your user_id
    private static $role_id  = 459;               # Replace with your role_id
    private static $secure_key = "xxxxsecure_key";    # Replace with your Secutiry Key
    private static $version = "2.0";
    const PANGLE_HOST = "https://www.pangle.cn";
    const KEY_SIGN_TYPE="sign_type";
    const KEY_SIGN = "sign";
    const KEY_ROLE_ID = "role_id";
    const KEY_USER_ID = "user_id";
    const KEY_VERSION = "version";
    
    // Supported sign method
    private static $sign_array = array(
        'MD5' => 'sign_md5',
    );

    // md5 sign method
    private static function sign_md5($str, $key){
        
        $sign_str = $str . $key;
        $sign = md5($sign_str);
        printf("md5 [sign string without key:%s] => [%s]", $sign_str,$sign);
        return $sign;
    }
    
    // Examine sign method
    private static function valid_sign_method($method){
        
        if (empty(self::$sign_array[$method])) {
            printf("unexpected sign method %s", $method);
            return false;
        }
        return true;
    }
    
    // Sign the string
    private static function sign($method, $str, $key){
        if (!self::valid_sign_method($method)) {
            return false;
        }
        
        return call_user_func(array('self', self::$sign_array[$method]), $str, $key);
    }
    
    // Sign the array
    private static function make_sign($method, $arr_input, $sign_key){
        // Config array to be signed
        $sign_str = '';
        foreach ($arr_input as $key => $val) {
            $sign_str .= '&' . strval($key) . '=' . strval($val);
        }
        $sign_str = substr($sign_str, 1);
        
        // Sign
        $ret = self::sign($method, $sign_str, $sign_key);
        return $ret;
    }
    
    // Create signed query string
    private static function make_signed_querystring($method, $arr_input, $key, $key_sign = self::KEY_SIGN, $key_sign_method = self::KEY_SIGN_TYPE){
        // Sign
        $sign = self::make_signed_array_utf8($method, $arr_input, $key, $key_sign, $key_sign_method);
        if (!$sign) {
            return false;
        }
        
        // Create querystring
        $query_string = http_build_query($arr_input);
        
        return sprintf("%s&%s=%s", $query_string, $key_sign, $sign);
    }
    
    /**
    * to retuen data in json,convert the sign result  into utf-8
    *@param sign_method, array to sign, secure_key, array key of sign and sign_method
    *@return result of sign when success while false if not.
    */
    private static function make_signed_array_utf8($method, &$arr_input, $key, $key_sign = self::KEY_SIGN, $key_sign_method = self::KEY_SIGN_TYPE){
        // add the sign method into the array to be signed
        $arr_input[$key_sign_method] = $method;
        $arr_input[self::KEY_VERSION] = self::$version;
        if (self::$role_id != "") {
            $arr_input[self::KEY_ROLE_ID] = self::$role_id;
        }
        
        if (self::$user_id != "") {
            $arr_input[self::KEY_USER_ID] = self::$user_id;
        }
    
        // Sort Array
        if(!ksort($arr_input)){
            printf("make_signed_array_utf8::sort arr_input failed, arr: %s", printf($arr_input, true));
            return false;
        }

        // create string to be signed
        $signStr = '';
        foreach ($arr_input as $inputKey => $inputVal) {
            $signStr .= '&'.$inputKey.'='.$inputVal;
        }
        $signStr = substr($signStr, 1);
        
        // convert the string to utf-8
        $signStr = mb_convert_encoding($signStr, 'UTF-8', 'GBK');
        
        // Sign
        $ret = self::sign($method, $signStr, $key);
        if(!$ret){
            printf("make sign_utf8 failed, method:%s, str:%s", $method, print_r($arr_input, true));
            return false;
        }
        return $ret;
    }

    static function get_media_rt_income_url($method, $arr_input, $key_sign = self::KEY_SIGN, $key_sign_method = self::KEY_SIGN_TYPE) {
        try {
            return self::PANGLE_HOST . "/union_pangle/open/api/rt/income?" . self::make_signed_querystring($method, $arr_input, self::$secure_key, $key_sign, $key_sign_method);
        } catch (Exception $e) {
            printf($e->getMessage());
            return "";
        }
    }
    
    static function set_secure_key($secure_key) {
        self::$secure_key = $secure_key;
    }
    
    static function set_user_id($user_id) {
        self::$user_id = $user_id;
    }
    
    static function set_role_id($role_id) {
        self::$role_id = $role_id;
    }

}

# Sample
# pangle_media_util::set_secure_key("xxxxsecure_key");
pangle_media_util::set_user_id(459);
pangle_media_util::set_role_id(459);
$params = array(
    "currency"  => "usd",
    "date"      => "2020-11-19",
    "region"    => "jp",
    "time_zone" => 0,
    "timestamp" => "1615778032",
);
$url = pangle_media_util::get_media_rt_income_url('MD5', $params);
echo "\n\n" . $url . "\n";

?>


Response

Primary Params Type Note
Code string Status code
Message string Gives explanation in case an error occurs
Data dict {
  "date" : income_info
}





income_info is a list comprised of dictionaries that contains the following information

Param Name Type Description
time_zone string Timezone
currency string Currency
region string Two digit country/region code under ISO3166-1
app_id int App id on Pangle platform
app_name string App Name on Pangle platform
ad_slot_id int Placement id on Pangle platform
ad_slot_type int Also known as Ad type,1(In-feed ad)、2(Banner(Horizontal))、3(Splash ad)、4(Interstitial ad)、5(Rewarded Video Ads)、6(Full Page Video Ads)、7(Draw in-feed ad)、8(In-Stream Ads)、9(New interstitial ad)
package_name string Package name in the form of aaa.bbb.ccc
request int The number of requests received by Pangle. A request is counted as 1 even if it asked for more than one ad creative. The pre-cached requests automatically initiated by Pangle are counted towards the number of Requests (only for Splash Ads and Rewarded Video Ads). For details, please visit: Metrics Description. Available upon invitation, returns 0 otherwise.
return int The number of ad creatives returned by Pangle. The Ad Responses are counted as X when a request asks for Y ads creatives and Pangle successfully returns X ad creatives. The number of ad creatives returned in response to the pre-cached requests automatically initiated by Pangle is counted towards the number of Ad Responses. Available upon invitation, returns 0 otherwise.
fill_rate float fill_rate =  response/request (Available upon invitation, returns 0 otherwise).
show int Impressions
click int Clicks
click_rate float Click Through Rate
revenue float Estimated Revenue
ecpm float Estimated Ecpm
media_name string Account name
code_name string Slot name on Pangle platform
os string Operating system
use_mediation int Is use pangle mediation. 0 (Not) 、1 (Yes)
bidding_type int RIT bidding type. 0 (standard) 、1 (in-app bidding) 、2 (client bidding)
ad_request int The number of requested ad creatives received by Pangle. When a request asks X ad creatives, it will be counted as X Ad Requests. The number of ad creatives requested by the pre-cached requests automatically initiated by Pangle is counted towards the number of Ad Requests.
response int The number of ad responses returned by Pangle. A response is counted as 1 even if Pangle returned more than one ad creative. Responses for the pre-cached requests automatically initiated by Pangle are counted towards the number of Responses.
ad_fill_rate float ad_fill_rate = return / ad_request (Available upon invitation, returns 0 otherwise).
ad_impression_rate float ad_impression_rate = show / return (Available upon invitation, returns 0 otherwise).




Request Sample
https://partner.oceanengine.com/union_pangle/open/api/rt/income?user_id=561&sign=7ff19ec1961d8c2b7c7b3845d974d22e&timestamp=1606034728&date=2020-11-19&currency=usd&region=jp&role_id=561&version=2.0&sign_type=MD5

Response Sample
{
    "Code":"100",
    "Message": ""
    "Data":{
        "2020-11-19":[
            {
                "ad_slot_id":811583091,
                "app_code_type":0,
                "app_id":5011583,
                "app_name":xxx,
                "ad_slot_type":1,
                "package_name":xxx,
                "media_name":xxx,
                "code_name":xxx,
                "os":xxx,
                "click":0,
                "click_rate":0,
                "currency":"usd",
                "date":"2020-11-19",
                "ecpm":7.71,
                "fill_rate":100,
                "region":"jp",
                "request":8,
                "return":8,
                "revenue":0.06,
                "show":8,
                "time_zone":8
            },
            {
               "ad_slot_id":811583092,
                "app_code_type":0,
                "app_id":5011583,
                "app_name":xxx,
                "ad_slot_type":1,
                "package_name":xxx,
                "media_name":xxx,
                "code_name":xxx,
                "os":xxx,
                "click":0,
                "click_rate":0,
                "currency":"usd",
                "date":"2020-11-19",
                "ecpm":7.71,
                "fill_rate":100,
                "region":"cn",
                "request":8,
                "return":8,
                "revenue":0.06,
                "show":8,
                "time_zone":8
            }
        ]
    }
}


Message

Code Message
100 Success. Any other status code means the request has been unsuccessful
101 Sign verification failed
102 Invalid user_id
103 Invalid date format
106 Exceed QPS limits. Pangle allows up to 5 qps
114 Invalid param
133 Invalid region enum
PD0004 Success. But no data available under the provided filtering conditions




Did the content solve your problem?