S2S ping: Difference between revisions
| No edit summary | |||
| (3 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
| =  | <languages/> | ||
| #  | <translate> | ||
| #  | <!--T:1--> | ||
| #  | = General workflow = | ||
| #  | # The user arrives at your website. You generate a unique <code>kmnrId</code> and send it to Kaminari server via /ping URL, and we save the data in the cache. | ||
| ##  | # Then you show the user a page with our JavaScript script, adding the same <code>kmnrId</code> to the script call. | ||
| ##  | # We collect user data and send it to the server. | ||
| ##  | # On the server, we check in the cache: | ||
| ## If there is a record with a similar <code>kmnrId</code> (coming from /ping), we combine the data from the cache with the results of the check and write them to the statistics. | |||
| ## If there is no record with such an <code>kmnrId</code>, we wait up to five minutes. If the second record still does not arrive, we write what we have in the statistics. | |||
| ## We also periodically check the cache and look for expired clicks that came to /ping but remained unpaired. If we couldn't verify them in any way, we write them to statistics with the status "Technical Losses." | |||
| =  | = Client-side configuration = <!--T:2--> | ||
| ==  | <!--T:3--> | ||
| == Sending data to https://kaminari.systems/v2/ping == | |||
| When a user visits the page, it is necessary to generate a unique display <code>kmnrId</code> on the client-side backend. | |||
| <!--T:4--> | |||
| Then, using PHP, Python, NodeJS, depending on the technology your website is built on, send initializing information to the https://kaminari.systems/v2/ping in the background. | |||
| <!--T:5--> | |||
| The request may not be accepted on the first attempt (in which case, the server will respond with a 204 status),  so we recommend sending the request in a loop until you receive a response with a 200 status. | |||
| <!--T:6--> | |||
| The data should be sent using the POST method in JSON format. | |||
| <!--T:7--> | |||
| List of parameters: | |||
| <!--T:8--> | |||
| {| class="wikitable" | {| class="wikitable" | ||
| |- | |- | ||
| !  | ! Parameter Name !! Parameter Description !! Mandatory? | ||
| |- | |- | ||
| | kmnrId ||  | | kmnrId || Random impression identifier. Generated on the client side. It can be anything - UUID, random number, random string of text. '''Must be sent as a string.''' || yes | ||
| |- | |- | ||
| | kmnrKey ||  | | kmnrKey || Integration identifier within which the impression is analyzed. || yes | ||
| |- | |- | ||
| | sub1 ||  | | sub1 || Filled sub-tags. If you do not use certain tags, there is no need to send them. || no | ||
| |- | |- | ||
| | sub2 || ... ||  | | sub2 || ... || no | ||
| |- | |- | ||
| | sub3 || ... ||  | | sub3 || ... || no | ||
| |- | |- | ||
| | sub4 || ... ||  | | sub4 || ... || no | ||
| |- | |- | ||
| | sub5 || ... ||  | | sub5 || ... || no | ||
| |- | |- | ||
| | sub6 || ... ||  | | sub6 || ... || no | ||
| |- | |- | ||
| | sub7 || ... ||  | | sub7 || ... || no | ||
| |} | |} | ||
| <!--T:9--> | |||
| In addition, the following headers obtained from the user should be added to this request: | |||
| {| class="wikitable" | {| class="wikitable" | ||
| |- | |- | ||
| !  | ! Parameter Name !! Parameter Description !! Mandatory? | ||
| |- | |- | ||
| | user-agent ||  | | user-agent || User agent || yes | ||
| |- | |- | ||
| | referer ||  | | referer || Page the user came from || no | ||
| |- | |- | ||
| | accept-language ||  | | accept-language || Browser interface language || yes | ||
| |- | |- | ||
| | x-original-ip ||  | | x-original-ip || Real user IP || yes | ||
| |- | |- | ||
| | x-forwarded-for || IP | | x-forwarded-for || IP || yes | ||
| |- | |- | ||
| | x-real-ip || IP | | x-real-ip || IP || no | ||
| |- | |- | ||
| | sec-ch-ua-arch ||   | | sec-ch-ua-arch || || no | ||
| |- | |- | ||
| | sec-ch-ua ||   | | sec-ch-ua || || no | ||
| |- | |- | ||
| | sec-ch-ua-full-version ||   | | sec-ch-ua-full-version || || no | ||
| |- | |- | ||
| | device-memory ||   | | device-memory || || no | ||
| |- | |- | ||
| | dpr ||   | | dpr || || no | ||
| |- | |- | ||
| | sec-ch-ua-mobile ||   | | sec-ch-ua-mobile || || no | ||
| |- | |- | ||
| | sec-ch-ua-model ||   | | sec-ch-ua-model || || no | ||
| |- | |- | ||
| | sec-ch-ua-platform ||   | | sec-ch-ua-platform || || no | ||
| |- | |- | ||
| | sec-ch-ua-platform-version ||   | | sec-ch-ua-platform-version || || no | ||
| |- | |- | ||
| | viewport-width ||   | | viewport-width || || no | ||
| |} | |} | ||
| <!--T:10--> | |||
| Example in CURL: | |||
| <pre> | <pre> | ||
| curl 'https://kaminari.systems/v2/ping' \ | curl 'https://kaminari.systems/v2/ping' \ | ||
|    -H 'accept: application/json' \   |    -H 'accept: application/json' \ | ||
|    -H 'accept-language: en-US,en;q=0.9' \   |    -H 'accept-language: en-US,en;q=0.9,uk;q=0.8' \ | ||
|    -H 'cache-control: no-cache' \   |    -H 'cache-control: no-cache' \ | ||
|    -H 'content-type:  |    -H 'content-type: application/json;charset=UTF-8' \ | ||
|    -H 'origin: https://test.com' \   |    -H 'origin: https://test.com' \ | ||
|    -H 'pragma: no-cache' \   |    -H 'pragma: no-cache' \ | ||
|    -H 'referer: https://test.com/' \   |    -H 'referer: https://test.com/' \ | ||
|    -H 'user-agent: Mozilla/5.0 ( |    -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36' \ | ||
|   -H 'x-original-ip: 127.0.0.1' \ | |||
|   -H 'x-forwarded-for: 192.168.0.1' \ | |||
|   -H 'sec-ch-ua-arch: "x86"' \ | |||
|   -H 'sec-ch-ua: "Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"' \ | |||
|   -H 'sec-ch-ua-full-version: "129.0.6668.100"' \ | |||
|   -H 'device-memory: 8' \ | |||
|   -H 'dpr: 2' \ | |||
|   -H 'sec-ch-ua-mobile: ?0' \ | |||
|   -H 'sec-ch-ua-model: ""' \ | |||
|   -H 'sec-ch-ua-platform: "macOS"' \ | |||
|   -H 'sec-ch-ua-platform-version: "14.5.0"' \ | |||
|   -H 'viewport-width: 981' \ | |||
|    --data-raw '{"kmnrKey":"XXXXXXXX","kmnrId":"1125570260","sub1":"test","sub2":"1001","sub3":"10","sub4":"1385282124113622","sub5":"222","sub6":"555666","sub7":"8833705265931305"}'   |    --data-raw '{"kmnrKey":"XXXXXXXX","kmnrId":"1125570260","sub1":"test","sub2":"1001","sub3":"10","sub4":"1385282124113622","sub5":"222","sub6":"555666","sub7":"8833705265931305"}'   | ||
|    --compressed |    --compressed | ||
| </pre> | </pre> | ||
| <!--T:11--> | |||
| Example in PHP: | |||
| <pre> | <pre> | ||
| $url = 'https://kaminari.systems/v2/ping'; | $url = 'https://kaminari.systems/v2/ping'; | ||
| Line 113: | Line 137: | ||
| ]); | ]); | ||
| <!--T:12--> | |||
| curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload ); | curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload ); | ||
| curl_setopt( $ch, CURLOPT_HTTPHEADER, [ | curl_setopt( $ch, CURLOPT_HTTPHEADER, [ | ||
|      'Content-Type:application/json', |      'Content-Type:application/json;charset=UTF-8', | ||
|      'Accept-Language:' . $_SERVER['HTTP_ACCEPT_LANGUAGE'], |      'Accept-Language:' . $_SERVER['HTTP_ACCEPT_LANGUAGE'], | ||
|      'User-Agent:' . $_SERVER['HTTP_USER_AGENT'], |      'User-Agent:' . $_SERVER['HTTP_USER_AGENT'], | ||
|      isset($_SERVER['HTTP_REFERER']) ? 'Referer:' . $_SERVER['HTTP_REFERER'] : '', |      isset($_SERVER['HTTP_REFERER']) ? 'Referer:' . $_SERVER['HTTP_REFERER'] : '', | ||
|     'X-Original-Ip:' . $_SERVER['REMOTE_ADDR'], | |||
|     isset($_SERVER['X_FORWARDED_FOR']) ? 'X-Forwarded-For:' . $_SERVER['X_FORWARDED_FOR'] : '', | |||
|     isset($_SERVER['HTTP_SEC_CH_UA_ARCH']) ? 'Sec-CH-UA-Arch:' . $_SERVER['HTTP_SEC_CH_UA_ARCH'] : '', | |||
|     isset($_SERVER['HTTP_SEC_CH_UA']) ? 'Sec-CH-UA:' . $_SERVER['HTTP_SEC_CH_UA'] : '', | |||
|     isset($_SERVER['HTTP_SEC_CH_UA_FULL_VERSION']) ? 'Sec-CH-UA-Full-Version:' . $_SERVER['HTTP_SEC_CH_UA_FULL_VERSION'] : '', | |||
|     isset($_SERVER['HTTP_DEVICE_MEMORY']) ? 'Device-Memory:' . $_SERVER['HTTP_DEVICE_MEMORY'] : '', | |||
|     isset($_SERVER['HTTP_DPR']) ? 'DPR:' . $_SERVER['HTTP_DPR'] : '', | |||
|     isset($_SERVER['HTTP_SEC_CH_UA_MOBILE']) ? 'Sec-CH-UA-Mobile:' . $_SERVER['HTTP_SEC_CH_UA_MOBILE'] : '', | |||
|     isset($_SERVER['HTTP_SEC_CH_UA_MODEL']) ? 'Sec-CH-UA-Model:' . $_SERVER['HTTP_SEC_CH_UA_MODEL'] : '', | |||
|     isset($_SERVER['HTTP_SEC_CH_UA_PLATFORM']) ? 'Sec-CH-UA-Platform:' . $_SERVER['HTTP_SEC_CH_UA_PLATFORM'] : '', | |||
|     isset($_SERVER['HTTP_SEC_CH_UA_PLATFORM_VERSION']) ? 'Sec-CH-UA-Platform-Version:' . $_SERVER['HTTP_SEC_CH_UA_PLATFORM_VERSION'] : '', | |||
|     isset($_SERVER['HTTP_VIEWPORT_WIDTH']) ? 'Viewport-Width:' . $_SERVER['HTTP_VIEWPORT_WIDTH'] : '', | |||
| ]); | ]); | ||
| curl_exec($ch); | curl_exec($ch); | ||
| Line 124: | Line 161: | ||
| </pre> | </pre> | ||
| ==  | <!--T:13--> | ||
| == Adding kmnrId to the script == | |||
| The generated <code>kmnrId</code>, as well as the <code>kmnrKey</code> and sub-tags, should then be passed into the call of the JavaScript script on the page. | |||
| <!--T:14--> | |||
| <pre> | <pre> | ||
| <script> | <script> | ||
| Line 141: | Line 180: | ||
|      }; |      }; | ||
|      var kmnrSc = document.createElement('script'); |      <!--T:15--> | ||
| var kmnrSc = document.createElement('script'); | |||
|      var kmnrPrnt = document.getElementsByTagName('head')[0] || document.body; |      var kmnrPrnt = document.getElementsByTagName('head')[0] || document.body; | ||
|      kmnrSc.setAttribute('async', true); |      kmnrSc.setAttribute('async', true); | ||
| Line 150: | Line 190: | ||
| </pre> | </pre> | ||
| =  | = Testing = <!--T:16--> | ||
| <!--T:17--> | |||
| For testing data transmission and ensuring their compatibility with the API, you can use the following URL: https://kaminari.systems/v1/pingtest | |||
| ===  | <!--T:18--> | ||
| === Example in PHP: === | |||
| <pre> | <pre> | ||
| $url = 'https://kaminari.systems/v1/pingtest'; | $url = 'https://kaminari.systems/v1/pingtest'; | ||
| Line 171: | Line 213: | ||
|      ]); |      ]); | ||
|      curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload ); |      <!--T:19--> | ||
| curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload ); | |||
|      curl_setopt( $ch, CURLOPT_HTTPHEADER, [ |      curl_setopt( $ch, CURLOPT_HTTPHEADER, [ | ||
|          'Content-Type:application/json', |          'Content-Type:application/json', | ||
| Line 191: | Line 234: | ||
|      echo "<pre>2. Response body: $body</pre>"; |      echo "<pre>2. Response body: $body</pre>"; | ||
|      'curl error: ' . curl_error($ch); |      <!--T:20--> | ||
| 'curl error: ' . curl_error($ch); | |||
|      curl_close($ch); |      curl_close($ch); | ||
| } catch (Exception $e) { | } catch (Exception $e) { | ||
| Line 198: | Line 242: | ||
| </pre> | </pre> | ||
| <!--T:21--> | |||
| Response : | |||
| <pre> | <pre> | ||
| 1. Response length: 287 | 1. Response length: 287 | ||
| Line 215: | Line 260: | ||
| </pre> | </pre> | ||
| ===  | <!--T:22--> | ||
| === Example in Node.js: === | |||
| <pre> | <pre> | ||
| const https = require('https'); | const https = require('https'); | ||
| <!--T:23--> | |||
| const payload = JSON.stringify({ | const payload = JSON.stringify({ | ||
|      'kmnrKey': 'XXXXXXXX', |      'kmnrKey': 'XXXXXXXX', | ||
| Line 231: | Line 278: | ||
| }); | }); | ||
| <!--T:24--> | |||
| const options = { | const options = { | ||
|      host: 'kaminari.click', |      host: 'kaminari.click', | ||
| Line 246: | Line 294: | ||
| }; | }; | ||
| <!--T:25--> | |||
| const req = https | const req = https | ||
|      .request(options, (res) => { |      .request(options, (res) => { | ||
|          let data = ''; |          let data = ''; | ||
|          res.on('data', (chunk) => { |          <!--T:26--> | ||
| res.on('data', (chunk) => { | |||
|              data += chunk; |              data += chunk; | ||
|          }); |          }); | ||
| Line 264: | Line 314: | ||
|      }); |      }); | ||
| <!--T:27--> | |||
| req.on('error', (err) => { | req.on('error', (err) => { | ||
|      console.log("Error: " + err.message); |      console.log("Error: " + err.message); | ||
| Line 271: | Line 322: | ||
| </pre> | </pre> | ||
| <!--T:28--> | |||
| Response: | |||
| <pre> | <pre> | ||
| { | { | ||
| Line 290: | Line 342: | ||
| </pre> | </pre> | ||
| [[Category:Features]] | [[Category:Features]] | ||
| </translate> | |||
Latest revision as of 16:53, 11 October 2024
General workflow
- The user arrives at your website. You generate a unique kmnrIdand send it to Kaminari server via /ping URL, and we save the data in the cache.
- Then you show the user a page with our JavaScript script, adding the same kmnrIdto the script call.
- We collect user data and send it to the server.
- On the server, we check in the cache:
- If there is a record with a similar kmnrId(coming from /ping), we combine the data from the cache with the results of the check and write them to the statistics.
- If there is no record with such an kmnrId, we wait up to five minutes. If the second record still does not arrive, we write what we have in the statistics.
- We also periodically check the cache and look for expired clicks that came to /ping but remained unpaired. If we couldn't verify them in any way, we write them to statistics with the status "Technical Losses."
 
- If there is a record with a similar 
Client-side configuration
Sending data to https://kaminari.systems/v2/ping
When a user visits the page, it is necessary to generate a unique display kmnrId on the client-side backend.
Then, using PHP, Python, NodeJS, depending on the technology your website is built on, send initializing information to the https://kaminari.systems/v2/ping in the background.
The request may not be accepted on the first attempt (in which case, the server will respond with a 204 status), so we recommend sending the request in a loop until you receive a response with a 200 status.
The data should be sent using the POST method in JSON format.
List of parameters:
| Parameter Name | Parameter Description | Mandatory? | 
|---|---|---|
| kmnrId | Random impression identifier. Generated on the client side. It can be anything - UUID, random number, random string of text. Must be sent as a string. | yes | 
| kmnrKey | Integration identifier within which the impression is analyzed. | yes | 
| sub1 | Filled sub-tags. If you do not use certain tags, there is no need to send them. | no | 
| sub2 | ... | no | 
| sub3 | ... | no | 
| sub4 | ... | no | 
| sub5 | ... | no | 
| sub6 | ... | no | 
| sub7 | ... | no | 
In addition, the following headers obtained from the user should be added to this request:
| Parameter Name | Parameter Description | Mandatory? | 
|---|---|---|
| user-agent | User agent | yes | 
| referer | Page the user came from | no | 
| accept-language | Browser interface language | yes | 
| x-original-ip | Real user IP | yes | 
| x-forwarded-for | IP | yes | 
| x-real-ip | IP | no | 
| sec-ch-ua-arch | no | |
| sec-ch-ua | no | |
| sec-ch-ua-full-version | no | |
| device-memory | no | |
| dpr | no | |
| sec-ch-ua-mobile | no | |
| sec-ch-ua-model | no | |
| sec-ch-ua-platform | no | |
| sec-ch-ua-platform-version | no | |
| viewport-width | no | 
Example in CURL:
curl 'https://kaminari.systems/v2/ping' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US,en;q=0.9,uk;q=0.8' \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json;charset=UTF-8' \
  -H 'origin: https://test.com' \
  -H 'pragma: no-cache' \
  -H 'referer: https://test.com/' \
  -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36' \
  -H 'x-original-ip: 127.0.0.1' \
  -H 'x-forwarded-for: 192.168.0.1' \
  -H 'sec-ch-ua-arch: "x86"' \
  -H 'sec-ch-ua: "Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"' \
  -H 'sec-ch-ua-full-version: "129.0.6668.100"' \
  -H 'device-memory: 8' \
  -H 'dpr: 2' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-model: ""' \
  -H 'sec-ch-ua-platform: "macOS"' \
  -H 'sec-ch-ua-platform-version: "14.5.0"' \
  -H 'viewport-width: 981' \
  --data-raw '{"kmnrKey":"XXXXXXXX","kmnrId":"1125570260","sub1":"test","sub2":"1001","sub3":"10","sub4":"1385282124113622","sub5":"222","sub6":"555666","sub7":"8833705265931305"}' 
  --compressed
Example in PHP:
$url = 'https://kaminari.systems/v2/ping';
$ch = curl_init( $url );
$payload = json_encode([
    'kmnrKey' => 'XXXXXXXX',
    'kmnrId' => '1125570260',
    'sub1' => 'test',
    'sub2' => '1001',
    'sub3' => '10',
    'sub4' => '1385282124113622',
    'sub5' => '222',
    'sub6' => '555666',
    'sub7' => '8833705265931305',
]);
curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload );
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
    'Content-Type:application/json;charset=UTF-8',
    'Accept-Language:' . $_SERVER['HTTP_ACCEPT_LANGUAGE'],
    'User-Agent:' . $_SERVER['HTTP_USER_AGENT'],
    isset($_SERVER['HTTP_REFERER']) ? 'Referer:' . $_SERVER['HTTP_REFERER'] : '',
    'X-Original-Ip:' . $_SERVER['REMOTE_ADDR'],
    isset($_SERVER['X_FORWARDED_FOR']) ? 'X-Forwarded-For:' . $_SERVER['X_FORWARDED_FOR'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_ARCH']) ? 'Sec-CH-UA-Arch:' . $_SERVER['HTTP_SEC_CH_UA_ARCH'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA']) ? 'Sec-CH-UA:' . $_SERVER['HTTP_SEC_CH_UA'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_FULL_VERSION']) ? 'Sec-CH-UA-Full-Version:' . $_SERVER['HTTP_SEC_CH_UA_FULL_VERSION'] : '',
    isset($_SERVER['HTTP_DEVICE_MEMORY']) ? 'Device-Memory:' . $_SERVER['HTTP_DEVICE_MEMORY'] : '',
    isset($_SERVER['HTTP_DPR']) ? 'DPR:' . $_SERVER['HTTP_DPR'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_MOBILE']) ? 'Sec-CH-UA-Mobile:' . $_SERVER['HTTP_SEC_CH_UA_MOBILE'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_MODEL']) ? 'Sec-CH-UA-Model:' . $_SERVER['HTTP_SEC_CH_UA_MODEL'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_PLATFORM']) ? 'Sec-CH-UA-Platform:' . $_SERVER['HTTP_SEC_CH_UA_PLATFORM'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_PLATFORM_VERSION']) ? 'Sec-CH-UA-Platform-Version:' . $_SERVER['HTTP_SEC_CH_UA_PLATFORM_VERSION'] : '',
    isset($_SERVER['HTTP_VIEWPORT_WIDTH']) ? 'Viewport-Width:' . $_SERVER['HTTP_VIEWPORT_WIDTH'] : '',
]);
curl_exec($ch);
curl_close($ch);
Adding kmnrId to the script
The generated kmnrId, as well as the kmnrKey and sub-tags, should then be passed into the call of the JavaScript script on the page.
<script>
    window.kmnr = {
        kmnrKey: 'XXXXXXXX',
        kmnrId: '1125570260',
        sub1: 'test',
        sub2: '1001',
        sub3: '10',
        sub4: '1385282124113622',
        sub5: '222',
        sub6: '555666',
        sub7: '8833705265931305',
    };
    var kmnrSc = document.createElement('script');
    var kmnrPrnt = document.getElementsByTagName('head')[0] || document.body;
    kmnrSc.setAttribute('async', true);
    kmnrSc.setAttribute('charset', 'utf-8');
    kmnrSc.src = '//kaminari.systems/v1/script.js?kmnrKey=' + window.kmnr.kmnrKey;
    kmnrPrnt && kmnrPrnt.appendChild(kmnrSc);
</script>
Testing
For testing data transmission and ensuring their compatibility with the API, you can use the following URL: https://kaminari.systems/v1/pingtest
Example in PHP:
$url = 'https://kaminari.systems/v1/pingtest';
try {
    $ch = curl_init( $url );
    $payload = json_encode([
        'kmnrKey' => 'XXXXXXXX',
        'kmnrId' => (string)mt_rand(),
        'sub1' => 'test',
        'sub2' => '1001',
        'sub3' => '10',
        'sub4' => '40',
        'sub5' => '555',
        'sub6' => '6',
        'sub7' => '77777',
    ]);
    curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload );
    curl_setopt( $ch, CURLOPT_HTTPHEADER, [
        'Content-Type:application/json',
        'Accept-Language:' . $_SERVER['HTTP_ACCEPT_LANGUAGE'],
        'User-Agent:' . $_SERVER['HTTP_USER_AGENT'],
        'DPR:2',
        isset($_SERVER['HTTP_REFERER']) ? 'Referer:' . $_SERVER['HTTP_REFERER'] : 'Referer:""',
    ]);
    curl_setopt( $ch, CURLOPT_HEADER, true);
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
    
    $response = curl_exec($ch);
    $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
    $header = substr($response, 0, $header_size);
    $body = substr($response, $header_size);
    $body = str_replace('", "', '",<br />    "', $body);
    $body = str_replace('{ "', '{<br />    "', $body);
    echo "<pre>1. Response length: $header_size</pre>";
    echo "<pre>2. Response body: $body</pre>";
    'curl error: ' . curl_error($ch);
    curl_close($ch);
} catch (Exception $e) {
    echo "Error register User: {$e->getMessage()}";
}
Response :
1. Response length: 287
2. Response body: {"errors": {
    "X-Forwarded-For":"recommended to add in request headers",
    "X-Real-IP":"recommended to add in request headers",
    "sec-ch-ua-arch":"recommended to add in request headers",
    "sec-ch-ua":"recommended to add in request headers",
    "sec-ch-ua-full-version":"recommended to add in request headers",
    "device-memory":"recommended to add in request headers",
    "sec-ch-ua-mobile":"recommended to add in request headers",
    "sec-ch-ua-model":"recommended to add in request headers",
    "sec-ch-ua-platform":"recommended to add in request headers",
    "sec-ch-ua-platform-version":"recommended to add in request headers",
    "viewport-width":"recommended to add in request headers"}}
Example in Node.js:
const https = require('https');
const payload = JSON.stringify({
    'kmnrKey': 'XXXXXXXX',
    'kmnrId': (Math.random() + 1).toString(36).substring(9),
    'sub1': 'test',
    'sub2': '1001',
    'sub3': '10',
    'sub4': '40',
    'sub5': '555',
    'sub6': '6',
    'sub7': '77777',
});
const options = {
    host: 'kaminari.click',
    port: 443,
    path: '/v1/pingtest',
    method: 'POST',
    headers: { 
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(payload),
        'Accept-Language': 'en-US,en;q=0.5',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0',
        'DPR': '2',
        'Referer': '""',
    }
};
const req = https
    .request(options, (res) => {
        let data = '';
        res.on('data', (chunk) => {
            data += chunk;
        });
        
        res.on('end', () => {
            try {
                data = JSON.parse(data);
                console.log(data);
            } catch {
                console.log(data);
            }
        });
    });
req.on('error', (err) => {
    console.log("Error: " + err.message);
});
req.write(payload);
req.end();
Response:
{
  errors: {
    'X-Forwarded-For': 'recommended to add in request headers',
    'X-Real-IP': 'recommended to add in request headers',
    'sec-ch-ua-arch': 'recommended to add in request headers',
    'sec-ch-ua': 'recommended to add in request headers',
    'sec-ch-ua-full-version': 'recommended to add in request headers',
    'device-memory': 'recommended to add in request headers',
    'sec-ch-ua-mobile': 'recommended to add in request headers',
    'sec-ch-ua-model': 'recommended to add in request headers',
    'sec-ch-ua-platform': 'recommended to add in request headers',
    'sec-ch-ua-platform-version': 'recommended to add in request headers',
    'viewport-width': 'recommended to add in request headers'
  }
}