Documentation

Handle error 152 “Insufficient points”

PHP 5 using JSON with the file_get_contents function

In version 5 of the Yandex.Direct API, points are awarded to all advertisers every 60 minutes, but each advertiser has its own schedule for awarding points that is not related to the beginning of the astronomical hour. The example shows how requests are executed if the first request returns the 152 error code.

For demonstration purposes, it uses the sleep function, which allows you to set delays for running the script. The delays are set in seconds in the set_DELAY array, so that after the first attempt no more than 5 repeat requests are sent with increasing intervals that cover the next 60 minutes after receiving the first error.

You should preferably design your application to use more reliable approaches for resending requests. You can organize the query execution logic using a task scheduler (such as cron) or a queuing system (such as Gearman).

<?php
//--- Settings ---------------------------------------------------------//
$set_DEBUG = true; // Output debugging information (html): false - do not output; true - output
$set_DELAY = array(0, 360, 540, 720, 900, 1080); // Array of delays between repeat requests (in seconds)
$err_NOT_ENOUGH_UNITS = 152; // "Insufficient points" error
ini_set('max_execution_time', 5400); // Maximum time for executing the script: 5400 seconds (90 minutes)
// Settings for printing the buffer contents to the screen when using the sleep function
ob_implicit_flush();

//--- Input data --------------------------------------------//
// Address of the Campaigns service for sending JSON requests (case-sensitive)
$url = 'https://api.direct.yandex.com/json/v5/campaigns';
// OAuth token of the user to execute requests on behalf of
$token = 'TOKEN';
// Login of an advertising agency client
// Required parameter if requests are made on behalf of an advertising agency
$clientLogin = 'CLIENT_LOGIN';
      
//--- Preparing the request ------------------------------------------------//
// Setting the request HTTP headers
$headers = array(
   "Authorization: Bearer $token",                    // OAuth token. The word Bearer must be used
   "Client-Login: $clientLogin",                      // Login of the advertising agency client
   "Accept-Language: en",                             // Language for response messages
   "Content-Type: application/json; charset=utf-8"    // Data type and request encoding
);

// Parameters for the request to the Yandex.Direct API server
$params = array(
   'method' => 'get',                                 // Method of the Campaigns service
   'params' => array(
      'SelectionCriteria' => (object) array(),        // Criteria for filtering campaigns. To get all campaigns, leave it empty
      'FieldNames' => array('Id', 'Name')             // Names of parameters to get        
   )
);
// Converting input parameters to JSON
$body = json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

// Creating the stream context: Setting HTTP headers and the request message body
$streamOptions = stream_context_create(array(
   'http' => array(
      'method' => 'POST',
      'ignore_errors' => true, // Get the response content even if the HTTP response code is not 200
      'header' => $headers,
      'content' => $body
   )
));

//--- Performing tasks -------------------------------------------------//
/*
Starting the request execution loop. If the first request completed successfully, i.e. it did not return error 152, the list of campaigns is output. 
If the first request returned an error, repeated requests are made after the interval set in the set_DELAY array.
*/
foreach ($set_DELAY as $currentAttempt => $delay) {
   
      // Executing the request and getting the result
      $result = file_get_contents($url, 0, $streamOptions);
      
      //--- Processing the request result ---------------------//
      // Taking the HTTP headers from the response, if there are any
      if (isset($http_response_header)) {
         foreach ($http_response_header as $header) {
            if (preg_match('#HTTP/[0-9\.]+\s+([0-9]+)#', $header, $arr)) {
               $httpStr = $header; // Explanation for the HTTP response code
               $httpCode = intval($arr[1]); // Numeric value of the HTTP response code
            }
            if (preg_match('/RequestId: (\d+)/', $header, $arr)) { 
               $requestId = $arr[1]; // Request ID
            }
            if (preg_match('/Units: (\d+)\/(\d+)\/(\d+)/', $header, $arr)) {
               $unitsSpent = $arr[1]; // Number of points spent
               $unitsAvailable = $arr[2]; // Number of points available
               $unitsLimit = $arr[3]; // Daily limit
            }
         }
      }
      else {
         // If there aren't any headers, it means the request wasn't executed, so we output an error
         echo date("d.m.Y H:i:s > ").'Request to the API server failed';
      }
      
      // Processing the response if an HTTP code was returned
      if (isset($httpCode)) {
         // If the HTTP 200 code was received, process the response message body
         if ($httpCode == 200) {            
            // Output information about points
            if (isset($unitsSpent)) {
               echo date("d.m.Y H:i:s > ")."Points spent on request: $unitsSpent Currently available: $unitsAvailable Daily limit: $unitsLimit<br>";
            }
            
            // Decoding the response from JSON
            $response = json_decode($result);
            
            // If the API server returned an error in the response message body, output it
            if (isset($response->error)) {
               $apiErr = $response->error;
               echo date("d.m.Y H:i:s > ")."API error {$apiErr->error_code}: {$apiErr->error_string} - {$apiErr->error_detail} (RequestId: {$apiErr->request_id})<br>";         
            }
         }
         else { echo date("d.m.Y H:i:s > ").'Request to the API server failed: '.$httpStr; }
      }
      
      //--- Output debugging information ---------------------------------//
      if ($set_DEBUG) {
         echo "<hr>Request URL: $url<br>";
         echo "Request headers: <pre>".implode($headers, '<br>')."</pre>";
         echo "Request body (JSON): <pre>".json_encode($params, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)."</pre>";
         if (isset($http_response_header)) { echo "Response headers: <pre>".implode($http_response_header, '<br>')."</pre>"; }
         if (isset($response)) { echo "Response body (JSON): <pre>".json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)."</pre>"; }
         if (isset($httpCode) and $httpCode != 200) { echo "Response body: <pre>".htmlspecialchars($result)."</pre>"; }
         echo "<hr>";
      }

      //--- Processing the result ----------------------------------------//
      // Output the list of campaigns if there is a result
      if (isset($response->result)) {            
         foreach ($response->result->Campaigns as $campaign) {
            echo "{$campaign->Name} (№ {$campaign->Id})<br>";
         }
         
         if ($response->result->LimitedBy) {
            // If the response contains the LimitedBy parameter, it means that not all available objects were returned. 
            // In this case, make additional requests to get all of the objects.
            // Detailed description of paginated selection - https://tech.yandex.ru/direct/doc/dg/best-practice/get-docpage/#page
            echo '<br>Not all available objects were returned.';
         }
      }

      //--- Actions if error 152 "Not enough points" occurs or doesn't occur ---//
      if (isset($apiErr->error_code) and $apiErr->error_code == $err_NOT_ENOUGH_UNITS) {
         // If the set_DELAY array has more values than the current iteration, set a delay before the next iteration of the cycle
         if (isset($set_DELAY[$currentAttempt+1])) {
            unset ($apiErr); // Delete the variable with the error information
            echo date("d.m.Y H:i:s > ")."Repeat request in ".$set_DELAY[$currentAttempt+1]." seconds<br>";
            sleep ($set_DELAY[$currentAttempt+1]);
         }
      }
      // If error 152 "Not enough points" wasn't returned, the task is finished and we can end the script
      else { die(); }
}
?>