Downloading Reports from the Bing Ads API

in Bing Ads API

You can extract performance data from the Bing Ads API for metrics such as cost, impressions, and clicks etc. The simplest way to do it is using the report service: It enables you to pull down similar reports to that which you can find on the Reports tab of the web interface. This article provides a basic tutorial showing you how to use the service with PHP.

The Bing Ads API Client Library

The adCenter API currently doesn’t have a client library available for PHP, so you need to construct the SOAP calls yourself; this makes using the API a little bit more challenging, but it’s not too hard once you get the hang of it. I’m using the standard PHP 5 SOAP extension for this tutorial.

The Bing Ads Report Service Location

You need to supply PHP with the location of the service WSDL and its XML namespace. Here are some constants to do this for version 8:

define("ADCENTER_API_ENDPOINT", "https://adcenterapi.microsoft.com/api/advertiser/v8/"); 
define("ADCENTER_API_NAMESPACE", "https://adcenter.microsoft.com/v8"); 
define("ADCENTER_API_REPORT_SERVICE", ADCENTER_API_ENDPOINT . "Reporting/ReportingService.svc?wsdl");
Bing Ads API Namespace and Endpoint

Authentication in the Bing Ads API

When you access the API, you need to supply a developer token together with the username and password of the account you wish to access; you also need the id of the account that you want to download data from:

define("ADCENTER_API_TOKEN", "YOUR_DEV_TOKEN"); 
$username = "YOUR_USERNAME"; 
$password = "YOUR_PASSWORD"; 
$accountId = ACCOUNT_ID;
Bing Ads API Credentials

The account ID can be found by looking for the aid query parameter in the URL when you go to the Accounts & Billing tab on the web UI.

The username, password, and developer token need to be encoded into SOAP headers to be sent along with the service request later:

$headers = array( 
   new SoapHeader(ADCENTER_API_NAMESPACE, 'DeveloperToken', ADCENTER_API_TOKEN, false), 
   new SoapHeader(ADCENTER_API_NAMESPACE, 'UserName', $username, false), 
   new SoapHeader(ADCENTER_API_NAMESPACE, 'Password', $password, false) 
);
Bing Ads API Auth Headers

Defining Reports in the Bing Ads API

You request reports by sending SOAP objects of the specific type for the report you want to produce: For example, to produce a keyword report you would use the KeywordPerformanceReportRequest, and for an ad report you would use the AdPerformanceReportRequest etc. All of the request types derive from a base type called ReportRequest that has the following properties:

  • Format – To specify if you want the report in a CSV or XML file type.
  • Language – The language of the heading rows.
  • ReportName – A descriptive name for the report.
  • ReturnOnlyCompleteData – Specifies if you want to see any data at all if the books aren’t closed for the chosen period of the report: For example, if you specify a time period of Today, then the data won’t be fully processed yet; so if you supply false for this parameter, then you'll get what’s currently available; alternatively, if you specify true, then the report won’t be produced.

In addition to these, most of the derived types also have the following properties:

  • Aggregation – The grouping of data according to time; valid values include Hourly, Daily, Weekly, Monthly, Yearly, and Summary. There are constraints on what values can be used in each report; one example is that some reports don’t allow Hourly aggregation. There’s also constraints about which time periods and columns can be combined with specific aggregation types: you can only use the Today, Yesterday, and Custom Date Range times with Hourly aggregation; and you can’t use the TimePeriod column in Summary reports.
  • Columns – The fields you want to see in the report. These are specific to each report. For example, the keyword performance report has columns such as Keyword, MatchType, Spend, and Impressions. The TimePeriod column can be included in most reports to show the date of a row.
  • Filter – For removing specific rows from a report. Most reports have filters available, but they are specific to each one.
  • Scope – Similar to the filter, it enables you to reduce the number of rows in the report. This is based on id’s of specific accounts, campaigns, and ad groups etc; if the scope is empty then the report will include all accounts for which the users has access.
  • Time – The date range of stats to include in the report: Either use a preset value by specifying a PredefinedTime, or use the CustomDateRangeStart and CustomDateRangeEnd properties. If you use the predefined time, then the value should be one of Today, Yesterday, ThisWeek, LastWeek, ThisMonth, LastMonth, ThisYear, LastYear, LastSevenDays, LastFourWeeks, LastThreeMonths, or LastSixMonths; for custom dates, you need to supply a Date object with appropriate Day, Month, and Year fields.

Here’s an example of a simple request for a keyword performance report:

$request = array( 
  "Format" => 'Csv', 
  "ReportName" => 'Keyword Performance Report', 
  "Aggregation" => 'Daily', 
  "Time" => array('PredefinedTime' => 'ThisMonth'), 
  "Columns" => array('Keyword', 'BidMatchType', 'Spend', 'Impressions', 'TimePeriod'), 
  "Scope" => array('AccountIds' => null) 
);
An Example Report Definition

Scheduling Reports in the Bing Ads API

Once you’ve put together your report request, it first needs to be encoded as the type of report you want to create, then sent to the report service. If the report is scheduled without any errors, then the service will return an id for the report.

$reportType = "KeywordPerformanceReportRequest"; 
$client = new SOAPClient(ADCENTER_API_REPORT_SERVICE); 
$params = array('ReportRequest' => new SoapVar($request, SOAP_ENC_ARRAY, $reportType, ADCENTER_API_NAMESPACE)); 
$result = $client->__soapCall("SubmitGenerateReport", array("SubmitGenerateReportRequest" => $params), null, $headers); 
$reportRequestId = $result->ReportRequestId;
Requesting a Report

Polling a Scheduled Report

The adCenter API uses a similar method to the old AdWords and Yahoo API report services: An asynchronous method is used where you schedule a report, then periodically polling the API and wait for status updates.

You use the PollGenerateReport action to request the status; it returns either Pending, Error, or Success. You keep polling until the Success or Error status is returned. Note that polling the report uses API quota so you shouldn’t call it too often.

Here’s some code for polling a report:

$pollDelay = 60; 
$params = array('ReportRequestId' => $reportRequestId); 
$reportStatus = "Pending"; 
 
while ($reportStatus == "Pending") { 
   sleep($pollDelay); 
   $result = $client->__soapCall("PollGenerateReport", array('PollGenerateReportRequest' => $params), null, $headers); 
   $reportStatus = $result->ReportRequestStatus->Status; 
}
Requesting the Report Status

Downloading the Report from the API

When the report is successfully produced, the PollGenerateReport response also contains a ReportDownloadUrl parameter that tells you the location where you can download the report.

if ($reportStatus == 'Success') { 
   $downloadURL = $result->ReportRequestStatus->ReportDownloadUrl; 
   $filename = "$accountId.zip"; 
   file_put_contents($filename, file_get_contents($downloadURL)); 
} else { 
   // Error occured 
   print "Report download failed"; 
}
Extracting the Completed Report

The download is zipped up, so you need to decompress it before further processing.

Full Example Code: Downloading a Bing Ads API Report

Here's a complete script that downloads a report from the Bing Ads API:

<?php
// Reference to the service and version of the API you want to use
define("ADCENTER_API_ENDPOINT", "https://adcenterapi.microsoft.com/api/advertiser/v8/"); 
define("ADCENTER_API_NAMESPACE", "https://adcenter.microsoft.com/v8"); 
define("ADCENTER_API_REPORT_SERVICE", ADCENTER_API_ENDPOINT . "Reporting/ReportingService.svc?wsdl"); 
 
// Your API Developer Token
define("ADCENTER_API_TOKEN", "YOUR_DEV_TOKEN");
 
// The account that you want to download performace data from
$username = "YOUR_USERNAME";
$password = "YOUR_PASSWORD";
$accountId = ACCOUNT_ID;
 
try {
    // Create the headers needed to access the API
    $headers = array(
        new SoapHeader(ADCENTER_API_NAMESPACE, 'DeveloperToken', ADCENTER_API_TOKEN, false),
        new SoapHeader(ADCENTER_API_NAMESPACE, 'UserName', $username, false),
        new SoapHeader(ADCENTER_API_NAMESPACE, 'Password', $password, false)
    );
 
    // The parameters for the report
    $request = array(
        "Format" => 'Csv',
        "ReportName" => 'Keyword Performance Report',
        "Aggregation" => 'Daily',
        "Time" => array('PredefinedTime' => 'ThisMonth'),
        "Columns" => array('Keyword', 'BidMatchType', 'Spend', 'Impressions', 'TimePeriod'),
        "Scope" => array('AccountIds' => null)
    );
 
    // Specify the type of report
    $reportType = "KeywordPerformanceReportRequest";
 
    // Create the SOAP client
    $client = new SOAPClient(ADCENTER_API_REPORT_SERVICE);
 
    // Encode the request
    $params = array('ReportRequest' => new SoapVar($request, SOAP_ENC_ARRAY, $reportType, ADCENTER_API_NAMESPACE));
 
    // Schedule report
    $result = $client->__soapCall("SubmitGenerateReport", array("SubmitGenerateReportRequest" => $params), null, $headers);
 
    // Get the report ID
    $reportRequestId = $result->ReportRequestId;
 
    // Wait for the report to complete
    $pollDelay = 60;
    $params = array('ReportRequestId' => $reportRequestId);
    $reportStatus = "Pending";
 
    while ($reportStatus == "Pending") {
        sleep($pollDelay);
        $result = $client->__soapCall("PollGenerateReport", array('PollGenerateReportRequest' => $params), null, $headers);
        $reportStatus = $result->ReportRequestStatus->Status;
    }
 
    // Download the report
    if ($reportStatus == 'Success') {
        $downloadURL = $result->ReportRequestStatus->ReportDownloadUrl;
        $filename = "$accountId.zip";
        file_put_contents($filename, file_get_contents($downloadURL));
    } else {
        // Error occured
        print "Report download failed";
    }
} catch (Exception $e) {
    print_r($e);
    print $client->__getLastRequest() . "\n";
    print $client->__getLastResponse() . "\n";
}
?>
Extracting the Completed Report

Comments

I'm keen to get feedback on my posts, so if you have any questions or comments, then please send me a message and I'll be happy to help.

Allan

Hi Ewan, another great post. Thanks for this. Just a little inquiry. Is there any possible way to get the "budget spent" for a month without using the Reporting Service? Becuase it takes a very long time for the script to finish.

Reply

Ewan

Hi Allan,

The only way I know of is to use the GetAccountMonthlySpend Operation of the adCenter API Customer Billing Service; however, unfortunately that only works with accounts that use invoice based billing, not the standard credit card or PayPal methods that most small advertisers have access to.

Regards,

Ewan

Reply

TJ

Ewan, I'm playing with your example code and keep getting this error. Any thoughts on why this is happening?

[faultstring] => Invalid client data. Check the SOAP fault details for more information [faultcode] => s:Server [detail] => stdClass Object ( [AdApiFaultDetail] => stdClass Object ( [TrackingId] => 833b7221-fed1-41e8-af6d-13ae096f98e6 [Errors] => stdClass Object ( [AdApiError] => stdClass Object ( [Code] => 105 [Detail] => [ErrorCode] => InvalidCredentials [Message] => Authentication failed. Either supplied credentials are invalid or the account is inactive ) ) ) ) )

Reply

Ewan

Hi TJ,

Sorry for the delay replying; how come you're not using the .NET code I developed for you last year? Anyway, this error looks like you're not entering a valid account id. Also, if you want to use the adCenter API with PHP, you might want to look at the client library I'm developing at http://php-adcenterapi.sourceforge.net. It's currently only working for the reporting, but I'm building functionality for campaign management at the moment.

Regards,

Ewan

Reply

I need to download "existing" reports. Either reports which are created in the web interface of adcenter. Is that possible ?

Silver

Reply

nate

Great post. AdCenter API documentation is pretty extensive, but lacks practical examples.

Reply

Ewan

Thanks, Nate – Yes it takes time to get to grips with their documentation and they currently don’t supply any client libraries (although I’m working on one myself). I intend to post more similar articles when I get time.

Reply