Bulk Download Requests Code Example

This example demonstrates how to download campaigns, ad groups, and ads in a .csv file using the Bulk service.

Tip

Use the language selector in the documentation header to choose C#, Java, Php, or Python.

To get access and refresh tokens for your Microsoft Advertising user and make your first service call using the Bing Ads API, see the Quick Start guide. You'll want to review the Get Started guide and walkthroughs for your preferred language e.g., C#, Java, Php, and Python.

Supporting files for C#, Java, Php, and Python examples are available at GitHub. You can clone each repository or repurpose snippets as needed.

using System;
using System.Globalization;
using System.Linq;
using System.ServiceModel;
using System.Threading.Tasks;
using Microsoft.BingAds;
using Microsoft.BingAds.V13.Bulk;
using Microsoft.BingAds.V13.Bulk.Entities;
using System.Threading;
using System.Collections.Generic;

namespace BingAdsExamplesLibrary.V13
{
    /// <summary>
    /// How to download entities such as campaigns and ads with the Bulk service.
    /// </summary>
    public class BulkServiceManagerDemo : BulkExampleBase
    {
        public override string Description
        {
            get { return "Bulk Service Manager Download Demo | Bulk V13"; }
        }

        public async override Task RunAsync(AuthorizationData authorizationData)
        {
            try
            {
                ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment;
                
                BulkServiceManager = new BulkServiceManager(
                    authorizationData: authorizationData,
                    apiEnvironment: environment);
                BulkServiceManager.StatusPollIntervalInMilliseconds = 5000;

                // Track download or upload progress

                var progress = new Progress<BulkOperationProgressInfo>(x =>
                    OutputStatusMessage(string.Format("{0} % Complete",
                        x.PercentComplete.ToString(CultureInfo.InvariantCulture))));

                // Some BulkServiceManager operations can be cancelled after a time interval. 

                var tokenSource = new CancellationTokenSource();
                tokenSource.CancelAfter(TimeoutInMilliseconds);
                
                // Download all campaigns, ad groups, and ads in the account.

                var entities = new[] {
                    DownloadEntity.Campaigns,
                    DownloadEntity.AdGroups,
                    DownloadEntity.Ads,
                };

                // DownloadParameters is used for Option A below.
                var downloadParameters = new DownloadParameters
                {
                    CampaignIds = null,
                    DataScope = DataScope.EntityData | DataScope.QualityScoreData,
                    DownloadEntities = entities,
                    FileType = FileType,
                    LastSyncTimeInUTC = null,
                    ResultFileDirectory = FileDirectory,
                    ResultFileName = DownloadFileName,
                    OverwriteResultFile = true
                };

                // SubmitDownloadParameters is used for Option B and Option C below.
                var submitDownloadParameters = new SubmitDownloadParameters
                {
                    CampaignIds = null,
                    DataScope = DataScope.EntityData | DataScope.QualityScoreData,
                    DownloadEntities = entities,
                    FileType = FileType,
                    LastSyncTimeInUTC = null
                };

                // Option A - Background Completion with BulkServiceManager
                // You can submit a download or upload request and the BulkServiceManager will automatically 
                // return results. The BulkServiceManager abstracts the details of checking for result file 
                // completion, and you don't have to write any code for results polling.

                OutputStatusMessage("-----\nAwaiting Background Completion with DownloadFileAsync...");
                await BackgroundCompletionAsync(
                    downloadParameters: downloadParameters,
                    progress: progress,
                    cancellationToken: tokenSource.Token);

                // Alternatively we can use DownloadEntitiesAsync if we want to work with the entities in memory.
                // If you enable this option the result file from BackgroundCompletionAsync will also be deleted
                // if written to the same working directory.
                OutputStatusMessage("-----\nAwaiting Background Completion with DownloadEntitiesAsync...");
                var downloadEntities = await DownloadEntitiesAsync(
                    downloadParameters: downloadParameters,
                    progress: progress,
                    cancellationToken: tokenSource.Token);

                // Option B - Submit and Download with BulkServiceManager
                // Submit the download request and then use the BulkDownloadOperation result to 
                // track status until the download is complete e.g. either using
                // TrackAsync or GetStatusAsync.

                OutputStatusMessage("-----\nAwaiting Submit, Track, and Download...");
                await SubmitTrackDownloadAsync(
                    submitDownloadParameters: submitDownloadParameters,
                    progress: progress,
                    cancellationToken: tokenSource.Token);

                // A second variation of Option B. 
                // See SubmitTrackDownloadAsync for details. 

                OutputStatusMessage("-----\nAwaiting Submit, Poll, and Download...");
                await SubmitTrackDownloadAsync(
                    submitDownloadParameters: submitDownloadParameters,
                    progress: progress,
                    cancellationToken: tokenSource.Token);

                // Option C - Download Results with BulkServiceManager
                // If for any reason you have to resume from a previous application state, 
                // you can use an existing download request identifier and use it 
                // to download the result file. 

                // For example you might have previously retrieved a request ID using SubmitDownloadAsync.

                var bulkDownloadOperation = await BulkServiceManager.SubmitDownloadAsync(
                    parameters: submitDownloadParameters);
                var requestId = bulkDownloadOperation.RequestId;

                // Given the request ID above, you can resume the workflow and download the bulk file.
                // The download request identifier is valid for two days. 
                // If you do not download the bulk file within two days, you must request it again.

                OutputStatusMessage("-----\nAwaiting Download Results...");
                await DownloadResultsAsync(
                    requestId: requestId,
                    authorizationData: authorizationData,
                    progress: progress,
                    cancellationToken: tokenSource.Token);
            }
            // Catch authentication exceptions
            catch (OAuthTokenRequestException ex)
            {
                OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description));
            }
            // Catch Bulk service exceptions
            catch (FaultException<Microsoft.BingAds.V13.Bulk.AdApiFaultDetail> ex)
            {
                OutputStatusMessage(String.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (FaultException<Microsoft.BingAds.V13.Bulk.ApiFaultDetail> ex)
            {
                OutputStatusMessage(String.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
                OutputStatusMessage(String.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (BulkOperationInProgressException ex)
            {
                OutputStatusMessage("The result file for the bulk operation is not yet available for download.");
                OutputStatusMessage(ex.Message);
            }
            catch (BulkOperationCouldNotBeCompletedException<DownloadStatus> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (BulkOperationCouldNotBeCompletedException<UploadStatus> ex)
            {
                OutputStatusMessage(string.Join("; ", ex.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message))));
            }
            catch (Exception ex)
            {
                OutputStatusMessage(ex.Message);
            }
        }

        /// <summary>
        /// Writes the specified entities to a local temporary file prior to upload.  
        /// </summary>
        /// <param name="uploadEntities"></param>
        /// <returns></returns>
        protected async Task<List<BulkEntity>> UploadEntitiesAsync(
            IEnumerable<BulkEntity> uploadEntities,
            Progress<BulkOperationProgressInfo> progress,
            CancellationToken cancellationToken)
        {
            // The system temp directory will be used if another working directory is not specified. If you are 
            // using a cloud service such as Azure you'll want to ensure you do not exceed the file or directory limits. 
            // You can specify a different working directory for each BulkServiceManager instance.

            BulkServiceManager.WorkingDirectory = FileDirectory;
                        
            var entityUploadParameters = new EntityUploadParameters
            {
                Entities = uploadEntities,
                OverwriteResultFile = true,
                ResultFileDirectory = FileDirectory,
                ResultFileName = ResultFileName,
                ResponseMode = ResponseMode.ErrorsAndResults
            };

            // The UploadEntitiesAsync method returns IEnumerable<BulkEntity>, so the result file will not
            // be accessible e.g. for CleanupTempFiles until you iterate over the result e.g. via ToList().

            var resultEntities = (await BulkServiceManager.UploadEntitiesAsync(
                parameters: entityUploadParameters,
                progress: progress,
                cancellationToken: cancellationToken)).ToList();

            // The CleanupTempFiles method removes all files (not sub-directories) within the working directory, 
            // whether or not the files were created by this BulkServiceManager instance. 

            //BulkServiceManager.CleanupTempFiles();

            return resultEntities;
        }

        /// <summary>
        /// Writes the specified entities to a local temporary file after download. 
        /// </summary>
        /// <param name="downloadParameters"></param>
        /// <returns></returns>
        protected async Task<List<BulkEntity>> DownloadEntitiesAsync(
            DownloadParameters downloadParameters,
            Progress<BulkOperationProgressInfo> progress,
            CancellationToken cancellationToken)
        {
            // The system temp directory will be used if another working directory is not specified. If you are 
            // using a cloud service such as Azure you'll want to ensure you do not exceed the file or directory limits. 
            // You can specify a different working directory for each BulkServiceManager instance.

            BulkServiceManager.WorkingDirectory = FileDirectory;

            // The DownloadEntitiesAsync method returns IEnumerable<BulkEntity>, so the download file will not
            // be accessible e.g. for CleanupTempFiles until you iterate over the result e.g. via ToList().

            var resultEntities = (await BulkServiceManager.DownloadEntitiesAsync(
                parameters: downloadParameters,
                progress: progress,
                cancellationToken: cancellationToken)).ToList();

            // The CleanupTempFiles method removes all files (not sub-directories) within the working directory, 
            // whether or not the files were created by this BulkServiceManager instance. 

            //BulkServiceManager.CleanupTempFiles();

            return resultEntities;
        }

        /// <summary>
        /// You can submit a download or upload request and the BulkServiceManager will automatically
        /// return results. The BulkServiceManager abstracts the details of checking for result file
        /// completion, and you don't have to write any code for results polling.
        /// </summary>
        /// <param name="downloadParameters"></param>
        /// <param name="progress"></param>
        /// <returns></returns>
        private async Task BackgroundCompletionAsync(
            DownloadParameters downloadParameters, 
            Progress<BulkOperationProgressInfo> progress,
            CancellationToken cancellationToken)
        {
            var resultFilePath = await BulkServiceManager.DownloadFileAsync(
                parameters: downloadParameters, 
                progress: progress, 
                cancellationToken: cancellationToken);
            OutputStatusMessage(string.Format("Download result file: {0}", resultFilePath));
        }

        /// <summary>
        /// Submit the download request and then use the BulkDownloadOperation result to 
        /// track status until the download is complete using TrackAsync.
        /// </summary>
        /// <param name="submitDownloadParameters"></param>
        /// <returns></returns>
        private async Task SubmitTrackDownloadAsync(
            SubmitDownloadParameters submitDownloadParameters,
            Progress<BulkOperationProgressInfo> progress,
            CancellationToken cancellationToken)
        {
            var bulkDownloadOperation = await BulkServiceManager.SubmitDownloadAsync(submitDownloadParameters);
            
            BulkOperationStatus<DownloadStatus> downloadStatus = await bulkDownloadOperation.TrackAsync(
                progress: progress,
                cancellationToken: cancellationToken);
            
            var resultFilePath = await bulkDownloadOperation.DownloadResultFileAsync(
                localResultDirectoryName: FileDirectory,
                localResultFileName: ResultFileName,
                decompress: true,
                overwrite: true // Set this value true if you want to overwrite the same file.
            );   

            OutputStatusMessage(string.Format("Download result file: {0}", resultFilePath));
        }

        /// <summary>
        /// Submit the download request and then use the BulkDownloadOperation result to 
        /// track status until the download is complete using GetStatusAsync.
        /// </summary>
        /// <param name="submitDownloadParameters"></param>
        /// <returns></returns>
        private async Task SubmitPollDownloadAsync(
            SubmitDownloadParameters submitDownloadParameters)
        {
            var bulkDownloadOperation = await BulkServiceManager.SubmitDownloadAsync(submitDownloadParameters);
            
            BulkOperationStatus<DownloadStatus> downloadStatus;
            var waitTime = new TimeSpan(0, 0, 5);

            for (int i = 0; i < 24; i++)
            {
                Thread.Sleep(waitTime);

                downloadStatus = await bulkDownloadOperation.GetStatusAsync();

                if (downloadStatus.Status == DownloadStatus.Completed)
                {
                    break;
                }
            }

            var resultFilePath = await bulkDownloadOperation.DownloadResultFileAsync(
                localResultDirectoryName: FileDirectory,
                localResultFileName: ResultFileName,
                decompress: true,
                overwrite: true // Set this value true if you want to overwrite the same file.
            );

            OutputStatusMessage(string.Format("Download result file: {0}", resultFilePath));
        }

        /// <summary>
        /// If for any reason you have to resume from a previous application state, 
        /// you can use an existing download request identifier and use it 
        /// to download the result file. Use TrackAsync to indicate that the application 
        /// should wait to ensure that the download status is completed.
        /// </summary>
        /// <param name="requestId"></param>
        /// <param name="authorizationData"></param>
        /// <returns></returns>
        private async Task DownloadResultsAsync(
            string requestId,
            AuthorizationData authorizationData,
            Progress<BulkOperationProgressInfo> progress,
            CancellationToken cancellationToken)
        {
            var bulkDownloadOperation = new BulkDownloadOperation(requestId, authorizationData);

            // Use TrackAsync to indicate that the application should wait to ensure that 
            // the download status is completed.
            var bulkOperationStatus = await bulkDownloadOperation.TrackAsync(
                progress: progress,
                cancellationToken: cancellationToken);

            var resultFilePath = await bulkDownloadOperation.DownloadResultFileAsync(
                localResultDirectoryName: FileDirectory,
                localResultFileName: ResultFileName,
                decompress: true,
                overwrite: true);   // Set this value true if you want to overwrite the same file.

            OutputStatusMessage(string.Format("Download result file: {0}", resultFilePath));
            OutputStatusMessage(string.Format("Status: {0}", bulkOperationStatus.Status));
            OutputStatusMessage(string.Format("TrackingId: {0}", bulkOperationStatus.TrackingId));
        }
    }
}
package com.microsoft.bingads.examples.v13;

import com.microsoft.bingads.v13.bulk.entities.*;
import com.microsoft.bingads.v13.bulk.*;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.net.URISyntaxException;
import java.util.concurrent.TimeoutException;

public class BulkServiceManagerDemo extends BulkExampleBase {
    
    public static void main(String[] args) {
        
        BulkEntityIterable downloadEntities = null;

        try {
            authorizationData = getAuthorizationData();

            BulkServiceManager = new BulkServiceManager(
                    authorizationData, 
                    API_ENVIRONMENT);
            
            BulkServiceManager.setStatusPollIntervalInMilliseconds(5000);
            
            // Download all campaigns, ad groups, and ads in the account.

            ArrayOfDownloadEntity entities = new ArrayOfDownloadEntity();
            entities.getDownloadEntities().add(DownloadEntity.CAMPAIGNS);
            entities.getDownloadEntities().add(DownloadEntity.AD_GROUPS);
            entities.getDownloadEntities().add(DownloadEntity.ADS);

            // Optionally you can request quality score data for the requested bulk records.

            List<DataScope> dataScopes = new ArrayList<DataScope>();
            dataScopes.add(DataScope.ENTITY_DATA);
            dataScopes.add(DataScope.QUALITY_SCORE_DATA);

            // DownloadParameters is used for Option A below.

            DownloadParameters downloadParameters = new DownloadParameters();
            downloadParameters.setCampaignIds(null);
            downloadParameters.setDataScope(dataScopes);
            downloadParameters.setDownloadEntities(entities);
            downloadParameters.setFileType(DownloadFileType.CSV);
            downloadParameters.setLastSyncTimeInUTC(null); 
            downloadParameters.setResultFileDirectory(new File(FileDirectory));
            downloadParameters.setResultFileName(DownloadFileName);
            downloadParameters.setOverwriteResultFile(true);

            // SubmitDownloadParameters is used for Option B and Option C below.

            SubmitDownloadParameters submitDownloadParameters = new SubmitDownloadParameters();
            submitDownloadParameters.setCampaignIds(null);
            submitDownloadParameters.setDataScope(dataScopes);
            submitDownloadParameters.setDownloadEntities(entities);
            submitDownloadParameters.setFileType(DownloadFileType.CSV);
            submitDownloadParameters.setLastSyncTimeInUTC(null); 

            // Option A - Background Completion with BulkServiceManager
            // You can submit a download or upload request and the BulkServiceManager will automatically 
            // return results. The BulkServiceManager abstracts the details of checking for result file 
            // completion, and you don't have to write any code for results polling.

            outputStatusMessage("-----\nAwaiting Background Completion...");
            backgroundCompletionAsync(downloadParameters);

            // Alternatively we can use downloadEntitiesAsync if we want to work with the entities in memory.
            // If you enable this option the result file from BackgroundCompletionAsync will also be deleted
            // if written to the same working directory.
            outputStatusMessage("-----\nAwaiting Background Completion with DownloadEntitiesAsync...");
            List<BulkEntity> resultEntities = downloadEntitiesAsync(downloadParameters);

            // Option B - Submit and Download with BulkServiceManager
            // Submit the download request and then use the BulkDownloadOperation result to 
            // track status until the download is complete e.g. either using
            // trackAsync or getStatusAsync.

            outputStatusMessage("-----\nAwaiting Submit and Download...");
            submitTrackDownloadAsync(submitDownloadParameters);

            // Option C - Download Results with BulkServiceManager
            // If for any reason you have to resume from a previous application state, 
            // you can use an existing download request identifier and use it 
            // to download the result file. Use trackAsync to indicate that the application 
            // should wait to ensure that the download status is completed.

            // For example you might have previously retrieved a request ID using SubmitDownloadAsync.
            BulkDownloadOperation bulkDownloadOperation = BulkServiceManager.submitDownloadAsync(submitDownloadParameters, null).get();
            java.lang.String requestId = bulkDownloadOperation.getRequestId();

            // Given the request ID above, you can resume the workflow and download the bulk file.
            // The download request identifier is valid for two days. 
            // If you do not download the bulk file within two days, you must request it again.
            outputStatusMessage("-----\nAwaiting Download Results...");
            downloadResultsAsync(requestId);
        }
        catch (Exception ex) {
            String faultXml = ExampleExceptionHelper.getBingAdsExceptionFaultXml(ex, System.out);
            outputStatusMessage(faultXml);
            String message = ExampleExceptionHelper.handleBingAdsSDKException(ex, System.out);
            outputStatusMessage(message);
        } 
        finally {
            if (downloadEntities != null){
                try {
                    downloadEntities.close();
                } 
                catch (IOException ex) {
                    outputStatusMessage(ex.getMessage());
                }
            }
        }
    }
    
    // Writes the specified entities to a local temporary file prior to upload.
    static List<BulkEntity> uploadEntitiesAsync(List<BulkEntity> uploadEntities) throws IOException, ExecutionException, InterruptedException 
    {
        // The system temp directory will be used if another working directory is not specified. If you are 
        // using a cloud service such as Microsoft Azure you'll want to ensure you do not exceed the file or directory limits. 
        // You can specify a different working directory for each BulkServiceManager instance.

        BulkServiceManager.setWorkingDirectory(new File(FileDirectory));

        EntityUploadParameters entityUploadParameters = new EntityUploadParameters();
        entityUploadParameters.setEntities(uploadEntities);
        entityUploadParameters.setOverwriteResultFile(true);
        entityUploadParameters.setResultFileDirectory(new File(FileDirectory));
        entityUploadParameters.setResultFileName(ResultFileName);
        entityUploadParameters.setResponseMode(ResponseMode.ERRORS_AND_RESULTS);
        
        // The uploadEntitiesAsync method returns BulkEntityIterable, so the result file will not
        // be accessible e.g. for cleanupTempFiles until you iterate over the result and close the BulkEntityIterable instance.
        
        BulkEntityIterable tempEntities = BulkServiceManager.uploadEntitiesAsync(entityUploadParameters, null, null).get();
        
        ArrayList<BulkEntity> resultEntities = new ArrayList<BulkEntity>();
        for (BulkEntity entity : tempEntities) {
            resultEntities.add(entity);
        }
        
        tempEntities.close();
        
        // The cleanupTempFiles method removes all files (not sub-directories) within the working directory, 
        // whether or not the files were created by this BulkServiceManager instance. 
        
        //BulkServiceManager.cleanupTempFiles();
        
        return resultEntities;
    }
    
    // Writes the specified entities to a local temporary file after download. 
    static List<BulkEntity> downloadEntitiesAsync(DownloadParameters downloadParameters) throws IOException, ExecutionException, InterruptedException 
    {
        // The system temp directory will be used if another working directory is not specified. If you are 
        // using a cloud service such as Microsoft Azure you'll want to ensure you do not exceed the file or directory limits. 
        // You can specify a different working directory for each BulkServiceManager instance.

        BulkServiceManager.setWorkingDirectory(new File(FileDirectory));
        
        // The downloadEntitiesAsync method returns BulkEntityIterable, so the download file will not
        // be accessible e.g. for cleanupTempFiles until you iterate over the result and close the BulkEntityIterable instance.
        
        BulkEntityIterable tempEntities = BulkServiceManager.downloadEntitiesAsync(downloadParameters, null, null).get();
        
        ArrayList<BulkEntity> resultEntities = new ArrayList<BulkEntity>();
        for (BulkEntity entity : tempEntities) {
            resultEntities.add(entity);
        }
        
        tempEntities.close();
        
        // The cleanupTempFiles method removes all files (not sub-directories) within the working directory, 
        // whether or not the files were created by this BulkServiceManager instance. 
        
        //BulkServiceManager.cleanupTempFiles();
        
        return resultEntities;
    }
    
    // You can submit a download or upload request and the BulkServiceManager will automatically 
    // return results. The BulkServiceManager abstracts the details of checking for result file 
    // completion, and you don't have to write any code for results polling.
    private static void backgroundCompletionAsync(DownloadParameters downloadParameters) 
        throws ExecutionException, InterruptedException, TimeoutException 
    {
        
        // You may optionally cancel the downloadFileAsync operation after a specified time interval. 
        File resultFile = BulkServiceManager.downloadFileAsync(downloadParameters, null).get(TimeoutInMilliseconds, TimeUnit.MILLISECONDS);

        outputStatusMessage(String.format("Download result file: %s", resultFile.getName()));
    }
    
    // Submit the download request and then use the BulkDownloadOperation result to 
    // track status until the download is complete using trackAsync.
    private static void submitTrackDownloadAsync(SubmitDownloadParameters submitDownloadParameters) 
        throws ExecutionException, InterruptedException, URISyntaxException, IOException, TimeoutException 
    {
        BulkDownloadOperation bulkDownloadOperation = BulkServiceManager.submitDownloadAsync(submitDownloadParameters, null).get();

        // You may optionally cancel the trackAsync operation after a specified time interval.
        BulkOperationStatus<DownloadStatus> downloadStatus = 
            bulkDownloadOperation.trackAsync(null).get(TimeoutInMilliseconds, TimeUnit.MILLISECONDS);
        
        File resultFile = bulkDownloadOperation.downloadResultFileAsync(
            new File(FileDirectory),
            ResultFileName,
            true, // Set this value to true if you want to decompress the ZIP file.
            true,  // Set this value true if you want to overwrite the named file.
            null).get();

        outputStatusMessage(String.format("Download result file: %s", resultFile.getName()));
    }
    
    // Submit the download request and then use the BulkDownloadOperation result to 
    // track status until the download is complete using getStatusAsync.
    private static void submitPollDownloadAsync(SubmitDownloadParameters submitDownloadParameters) 
        throws ExecutionException, InterruptedException, URISyntaxException, IOException, TimeoutException 
    {
        BulkDownloadOperation bulkDownloadOperation = BulkServiceManager.submitDownloadAsync(submitDownloadParameters, null).get();

    BulkOperationStatus<DownloadStatus> downloadStatus;
        
    for (int i = 0; i < 24; i++)
    {
        Thread.sleep(5000);
        downloadStatus = bulkDownloadOperation.getStatusAsync(null).get(TimeoutInMilliseconds, TimeUnit.MILLISECONDS);
        if (downloadStatus.getStatus() == DownloadStatus.COMPLETED)
        {
            break;
        }
    }

        File resultFile = bulkDownloadOperation.downloadResultFileAsync(
            new File(FileDirectory),
            ResultFileName,
            true, // Set this value to true if you want to decompress the ZIP file.
            true,  // Set this value true if you want to overwrite the named file.
            null).get();

        outputStatusMessage(String.format("Download result file: %s", resultFile.getName()));
    }
    
    // If for any reason you have to resume from a previous application state, 
    // you can use an existing download request identifier and use it 
    // to download the result file. Use trackAsync to indicate that the application 
    // should wait to ensure that the download status is completed.
    private static void downloadResultsAsync(java.lang.String requestId) 
        throws ExecutionException, InterruptedException, URISyntaxException, IOException, TimeoutException 
    {

        BulkDownloadOperation bulkDownloadOperation = new BulkDownloadOperation(requestId, authorizationData, API_ENVIRONMENT);

        bulkDownloadOperation.setStatusPollIntervalInMilliseconds(5000);

        // You can use trackAsync to poll until complete as shown here, 
        // or use custom polling logic with getStatusAsync.
        
        // You may optionally cancel the trackAsync operation after a specified time interval.
        BulkOperationStatus<DownloadStatus> downloadStatus = 
                        bulkDownloadOperation.trackAsync(null).get(TimeoutInMilliseconds, TimeUnit.MILLISECONDS);

        File resultFile = bulkDownloadOperation.downloadResultFileAsync(
            new File(FileDirectory),
            ResultFileName,
            true, // Set this value to true if you want to decompress the ZIP file
            true,  // Set this value true if you want to overwrite the named file.
            null).get();

        outputStatusMessage(String.format("Download result file: %s", resultFile.getName()));
        outputStatusMessage(String.format("Status: %s", downloadStatus.getStatus()));
        outputStatusMessage(String.format("TrackingId: %s", downloadStatus.getTrackingId()));
    }
}
<?php

namespace Microsoft\BingAds\Samples\V13;

// For more information about installing and using the Bing Ads PHP SDK, 
// see https://go.microsoft.com/fwlink/?linkid=838593.

require_once __DIR__ . "/../vendor/autoload.php";

include __DIR__ . "/AuthHelper.php";
include __DIR__ . "/BulkExampleHelper.php";

use SoapVar;
use SoapFault;
use Exception;
use DateTime;
use ZipArchive;

// Specify the Microsoft\BingAds\V13\Bulk classes that will be used.
use Microsoft\BingAds\V13\Bulk\DownloadEntity;
use Microsoft\BingAds\V13\Bulk\DataScope;
use Microsoft\BingAds\V13\Bulk\DownloadFileType;
use Microsoft\BingAds\V13\Bulk\Date;
use Microsoft\BingAds\V13\Bulk\ResponseMode;

// Specify the Microsoft\BingAds\Auth classes that will be used.
use Microsoft\BingAds\Auth\ServiceClient;
use Microsoft\BingAds\Auth\ServiceClientType;

// Specify the Microsoft\BingAds\Samples classes that will be used.
use Microsoft\BingAds\Samples\V13\AuthHelper;
use Microsoft\BingAds\Samples\V13\BulkExampleHelper;

// The full path to the bulk file.

$BulkFilePath = "c:\\bulk\\campaigns.zip";

// The full path to the previously extracted bulk file.
// This file is used to access the last sync time.

$ExtractedFilePath = "c:\\bulk\\extracted\\accounts.csv";

// The full path to the upload result file.

$UploadResultFilePath = "c:\\bulk\\uploadresults.zip";

// Specifies the bulk file format.

$DownloadFileType = DownloadFileType::Csv;

// Confirm that the download folder exist; otherwise, exit.

$length = strrpos($BulkFilePath, '\\');
$folder = substr($BulkFilePath, 0, $length);

if (!is_dir($folder))
{
    printf("The output folder, %s, does not exist.\r\nEnsure that the " .
        "folder exists and try again.", $folder);
    return;
}

try
{
    // Authenticate user credentials and set the account ID for the sample.  
    AuthHelper::Authenticate();

    $accountIds = array();
    $accountIds[] = $GLOBALS['AuthorizationData']->AccountId;

    $dataScope = DataScope::EntityData;
    
    $downloadEntities = array (
        DownloadEntity::Ads,
        DownloadEntity::AdGroups,
        DownloadEntity::Campaigns
    );

    $formatVersion = "6.0";
    
    $lastSyncTimeInUTC = GetLastSyncTime($ExtractedFilePath);
    
    // The request ID will be used to poll for status before downloading the bulk file.
    
    print("-----\r\nDownloadCampaignsByAccountIds:\r\n");
    $downloadRequestId = BulkExampleHelper::DownloadCampaignsByAccountIds(
        $accountIds,
        null, // By default the compression type is Zip
        $dataScope,
        $downloadEntities,
        $DownloadFileType,
        $formatVersion,
        $lastSyncTimeInUTC,
        null
    )->DownloadRequestId;
    
    $waitTime = 5 * 1; 
        
    if ($downloadRequestId != null)
    {
        printf("Download Request Id: %s\r\n", $downloadRequestId);

        $downloadSuccess = false;
    
        // This sample polls every 30 seconds up to 5 minutes.
        // In production you may poll the status every 1 to 2 minutes for up to one hour.
        // If the call succeeds, stop polling. If the call or 
        // download fails, the call throws a fault.
    
        for ($i = 0; $i < 10; $i++)
        {
            sleep($waitTime);
    
            // GetDownloadRequestStatus helper method calls the corresponding Bing Ads service operation 
            // to get the download status.
        
            print("-----\r\nGetBulkDownloadStatus:\r\n");
            $getBulkDownloadStatusResponse = BulkExampleHelper::GetBulkDownloadStatus(
                $downloadRequestId
            );
            $requestStatus = $getBulkDownloadStatusResponse->RequestStatus;
            $resultFileUrl = $getBulkDownloadStatusResponse->ResultFileUrl;
            printf("PercentComplete: %s\r\n", $getBulkDownloadStatusResponse->PercentComplete);
            printf("RequestStatus: %s\r\n", $requestStatus);
            printf("ResultFileUrl: %s\r\n", $resultFileUrl);
    
            if (($requestStatus != null) && ($requestStatus == "Completed"))
            {
                $downloadSuccess = true;
                break;
            }
        }

        if ($downloadSuccess)
        {
            // Download the file.
            printf("-----\r\nDownloading from %s...\r\n", $resultFileUrl);
            DownloadFile($resultFileUrl, $BulkFilePath);
            printf("The download file was written to %s.\r\n", $BulkFilePath);
        }
        else // Pending
        {
            printf("The request is taking longer than expected.\r\n " .
                "Save the download request ID (%s) and try again later.\r\n",
                $downloadRequestId
            );
        }
    }
    
    // You may unzip and update the downloaded bulk file or prepare a new file elsewhere.
    // Changes to the bulk file are not shown here.

    DecompressFile($BulkFilePath, $ExtractedFilePath);
    CompressFile($ExtractedFilePath, $BulkFilePath);
    
    // Use the bulk service to upload a bulk file.
    
    $responseMode = ResponseMode::ErrorsAndResults;
    
    print("-----\r\nGetBulkUploadUrl:\r\n");
    $uploadResponse = BulkExampleHelper::GetBulkUploadUrl(
        $responseMode,
        $GLOBALS['AuthorizationData']->AccountId
    );
    
    $uploadRequestId = $uploadResponse->RequestId;
    $uploadUrl = $uploadResponse->UploadUrl;
    printf("RequestId: %s\r\n", $uploadRequestId);
    printf("UploadUrl: %s\r\n", $uploadUrl);
    
    printf("-----\r\nUploading file from %s.\r\n", $BulkFilePath);  
    $uploadSuccess = UploadFile(
        $uploadUrl, 
        $BulkFilePath
    );
    
    // If the file was not uploaded, do not continue to poll for results.
    if($uploadSuccess == false){
        print "Upload failed.\r\n";
        return;
    }
    else{
        print "Upload succeeded.\r\n";
    }
    
    // This sample polls every 30 seconds up to 5 minutes.
    // In production you may poll the status every 1 to 2 minutes for up to one hour.
    // If the call succeeds, stop polling. If the call or
    // download fails, the call throws a fault.
    
    for ($i = 0; $i < 10; $i++)
    {
        sleep($waitTime);
        
        // Get the upload request status.
        print("-----\r\nGetBulkUploadStatus:\r\n");
        $getBulkUploadStatusResponse = BulkExampleHelper::GetBulkUploadStatus(
            $uploadRequestId
        );

        $requestStatus = $getBulkUploadStatusResponse->RequestStatus;
        $resultFileUrl = $getBulkUploadStatusResponse->ResultFileUrl;
        printf("PercentComplete: %s\r\n", $getBulkUploadStatusResponse->PercentComplete);
        printf("RequestStatus: %s\r\n", $requestStatus);
        printf("ResultFileUrl: %s\r\n", $resultFileUrl);
        
        if (($requestStatus != null) && (($requestStatus == "Completed")
            || ($requestStatus == "CompletedWithErrors")))
        {
            $uploadSuccess = true;
            break;
        }
    }
    
    if ($uploadSuccess)
    {
        // Get the upload result file.
        printf("-----\r\nDownloading the upload result file from %s...\r\n", $resultFileUrl);
        DownloadFile($resultFileUrl, $UploadResultFilePath);
        printf("The upload result file was written to %s.\r\n", $UploadResultFilePath);
    }
    else // Pending
    {
        printf("The request is taking longer than expected.\r\n" +
        "Save the upload ID (%s) and try again later.", $uploadRequestId);
    }
    
}
catch (SoapFault $e)
{
    printf("-----\r\nFault Code: %s\r\nFault String: %s\r\nFault Detail: \r\n", $e->faultcode, $e->faultstring);
    var_dump($e->detail);
    print "-----\r\nLast SOAP request/response:\r\n";
    print $GLOBALS['Proxy']->GetWsdl() . "\r\n";
    print $GLOBALS['Proxy']->GetService()->__getLastRequest()."\r\n";
    print $GLOBALS['Proxy']->GetService()->__getLastResponse()."\r\n";
}
catch (Exception $e)
{
    // Ignore fault exceptions that we already caught.
    if ($e->getPrevious())
    { ; }
    else
    {
        print $e->getCode()." ".$e->getMessage()."\r\n";
        print $e->getTraceAsString()."\r\n";
    }
}

// Using the URL returned by the GetBulkUploadUrl operation,
// POST the bulk file using a HTTP client.

function UploadFile($uploadUrl, $filePath)
{
    date_default_timezone_set("UTC");
    $ch = curl_init($uploadUrl);

    curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);

    if(!isset($GLOBALS['AuthorizationData']))
    {
        throw new Exception("AuthorizationData is not set.");
    }
    
    // Set the authorization headers.
    if(isset($GLOBALS['AuthorizationData']->Authentication) && isset($GLOBALS['AuthorizationData']->Authentication->Type))
    {
        $authorizationHeaders = array();
        $authorizationHeaders[] = "DeveloperToken: " . $GLOBALS['AuthorizationData']->DeveloperToken;
        $authorizationHeaders[] = "CustomerId: " . $GLOBALS['AuthorizationData']->CustomerId;
        $authorizationHeaders[] = "CustomerAccountId: " . $GLOBALS['AuthorizationData']->AccountId;
        
        if(isset($GLOBALS['AuthorizationData']->Authentication->OAuthTokens)) 
        {
            $authorizationHeaders[] = "AuthenticationToken: " . $GLOBALS['AuthorizationData']->Authentication->OAuthTokens->AccessToken;
        }
    }
    else
    {
        throw new Exception("Invalid Authentication Type.");
    }

    curl_setopt($ch, CURLOPT_HTTPHEADER, $authorizationHeaders);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);

    $file = curl_file_create($filePath, "application/zip", "payload.zip");
    curl_setopt($ch, CURLOPT_POSTFIELDS, array("payload" => $file));

    $result = curl_exec($ch);
    $info = curl_getinfo($ch);
    $http_code = $info['http_code'];
              
    if (curl_errno($ch))
    {
        print "Curl Error: " . curl_error($ch) . "\r\n";
    }
    else
    {
        print "Upload Result:\n" . $result . "\r\n";
        print "HTTP Result Code:\n" . $http_code . "\r\n";
        // Whether or not the curl execution failed, the response could include Bing Ads error codes  
        // if the bulk file upload failed, for example in the range of 3220-3227.
        // 
        // {
        //     "TrackingId":"Tracking-Id-Here",
        //     "RequestId":"Request-Id-Here",
        //     "Code":3224,
        //     "ErrorCode":"BulkServiceUrlAlreadyUsedForUpload",
        //     "Message":"The URL has already been used for file upload."
        // }
    }
             
    curl_close($ch);
    
    if($http_code == 200){
        return true;
    }
    else {
        return false;
    }
}

// Using the URL that the GetBulkDownloadStatus operation returned,
// send an HTTP request to get the download data and write it to the specified
// ZIP file.

function DownloadFile($downloadUrl, $filePath)
{
    if (!$reader = fopen($downloadUrl, 'rb'))
    {
        throw new Exception("Failed to open URL " . $downloadUrl . ".");
    }

    if (!$writer = fopen($filePath, 'wb'))
    {
        fclose($reader);
        throw new Exception("Failed to create ZIP file " . $filePath . ".");
    }

    $bufferSize = 100 * 1024;

    while (!feof($reader))
    {
        if (false === ($buffer = fread($reader, $bufferSize)))
        {
            fclose($reader);
            fclose($writer);
            throw new Exception("Read operation from URL failed.");
        }

        if (fwrite($writer, $buffer) === false)
        {
            fclose($reader);
            fclose($writer);
            throw new Exception ("Write operation to ZIP file failed.");
        }
    }

    fclose($reader);
    fflush($writer);
    fclose($writer);
}

// Decompresses a ZIP Archive and writes the contents to the specified file path.

function DecompressFile($fromZipArchive, $toExtractedFile)
{
    $archive = new ZipArchive;

    if ($archive->open($fromZipArchive) === TRUE) {
        $archive->extractTo(dirname($toExtractedFile));
        $archive->close();
    }
    else {
        throw new Exception ("Decompress operation from ZIP file failed.");
    }
}

// Compresses a bulk file to a ZIP Archive.

function CompressFile($fromExtractedFile, $toZipArchive)
{
    $archive = new ZipArchive;

    if ($archive->open($toZipArchive, ZipArchive::OVERWRITE) === TRUE) {
        $archive->addFile($fromExtractedFile, basename($fromExtractedFile));
        $archive->close();
    }
    else {
        throw new Exception ("Compress operation to ZIP file failed.");
    }
}

// Get the time stamp of the last download from the previous
// download file. The SyncTime node contains the time stamp.

function GetLastSyncTime($path)
{
    $lastSyncTime = null;

    if (is_file($path))
    {
        $reader = @fopen($path, "r");

        try
        {
            $syncTimeColumn = 0;

            // The first record contains column header information, for example "Type" and "Sync Time".
            $record = fgets($reader);

            if($record != null)
            {
                $fields = explode(",", $record, 100);
                $column = 0;
                
                do
                {
                    $syncTimeColumn = ($fields[$column] == "Sync Time") ? $column : $syncTimeColumn;
                } while($syncTimeColumn == 0 && (++$column < count($fields)));
            }
            
            // Look for the Account record after any other metadata.

            $isAccount = false;

            do
            {
                $record = fgets($reader);
                $fields = explode(",", $record, 100);
                if($fields[0] == "Account")
                {
                    date_default_timezone_set("UTC");
                    $date = (new DateTime($fields[$syncTimeColumn]))->format('Y-m-d\TH:i:s');
                    $lastSyncTime = ($fields[$syncTimeColumn] != "") ? $date : null;
                    $isAccount = true;
                }
            } while(!$isAccount);
            
            fclose($reader);
            $reader = null;

        }
        catch (Exception $e)
        {
            if (isset($reader))
            {
                fclose($reader);
            }
            throw $e;
        }
    }

    return $lastSyncTime;
}
 
?>
from auth_helper import *
from bulk_service_manager_helper import *
from output_helper import *

# You must provide credentials in auth_helper.py.


def main(authorization_data):

    try:
        # Download all campaigns, ad groups, and ads in the account.
        entities=['Campaigns', 'AdGroups', 'Ads']

        # DownloadParameters is used for Option A below.
        download_parameters = DownloadParameters(
            campaign_ids=None,
            data_scope=['EntityData', 'QualityScoreData'],
            download_entities=entities,
            file_type=FILE_TYPE,
            last_sync_time_in_utc=None,
            result_file_directory = FILE_DIRECTORY, 
            result_file_name = DOWNLOAD_FILE_NAME, 
            overwrite_result_file = True, # Set this value true if you want to overwrite the same file.
            timeout_in_milliseconds=TIMEOUT_IN_MILLISECONDS # You may optionally cancel the download after a specified time interval.
        )
        
        # SubmitDownloadParameters is used for Option B and Option C below.
        submit_download_parameters = SubmitDownloadParameters(
            campaign_ids=None,
            data_scope=['EntityData', 'QualityScoreData'],
            download_entities=entities,
            file_type=FILE_TYPE,
            last_sync_time_in_utc=None
        )

        #Option A - Background Completion with BulkServiceManager
        #You can submit a download request and the BulkServiceManager will automatically 
        #return results. The BulkServiceManager abstracts the details of checking for result file 
        #completion, and you don't have to write any code for results polling.

        output_status_message("-----\nAwaiting Background Completion...")
        background_completion(download_parameters)

        #Option B - Submit and Download with BulkServiceManager
        #Submit the download request and then use the BulkDownloadOperation result to 
        #track status yourself using BulkServiceManager.get_status().

        output_status_message("-----\nAwaiting Submit and Download...")
        submit_and_download(submit_download_parameters)

        #Option C - Download Results with BulkServiceManager
        #If for any reason you have to resume from a previous application state, 
        #you can use an existing download request identifier and use it 
        #to download the result file. Use track() to indicate that the application 
        #should wait to ensure that the download status is completed.

        #For example you might have previously retrieved a request ID using submit_download.
        bulk_download_operation=bulk_service_manager.submit_download(submit_download_parameters)
        request_id=bulk_download_operation.request_id

        #Given the request ID above, you can resume the workflow and download the bulk file.
        #The download request identifier is valid for two days. 
        #If you do not download the bulk file within two days, you must request it again.
        output_status_message("-----\nAwaiting Download Results...")
        download_results(request_id, authorization_data)

    except WebFault as ex:
        output_webfault_errors(ex)
    except Exception as ex:
        output_status_message(ex)

def background_completion(download_parameters):
    '''
    You can submit a download or upload request and the BulkServiceManager will automatically 
    return results. The BulkServiceManager abstracts the details of checking for result file 
    completion, and you don't have to write any code for results polling.
    '''
    global bulk_service_manager
    result_file_path = bulk_service_manager.download_file(download_parameters)
    output_status_message("Download result file: {0}".format(result_file_path))

def submit_and_download(submit_download_parameters):
    '''
    Submit the download request and then use the BulkDownloadOperation result to 
    track status until the download is complete e.g. either using
    BulkDownloadOperation.track() or BulkDownloadOperation.get_status().
    '''
    global bulk_service_manager
    bulk_download_operation = bulk_service_manager.submit_download(submit_download_parameters)

    # You may optionally cancel the track() operation after a specified time interval.
    download_status = bulk_download_operation.track(timeout_in_milliseconds=TIMEOUT_IN_MILLISECONDS)

    # You can use BulkDownloadOperation.track() to poll until complete as shown above, 
    # or use custom polling logic with getStatus() as shown below.
    #for i in range(10):
    #    time.sleep(bulk_service_manager.poll_interval_in_milliseconds / 1000.0)

    #    download_status = bulk_download_operation.get_status()
        
    #    if download_status.status == 'Completed':
    #        break
    
    result_file_path = bulk_download_operation.download_result_file(
        result_file_directory = FILE_DIRECTORY, 
        result_file_name = DOWNLOAD_FILE_NAME, 
        decompress = True, 
        overwrite = True, # Set this value true if you want to overwrite the same file.
        timeout_in_milliseconds=TIMEOUT_IN_MILLISECONDS # You may optionally cancel the download after a specified time interval.
    )
    
    output_status_message("Download result file: {0}".format(result_file_path))

def download_results(request_id, authorization_data):
    '''
    If for any reason you have to resume from a previous application state, 
    you can use an existing download request identifier and use it 
    to download the result file. Use BulkDownloadOperation.track() to indicate that the application 
    should wait to ensure that the download status is completed.
    '''
    bulk_download_operation = BulkDownloadOperation(
        request_id = request_id, 
        authorization_data=authorization_data, 
        poll_interval_in_milliseconds=1000, 
        environment=ENVIRONMENT,
    )

    # Use track() to indicate that the application should wait to ensure that 
    # the download status is completed.
    # You may optionally cancel the track() operation after a specified time interval.
    bulk_operation_status = bulk_download_operation.track(timeout_in_milliseconds=TIMEOUT_IN_MILLISECONDS)
    
    result_file_path = bulk_download_operation.download_result_file(
        result_file_directory = FILE_DIRECTORY, 
        result_file_name = DOWNLOAD_FILE_NAME, 
        decompress = True, 
        overwrite = True, # Set this value true if you want to overwrite the same file.
        timeout_in_milliseconds=TIMEOUT_IN_MILLISECONDS # You may optionally cancel the download after a specified time interval.
    ) 

    output_status_message("Download result file: {0}".format(result_file_path))
    output_status_message("Status: {0}".format(bulk_operation_status.status))

# Main execution
if __name__ == '__main__':

    print("Loading the web service client proxies...")
    
    authorization_data=AuthorizationData(
        account_id=None,
        customer_id=None,
        developer_token=DEVELOPER_TOKEN,
        authentication=None,
    )

    bulk_service_manager=BulkServiceManager(
        authorization_data=authorization_data, 
        poll_interval_in_milliseconds=5000, 
        environment=ENVIRONMENT,
    )
        
    authenticate(authorization_data)
        
    main(authorization_data)

See Also

Get Started with the Bing Ads API