YouTube Reporting API - Data Model

The YouTube Reporting API supports predefined reports that contain a comprehensive set of YouTube Analytics data for a channel or content owner. These reports allow you to download the bulk data sets that you can query with the YouTube Analytics API or in the Analytics section of the Creator Studio.

Overview

Report fields in these reports are characterized as either dimensions or metrics:

  • Dimensions are common criteria that are used to aggregate data, such as the date on which an action occurred or the country where the users were located. In a report, each row of data has a unique combination of dimension values.
  • Metrics are individual measurements related to user activity, ad performance, or estimated revenue. User activity metrics include things like video view counts and ratings (likes and dislikes).

As an example, the basic user activity report for channels contains the following dimensions:

  • date: The date on which the activity occurred.
  • channel_id: The YouTube channel associated with the activity.
  • video_id: The YouTube video associated with the activity.
  • live_or_on_demand: A value that indicates whether viewers were watching a live video stream.
  • subscribed_status: A value that indicates whether the viewers were subscribed to the channel.
  • country_code: The country where the viewers were located.

The report also contains a lot of metrics, such as views, likes, and average_view_duration_seconds. After retrieving and importing the report, an application could make many different calculations based on common dimension values.

Retrieving YouTube Analytics reports

Step 1: Retrieve authorization credentials

All YouTube Reporting API requests must be authorized. The Authorization guide explains how to use the OAuth 2.0 protocol to retrieve authorization tokens.

YouTube Reporting API requests use the following authorization scopes:

Scopes
https://www.googleapis.com/auth/yt-analytics.readonly View YouTube Analytics reports for your YouTube content. This scope provides access to user activity metrics, like view counts and rating counts.
https://www.googleapis.com/auth/yt-analytics-monetary.readonly View YouTube Analytics monetary reports for your YouTube content. This scope provides access to user activity metrics and to estimated revenue and ad performance metrics.

Step 2: Identify the report to retrieve

Call the API's reportTypes.list method to retrieve a list of reports that can be generated for the channel or content owner. The method returns a list of report IDs and names. Capture the id property value for the reports that you want to have generated. For example, the ID of the basic user activity report for channels is channel_basic_a1.

You can also find the report names in the documentation that defines supported channel reports and content owner reports.

Step 3: Create a reporting job

YouTube does not begin to generate your report until you create a reporting job for that report. (As such, reports are only generated for the channels and content owners that actually want to retrieve them.)

To create a reporting job, call the API's jobs.create method. Set the following values in the request body:

  • Set the reportTypeId property's value to the report ID that you retrieved in step 2.
  • Set the name property's value to the name that you want to associate with the report.

The API response to the jobs.create method contains a Job resource, which specifies the ID that uniquely identifies the job. You can start retrieving the report within 48 hours of the time that the job is created, and the first available report will be for the day that you scheduled the job.

For example, if you schedule a job on September 1, 2015, then the report for September 1, 2015, will be ready on September 3, 2015. The report for September 2, 2015, will be posted on September 4, 2015, and so forth.

Step 4: Retrieve the job ID

Note: If your application stored the job ID returned in step 3, then you can skip this step.

Call the jobs.list method to retrieve a list of scheduled jobs. The reportTypeId property in each returned Job resource identifies the type of report that that job generates. Your application needs the id property value from the same resource in the following step.

Step 5: Retrieve the report's download URL

Call the jobs.reports.list method to retrieve a list of reports created for the job. In the request, set the jobId parameter to the job ID of the report that you want to retrieve.

Tip: Use the createdAfter parameter to indicate that the API should only return reports created after a specified time. This parameter can be used to ensure that the API only returns reports that you have not already processed.

The API response contains a list of Report resources for that job. Each resource refers to a report that contains data for a unique 24-hour period. Note that YouTube does generate downloadable reports for days on which no data was available. Those reports contain a header row but do not contain additional data.

  • The resource's startTime and endTime properties identify the time period that the report's data covers.
  • The resource's downloadUrl property identifies the URL from which the report can be fetched.

  • The resource's createTime property specifies the date and time when the report was generated. Your application should store this value and use it to determine whether previously downloaded reports have changed.

Step 6: Download the report

Send an HTTP GET request to the downloadUrl obtained in step 5 to retrieve the report.

Processing reports

Best practices

Applications that use the YouTube Reporting API should always follow these practices:

  • Use a report's header row to determine the ordering of the report's columns. For example, do not assume that views will be the first metric returned in a report just because it is the first metric listed in a report description. Instead, use the report's header row to determine which column contains that data.

  • Keep a record of the reports you have downloaded to avoid repeatedly processing the same report. The following list suggests a couple of ways to do that.

    • When calling the reports.list method, use the createdAfter parameter to only retrieve reports created after a certain date. (Omit the createdAfter parameter the first time you retrieve reports.)

      Each time you retrieve and successfully process reports, store the timestamp corresponding to the date and time when the newest of those reports was created. Then, update the createdAfter parameter value on each successive call to the reports.list method to ensure that you are only retrieving new reports, including new reports with backfilled data, each time you call the API.

      As a safeguard, before retrieving a report, also check to ensure that the report's ID is not already listed in your database.

    • Store the ID for each report that you have downloaded and processed. You can also store additional information like the date and time when each report was generated or the report's startTime and endTime, which together identify the period for which the report contains data. Note that each job will likely have many reports since each report contains data for a 24-hour period.

      Use the report ID to identify reports that you still need to download and import. However, if two new reports have the same startTime and endTime property values, only import the report with the newer createTime value.

Report characteristics

API reports are versioned .csv (comma-separated values) files that have the following characteristics:

  • Each report contains data for a unique 24-hour period lasting from 12:00 a.m. through 11:59 p.m. Pacific time. As such, in any given report, the date dimension value is always the same.

  • Reports are updated daily.

  • YouTube does generate downloadable reports for days on which no data was available. Those reports will contain a header row but will not contain additional data.

  • Reports are available via the API for 180 days from the time that they are generated.

  • Report data is not filtered. As such, a channel report contains all data for a channel's videos or playlists. Similarly, a content owner report contains all data for the content owner's channels (videos, playlists, ad performance, etc).

  • Report data is not sorted.

  • Reports omit rows that do not have metrics. In other words, rows that do not have any metrics are excluded from the report. For example, if a video has no views in Albania on a particular day, that day's report will not contain rows for Albania.

Historical data

After you schedule a reporting job, YouTube generates reports from that day forward and also generates reports covering the 180-day period prior to the time the job was scheduled.

Historical reports are posted as soon as they are available, though it takes roughly one month for all of the historical data to be posted for a job. So, a month after scheduling a reporting job, you will have access to around seven months of data.

Note that historical data is only available as of July 1, 2015. As a result, jobs created before December 28, 2015, will have less than 180 days of historical data.

Backfill data

If YouTube has backfill data, it generates a new report with a new report ID. (In that case, the report's startTime and endTime property values would match the start and end times of a report you had previously downloaded.) Your application should retrieve the new report and update your stored data to match the revised data set. For example, it could delete the previous data for the day and then import the new data set.

Data anonymization

To ensure the anonymity of YouTube viewers, values for some dimensions are returned only if a metric in the same row meets a certain threshold.

For example, in the video traffic source report for channels, each row contains a number of dimensions, including traffic_source_type and traffic_source_detail. Each row also contains various metrics, including views. In rows that describe traffic that originated from a YouTube search, the traffic_source_detail dimension identifies the search term that led to the traffic.

In this example, the following rules apply:

  • The traffic source report identifies the query term (traffic_source_detail) only if it led to at least a certain number of views of a particular video on a particular day. In this case, views is the metric, video_id is the aggregating dimension, and traffic_source_detail is the anonymized dimension.

  • The report includes an additional row that aggregates metrics for all traffic_source_detail values that do not meet the view count threshold. That row reports the total number of views associated with those query terms but does not identify the terms themselves.

The following tables illustrate these rules. The first table contains a hypothetical set of raw data that YouTube would use to generate a traffic source report, and the second table contains the report itself. In this example, the view count threshold is 10, meaning the report only identifies a search term if it led to at least 10 views of a particular video on a particular day. (Actual thresholds are subject to change.)

Raw YouTube search traffic data for a video

Assume that the data below describes YouTube search traffic to a particular video on a particular day.

search term views estimated minutes watched
gangnam style 100 200
psy 15 25
psy gangnam 9 15
oppa gangnam 5 8
horse riding dance 2 5

Sample traffic source report

The following table shows an excerpt from the traffic source report that YouTube would generate for the raw data in the preceding section. (The actual report would contain more dimensions and metrics.) In this example, the report identifies search terms only if they led to at least 10 views. Actual thresholds are subject to change.

In the report's third row, the trafficSourceDetail dimension value is NULL. The views and estimatedMinutesWatched metrics contain the combined views and minutes watched for the three search terms that generated fewer than 10 views.

trafficSourceDetail views estimatedMinutesWatched
gangnam style 100 200
psy 15 25
NULL 16 28

Dimensions subject to anonymization

The following table identifies dimension values that are anonymized if associated metric values do not meet a certain threshold. In each case, the metric's value is aggregated over another dimension. For example, if the metric is views and the aggregating dimension is video_id, then the dimension value is anonymized unless the video video was viewed a certain number of times.

Metric Aggregating dimension(s) Anonymized dimension Anonymized value
subscribers_gained channel_id country_code ZZ
subscribers_gained channel_id province_code US-ZZ
subscribers_lost channel_id country_code ZZ
subscribers_lost channel_id province_code US-ZZ
comments video_id country_code ZZ
comments video_id province_code US-ZZ
likes video_id country_code ZZ
likes video_id province_code US-ZZ
dislikes video_id country_code ZZ
dislikes video_id province_code US-ZZ
views video_id age_group NULL
views video_id gender NULL
views video_id and traffic_source_detail traffic_source_detail NULL
Number of subscribers to channel channel_id subscribed_status NULL

Code samples

The following code samples demonstrate how to use the API to create a reporting job and then retrieve a report for that job. Two code samples are provided for each language:

  1. The first code sample shows how to retrieve a list of available report types and then create a new reporting job.

  2. The second code sample shows how to retrieve a report for a particular job. You can begin retrieving reports within 48 hours of the time that the job is created.

Note: The following code samples may not represent all supported programming languages. See the client libraries documentation for a list of supported languages.

Java

The following samples use the Java client library:

Example 1: Create a reporting job

The following code sample calls the reportTypes.list method to retrieve a list of available report types. It then calls the jobs.create method to create a new reporting job.

/*
 * Copyright (c) 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.api.services.samples.youtube.cmdline.reporting;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.services.samples.youtube.cmdline.Auth;
import com.google.api.services.youtubereporting.YouTubeReporting;
import com.google.api.services.youtubereporting.model.Job;
import com.google.api.services.youtubereporting.model.ListReportTypesResponse;
import com.google.api.services.youtubereporting.model.ReportType;
import com.google.common.collect.Lists;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;

/**
 * This sample creates a reporting job by:
 *
 * 1. Listing the available report types using the "reportTypes.list" method.
 * 2. Creating a reporting job using the "jobs.create" method.
 *
 * @author Ibrahim Ulukaya
 */
public class CreateReportingJob {

    /**
     * Define a global instance of a YouTube Reporting object, which will be used to make
     * YouTube Reporting API requests.
     */
    private static YouTubeReporting youtubeReporting;


    /**
     * Create a reporting job.
     *
     * @param args command line args (not used).
     */
    public static void main(String[] args) {

        /*
         * This OAuth 2.0 access scope allows for read access to the YouTube Analytics monetary reports for
         * authenticated user's account. Any request that retrieves earnings or ad performance metrics must
         * use this scope.
         */
        List<String> scopes = Lists.newArrayList("https://www.googleapis.com/auth/yt-analytics-monetary.readonly");

        try {
            // Authorize the request.
            Credential credential = Auth.authorize(scopes, "createreportingjob");

            // This object is used to make YouTube Reporting API requests.
            youtubeReporting = new YouTubeReporting.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, credential)
                    .setApplicationName("youtube-cmdline-createreportingjob-sample").build();

            // Prompt the user to specify the name of the job to be created.
            String name = getNameFromUser();

            if (listReportTypes()) {
              createReportingJob(getReportTypeIdFromUser(), name);
            }
        } catch (GoogleJsonResponseException e) {
            System.err.println("GoogleJsonResponseException code: " + e.getDetails().getCode()
                    + " : " + e.getDetails().getMessage());
            e.printStackTrace();

        } catch (IOException e) {
            System.err.println("IOException: " + e.getMessage());
            e.printStackTrace();
        } catch (Throwable t) {
            System.err.println("Throwable: " + t.getMessage());
            t.printStackTrace();
        }
    }

    /**
     * Lists report types. (reportTypes.listReportTypes)
     * @return true if at least one report type exists
     * @throws IOException
     */
    private static boolean listReportTypes() throws IOException {
        // Call the YouTube Reporting API's reportTypes.list method to retrieve report types.
        ListReportTypesResponse reportTypesListResponse = youtubeReporting.reportTypes().list()
            .execute();
        List<ReportType> reportTypeList = reportTypesListResponse.getReportTypes();

        if (reportTypeList == null || reportTypeList.isEmpty()) {
          System.out.println("No report types found.");
          return false;
        } else {
            // Print information from the API response.
            System.out.println("\n================== Report Types ==================\n");
            for (ReportType reportType : reportTypeList) {
                System.out.println("  - Id: " + reportType.getId());
                System.out.println("  - Name: " + reportType.getName());
                System.out.println("\n-------------------------------------------------------------\n");
           }
        }
        return true;
    }

    /**
     * Creates a reporting job. (jobs.create)
     *
     * @param reportTypeId Id of the job's report type.
     * @param name name of the job.
     * @throws IOException
     */
    private static void createReportingJob(String reportTypeId, String name)
        throws IOException {
        // Create a reporting job with a name and a report type id.
        Job job = new Job();
        job.setReportTypeId(reportTypeId);
        job.setName(name);

        // Call the YouTube Reporting API's jobs.create method to create a job.
        Job createdJob = youtubeReporting.jobs().create(job).execute();

        // Print information from the API response.
        System.out.println("\n================== Created reporting job ==================\n");
        System.out.println("  - ID: " + createdJob.getId());
        System.out.println("  - Name: " + createdJob.getName());
        System.out.println("  - Report Type Id: " + createdJob.getReportTypeId());
        System.out.println("  - Create Time: " + createdJob.getCreateTime());
        System.out.println("\n-------------------------------------------------------------\n");
    }

    /*
     * Prompt the user to enter a name for the job. Then return the name.
     */
    private static String getNameFromUser() throws IOException {

        String name = "";

        System.out.print("Please enter the name for the job [javaTestJob]: ");
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        name = bReader.readLine();

        if (name.length() < 1) {
            // If nothing is entered, defaults to "javaTestJob".
          name = "javaTestJob";
        }

        System.out.println("You chose " + name + " as the name for the job.");
        return name;
    }

    /*
     * Prompt the user to enter a report type id for the job. Then return the id.
     */
    private static String getReportTypeIdFromUser() throws IOException {

        String id = "";

        System.out.print("Please enter the reportTypeId for the job: ");
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        id = bReader.readLine();

        System.out.println("You chose " + id + " as the report type Id for the job.");
        return id;
    }
}

Example 2: Retrieve a report

The code sample calls the jobs.list method to retrieve a list of reporting jobs. It then calls the reports.list method with the jobId parameter set to a specific job ID to retrieve reports created by that job. Finally, the sample prints out the download URL for each report.

/*
 * Copyright (c) 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.api.services.samples.youtube.cmdline.reporting;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.services.samples.youtube.cmdline.Auth;
import com.google.api.services.youtubereporting.YouTubeReporting;
import com.google.api.services.youtubereporting.YouTubeReporting.Media.Download;
import com.google.api.services.youtubereporting.model.Job;
import com.google.api.services.youtubereporting.model.ListJobsResponse;
import com.google.api.services.youtubereporting.model.ListReportsResponse;
import com.google.api.services.youtubereporting.model.Report;

import com.google.common.collect.Lists;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;

import javax.print.attribute.standard.Media;

/**
 * This sample retrieves reports created by a specific job by:
 *
 * 1. Listing the jobs using the "jobs.list" method.
 * 2. Retrieving reports using the "reports.list" method.
 *
 * @author Ibrahim Ulukaya
 */
public class RetrieveReports {

    /**
     * Define a global instance of a YouTube Reporting object, which will be used to make
     * YouTube Reporting API requests.
     */
    private static YouTubeReporting youtubeReporting;


    /**
     * Retrieve reports.
     *
     * @param args command line args (not used).
     */
    public static void main(String[] args) {

        /*
         * This OAuth 2.0 access scope allows for read access to the YouTube Analytics monetary reports for
         * authenticated user's account. Any request that retrieves earnings or ad performance metrics must
         * use this scope.
         */
        List<String> scopes = Lists.newArrayList("https://www.googleapis.com/auth/yt-analytics-monetary.readonly");

        try {
            // Authorize the request.
            Credential credential = Auth.authorize(scopes, "retrievereports");

            // This object is used to make YouTube Reporting API requests.
            youtubeReporting = new YouTubeReporting.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, credential)
                    .setApplicationName("youtube-cmdline-retrievereports-sample").build();

            if (listReportingJobs()) {
              if(retrieveReports(getJobIdFromUser())) {
                downloadReport(getReportUrlFromUser());
              }
            }
        } catch (GoogleJsonResponseException e) {
            System.err.println("GoogleJsonResponseException code: " + e.getDetails().getCode()
                    + " : " + e.getDetails().getMessage());
            e.printStackTrace();

        } catch (IOException e) {
            System.err.println("IOException: " + e.getMessage());
            e.printStackTrace();
        } catch (Throwable t) {
            System.err.println("Throwable: " + t.getMessage());
            t.printStackTrace();
        }
    }

    /**
     * Lists reporting jobs. (jobs.listJobs)
     * @return true if at least one reporting job exists
     * @throws IOException
     */
    private static boolean listReportingJobs() throws IOException {
        // Call the YouTube Reporting API's jobs.list method to retrieve reporting jobs.
        ListJobsResponse jobsListResponse = youtubeReporting.jobs().list().execute();
        List<Job> jobsList = jobsListResponse.getJobs();

        if (jobsList == null || jobsList.isEmpty()) {
          System.out.println("No jobs found.");
          return false;
        } else {
            // Print information from the API response.
            System.out.println("\n================== Reporting Jobs ==================\n");
            for (Job job : jobsList) {
                System.out.println("  - Id: " + job.getId());
                System.out.println("  - Name: " + job.getName());
                System.out.println("  - Report Type Id: " + job.getReportTypeId());
                System.out.println("\n-------------------------------------------------------------\n");
            }
        }
        return true;
    }

    /**
     * Lists reports created by a specific job. (reports.listJobsReports)
     *
     * @param jobId The ID of the job.
     * @throws IOException
     */
    private static boolean retrieveReports(String jobId)
        throws IOException {
        // Call the YouTube Reporting API's reports.list method
        // to retrieve reports created by a job.
        ListReportsResponse reportsListResponse = youtubeReporting.jobs().reports().list(jobId).execute();
        List<Report> reportslist = reportsListResponse.getReports();

        if (reportslist == null || reportslist.isEmpty()) {
            System.out.println("No reports found.");
            return false;
        } else {
            // Print information from the API response.
            System.out.println("\n============= Reports for the job " + jobId + " =============\n");
            for (Report report : reportslist) {
                System.out.println("  - Id: " + report.getId());
                System.out.println("  - From: " + report.getStartTime());
                System.out.println("  - To: " + report.getEndTime());
                System.out.println("  - Download Url: " + report.getDownloadUrl());
                System.out.println("\n-------------------------------------------------------------\n");
            }
        }
        return true;
    }

    /**
     * Download the report specified by the URL. (media.download)
     *
     * @param reportUrl The URL of the report to be downloaded.
     * @throws IOException
     */
    private static boolean downloadReport(String reportUrl)
        throws IOException {
        // Call the YouTube Reporting API's media.download method to download a report.
        Download request = youtubeReporting.media().download("");
        FileOutputStream fop = new FileOutputStream(new File("report"));
        request.getMediaHttpDownloader().download(new GenericUrl(reportUrl), fop);
        return true;
    }

    /*
     * Prompt the user to enter a job id for report retrieval. Then return the id.
     */
    private static String getJobIdFromUser() throws IOException {

        String id = "";

        System.out.print("Please enter the job id for the report retrieval: ");
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        id = bReader.readLine();

        System.out.println("You chose " + id + " as the job Id for the report retrieval.");
        return id;
    }

    /*
     * Prompt the user to enter a URL for report download. Then return the URL.
     */
    private static String getReportUrlFromUser() throws IOException {

        String url = "";

        System.out.print("Please enter the report URL to download: ");
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        url = bReader.readLine();

        System.out.println("You chose " + url + " as the URL to download.");
        return url;
    }}

PHP

The following samples use the PHP client library.

Example 1: Create a reporting job

The following code sample calls the reportTypes.list method to retrieve a list of available report types. It then calls the jobs.create method to create a new reporting job.

<?php

/**
 * This sample creates a reporting job by:
 *
 * 1. Listing the available report types using the "reportTypes.list" method.
 * 2. Creating a reporting job using the "jobs.create" method.
 *
 * @author Ibrahim Ulukaya
 */

/**
 * Library Requirements
 *
 * 1. Install composer (https://getcomposer.org)
 * 2. On the command line, change to this directory (api-samples/php)
 * 3. Require the google/apiclient library
 *    $ composer require google/apiclient:~2.0
 */
if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
  throw new \Exception('please run "composer require google/apiclient:~2.0" in "' . __DIR__ .'"');
}

require_once __DIR__ . '/vendor/autoload.php';
session_start();

/*
 * You can acquire an OAuth 2.0 client ID and client secret from the
 * Google API Console <https://console.developers.google.com/>
 * For more information about using OAuth 2.0 to access Google APIs, please see:
 * <https://developers.google.com/youtube/v3/guides/authentication>
 * Please ensure that you have enabled the YouTube Data API for your project.
 */
$OAUTH2_CLIENT_ID = 'REPLACE_ME';
$OAUTH2_CLIENT_SECRET = 'REPLACE_ME';

$client = new Google_Client();
$client->setClientId($OAUTH2_CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);

/*
 * This OAuth 2.0 access scope allows for read access to the YouTube Analytics monetary reports for
 * authenticated user's account. Any request that retrieves earnings or ad performance metrics must
 * use this scope.
 */
$client->setScopes('https://www.googleapis.com/auth/yt-analytics-monetary.readonly');
$redirect = filter_var('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'],
    FILTER_SANITIZE_URL);
$client->setRedirectUri($redirect);

// YouTube Reporting object used to make YouTube Reporting API requests.
$youtubeReporting = new Google_Service_YouTubeReporting($client);

// Check if an auth token exists for the required scopes
$tokenSessionKey = 'token-' . $client->prepareScopes();
if (isset($_GET['code'])) {
  if (strval($_SESSION['state']) !== strval($_GET['state'])) {
    die('The session state did not match.');
  }

  $client->authenticate($_GET['code']);
  $_SESSION[$tokenSessionKey] = $client->getAccessToken();
  header('Location: ' . $redirect);
}

if (isset($_SESSION[$tokenSessionKey])) {
  $client->setAccessToken($_SESSION[$tokenSessionKey]);
}

// Check to ensure that the access token was successfully acquired.
if ($client->getAccessToken()) {
  // This code executes if the user enters a name in the form
  // and submits the form. Otherwise, the page displays the form above.
  try {
    if (empty(listReportTypes($youtubeReporting, $htmlBody))) {
      $htmlBody .= sprintf('<p>No report types found.</p>');
    } else if ($_GET['reportTypeId']){
      createReportingJob($youtubeReporting, $_GET['reportTypeId'], $_GET['jobName'], $htmlBody);
    }
  } catch (Google_Service_Exception $e) {
    $htmlBody = sprintf('<p>A service error occurred: <code>%s</code></p>',
        htmlspecialchars($e->getMessage()));
  } catch (Google_Exception $e) {
    $htmlBody = sprintf('<p>An client error occurred: <code>%s</code></p>',
        htmlspecialchars($e->getMessage()));
  }
  $_SESSION[$tokenSessionKey] = $client->getAccessToken();
} elseif ($OAUTH2_CLIENT_ID == 'REPLACE_ME') {
  $htmlBody = <<<END
  <h3>Client Credentials Required</h3>
  <p>
    You need to set <code>\$OAUTH2_CLIENT_ID</code> and
    <code>\$OAUTH2_CLIENT_ID</code> before proceeding.
  <p>
END;
} else {
  // If the user hasn't authorized the app, initiate the OAuth flow
  $state = mt_rand();
  $client->setState($state);
  $_SESSION['state'] = $state;

  $authUrl = $client->createAuthUrl();
  $htmlBody = <<<END
  <h3>Authorization Required</h3>
  <p>You need to <a href="$authUrl">authorize access</a> before proceeding.<p>
END;
}


/**
 * Creates a reporting job. (jobs.create)
 *
 * @param Google_Service_YouTubereporting $youtubeReporting YouTube Reporting service object.
 * @param string $reportTypeId Id of the job's report type.
 * @param string $name name of the job.
 * @param $htmlBody - html body.
 */
function createReportingJob(Google_Service_YouTubeReporting $youtubeReporting, $reportTypeId,
    $name, &$htmlBody) {
  # Create a reporting job with a name and a report type id.
  $reportingJob = new Google_Service_YouTubeReporting_Job();
  $reportingJob->setReportTypeId($reportTypeId);
  $reportingJob->setName($name);

  // Call the YouTube Reporting API's jobs.create method to create a job.
  $jobCreateResponse = $youtubeReporting->jobs->create($reportingJob);

  $htmlBody .= "<h2>Created reporting job</h2><ul>";
  $htmlBody .= sprintf('<li>"%s" for reporting type "%s" at "%s"</li>',
      $jobCreateResponse['name'], $jobCreateResponse['reportTypeId'], $jobCreateResponse['createTime']);
  $htmlBody .= '</ul>';
}


/**
 * Returns a list of report types. (reportTypes.listReportTypes)
 *
 * @param Google_Service_YouTubereporting $youtubeReporting YouTube Reporting service object.
 * @param $htmlBody - html body.
 */
function listReportTypes(Google_Service_YouTubeReporting $youtubeReporting, &$htmlBody) {
  // Call the YouTube Reporting API's reportTypes.list method to retrieve report types.
  $reportTypes = $youtubeReporting->reportTypes->listReportTypes();

  $htmlBody .= "<h3>Report Types</h3><ul>";
  foreach ($reportTypes as $reportType) {
    $htmlBody .= sprintf('<li>id: "%s", name: "%s"</li>', $reportType['id'], $reportType['name']);
  }
  $htmlBody .= '</ul>';

  return $reportTypes;
}
?>

<!doctype html>
<html>
<head>
<title>Create a reporting job</title>
</head>
<body>
  <form method="GET">
    <div>
      Job Name: <input type="text" id="jobName" name="jobName" placeholder="Enter Job Name">
    </div>
    <br>
    <div>
      Report Type Id: <input type="text" id="reportTypeId" name="reportTypeId" placeholder="Enter Report Type Id">
    </div>
    <br>
    <input type="submit" value="Create!">
  </form>
  <?=$htmlBody?>
</body>
</html>

Example 2: Retrieve a report

The code sample calls the jobs.list method to retrieve a list of reporting jobs. It then calls the reports.list method with the jobId parameter set to a specific job ID to retrieve reports created by that job. Finally, the sample prints out the download URL for each report.

<?php

/**
 * This sample retrieves reports created by a specific job by:
 *
 * 1. Listing the jobs using the "jobs.list" method.
 * 2. Retrieving reports using the "reports.list" method.
 *
 * @author Ibrahim Ulukaya
 */

/**
 * Library Requirements
 *
 * 1. Install composer (https://getcomposer.org)
 * 2. On the command line, change to this directory (api-samples/php)
 * 3. Require the google/apiclient library
 *    $ composer require google/apiclient:~2.0
 */
if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
  throw new \Exception('please run "composer require google/apiclient:~2.0" in "' . __DIR__ .'"');
}

require_once __DIR__ . '/vendor/autoload.php';
session_start();


/*
 * You can acquire an OAuth 2.0 client ID and client secret from the
 * Google API Console <https://console.developers.google.com/>
 * For more information about using OAuth 2.0 to access Google APIs, please see:
 * <https://developers.google.com/youtube/v3/guides/authentication>
 * Please ensure that you have enabled the YouTube Data API for your project.
 */
$OAUTH2_CLIENT_ID = 'REPLACE_ME';
$OAUTH2_CLIENT_SECRET = 'REPLACE_ME';

$client = new Google_Client();
$client->setClientId($OAUTH2_CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);

/*
 * This OAuth 2.0 access scope allows for full read/write access to the
 * authenticated user's account.
 */
$client->setScopes('https://www.googleapis.com/auth/yt-analytics-monetary.readonly');
$redirect = filter_var('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'],
    FILTER_SANITIZE_URL);
$client->setRedirectUri($redirect);

// YouTube Reporting object used to make YouTube Reporting API requests.
$youtubeReporting = new Google_Service_YoutubeReporting($client);

// Check if an auth token exists for the required scopes
$tokenSessionKey = 'token-' . $client->prepareScopes();
if (isset($_GET['code'])) {
  if (strval($_SESSION['state']) !== strval($_GET['state'])) {
    die('The session state did not match.');
  }

  $client->authenticate($_GET['code']);
  $_SESSION[$tokenSessionKey] = $client->getAccessToken();
  header('Location: ' . $redirect);
}

if (isset($_SESSION[$tokenSessionKey])) {
  $client->setAccessToken($_SESSION[$tokenSessionKey]);
}

// Check to ensure that the access token was successfully acquired.
if ($client->getAccessToken()) {
  $htmlBody = '';
  try {
    if (empty(listReportingJobs($youtubeReporting, $htmlBody))) {
      $htmlBody .= sprintf('<p>No jobs found.</p>');
    } else if ($_GET['reportUrl']){
      downloadReport($youtubeReporting, $_GET['reportUrl'], $htmlBody);
    } else if ($_GET['jobId']){
      retrieveReports($youtubeReporting, $_GET['jobId'], $htmlBody);
    }
  } catch (Google_Service_Exception $e) {
    $htmlBody .= sprintf('<p>A service error occurred: <code>%s</code></p>',
        htmlspecialchars($e->getMessage()));
  } catch (Google_Exception $e) {
    $htmlBody .= sprintf('<p>An client error occurred: <code>%s</code></p>',
        htmlspecialchars($e->getMessage()));
  }
  $_SESSION[$tokenSessionKey] = $client->getAccessToken();
} elseif ($OAUTH2_CLIENT_ID == 'REPLACE_ME') {
  $htmlBody = <<<END
  <h3>Client Credentials Required</h3>
  <p>
    You need to set <code>\$OAUTH2_CLIENT_ID</code> and
    <code>\$OAUTH2_CLIENT_ID</code> before proceeding.
  <p>
END;
} else {
  // If the user hasn't authorized the app, initiate the OAuth flow
  $state = mt_rand();
  $client->setState($state);
  $_SESSION['state'] = $state;

  $authUrl = $client->createAuthUrl();
  $htmlBody = <<<END
  <h3>Authorization Required</h3>
  <p>You need to <a href="$authUrl">authorize access</a> before proceeding.<p>
END;
}


/**
 * Returns a list of reporting jobs. (jobs.listJobs)
 *
 * @param Google_Service_YouTubereporting $youtubeReporting YouTube Reporting service object.
 * @param $htmlBody - html body.
 */
function listReportingJobs(Google_Service_YouTubeReporting $youtubeReporting, &$htmlBody) {
  // Call the YouTube Reporting API's jobs.list method to retrieve reporting jobs.
  $reportingJobs = $youtubeReporting->jobs->listJobs();

  $htmlBody .= "<h3>Reporting Jobs</h3><ul>";
  foreach ($reportingJobs as $job) {
    $htmlBody .= sprintf('<li>id: "%s", name: "%s" report type: "%s"</li>', $job['id'],
        $job['name'], $job['reportTypeId']);
  }
  $htmlBody .= '</ul>';

  return $reportingJobs;
}


/**
 * Lists reports created by a specific job. (reports.listJobsReports)
 *
 * @param Google_Service_YouTubereporting $youtubeReporting YouTube Reporting service object.
 * @param string $jobId The ID of the job.
 * @param $htmlBody - html body.
 */
function retrieveReports(Google_Service_YouTubeReporting $youtubeReporting, $jobId, &$htmlBody) {
  // Call the YouTube Reporting API's reports.list method to retrieve reports created by a job.
  $reports = $youtubeReporting->jobs_reports->listJobsReports($jobId);

  if (empty($reports)) {
    $htmlBody .= sprintf('<p>No reports found.</p>');
  } else {
    $htmlBody .= sprintf('<h2>Reports for the job "%s"</h2><ul>', $jobId);
    foreach ($reports as $report) {
      $htmlBody .= sprintf('<li>From "%s" to "%s" downloadable at "%s"</li>',
          $report['startTime'], $report['endTime'], $report['downloadUrl']);
      $htmlBody .= '</ul>';
    }
  }
}


/**
 * Download the report specified by the URL. (media.download)
 *
 * @param Google_Service_YouTubereporting $youtubeReporting YouTube Reporting service object.
 * @param string $reportUrl The URL of the report to be downloaded.
 * @param $htmlBody - html body.
 */
function downloadReport(Google_Service_YouTubeReporting $youtubeReporting, $reportUrl, &$htmlBody) {
  $client = $youtubeReporting->getClient();
  // Setting the defer flag to true tells the client to return a request which can be called
  // with ->execute(); instead of making the API call immediately.
  $client->setDefer(true);

  // Call the YouTube Reporting API's media.download method to download a report.
  $request = $youtubeReporting->media->download("");
  $request->setUrl($reportUrl);
  $response = $client->execute($request);

  file_put_contents("reportFile", $response->getResponseBody());
  $client->setDefer(false);
}
?>

<!doctype html>
<html>
<head>
<title>Retrieve reports</title>
</head>
<body>
  <form method="GET">
    <div>
      Job Id: <input type="text" id="jobId" name="jobId" placeholder="Enter Job Id">
    </div>
    <br>
    <div>
      Report URL: <input type="text" id="reportUrl" name="reportUrl" placeholder="Enter Report Url">
    </div>
    <br>    <input type="submit" value="Retrieve!">
  </form>
  <?=$htmlBody?>
</body>
</html>

Python

The following samples use the Python client library.

Example 1: Create a reporting job

The following code sample calls the reportTypes.list method to retrieve a list of available report types. It then calls the jobs.create method to create a new reporting job.

#!/usr/bin/python

# Usage example:
# python create_reporting_job.py --name='<name>'

import httplib2
import os
import sys

from apiclient.discovery import build
from apiclient.errors import HttpError
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
from oauth2client.tools import argparser, run_flow


# The CLIENT_SECRETS_FILE variable specifies the name of a file that contains

# the OAuth 2.0 information for this application, including its client_id and
# client_secret. You can acquire an OAuth 2.0 client ID and client secret from
# the Google API Console at
# https://console.developers.google.com/.
# Please ensure that you have enabled the YouTube Data API for your project.
# For more information about using OAuth2 to access the YouTube Data API, see:
#   https://developers.google.com/youtube/v3/guides/authentication
# For more information about the client_secrets.json file format, see:
#   https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
CLIENT_SECRETS_FILE = "client_secrets.json"

# This OAuth 2.0 access scope allows for read access to the YouTube Analytics monetary reports for
# authenticated user's account. Any request that retrieves earnings or ad performance metrics must
# use this scope.
YOUTUBE_ANALYTICS_MONETARY_READ_SCOPE = (
  "https://www.googleapis.com/auth/yt-analytics-monetary.readonly")
YOUTUBE_REPORTING_API_SERVICE_NAME = "youtubereporting"
YOUTUBE_REPORTING_API_VERSION = "v1"

# This variable defines a message to display if the CLIENT_SECRETS_FILE is
# missing.
MISSING_CLIENT_SECRETS_MESSAGE = """
WARNING: Please configure OAuth 2.0

To make this sample run you will need to populate the client_secrets.json file
found at:
   %s
with information from the APIs Console
https://console.developers.google.com

For more information about the client_secrets.json file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
""" % os.path.abspath(os.path.join(os.path.dirname(__file__),
                                   CLIENT_SECRETS_FILE))

# Authorize the request and store authorization credentials.
def get_authenticated_service(args):
  flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE, scope=YOUTUBE_ANALYTICS_MONETARY_READ_SCOPE,
    message=MISSING_CLIENT_SECRETS_MESSAGE)

  storage = Storage("%s-oauth2.json" % sys.argv[0])
  credentials = storage.get()

  if credentials is None or credentials.invalid:
    credentials = run_flow(flow, storage, args)

  return build(YOUTUBE_REPORTING_API_SERVICE_NAME, YOUTUBE_REPORTING_API_VERSION,
    http=credentials.authorize(httplib2.Http()))


# Call the YouTube Reporting API's reportTypes.list method to retrieve report types.
def list_report_types(youtube_reporting):
  results = youtube_reporting.reportTypes().list().execute()
  reportTypes = results["reportTypes"]

  if "reportTypes" in results and results["reportTypes"]:
    reportTypes = results["reportTypes"]
    for reportType in reportTypes:
      print "Report type id: %s\n name: %s\n" % (reportType["id"], reportType["name"])
  else:
    print "No report types found"
    return False

  return True


# Call the YouTube Reporting API's jobs.create method to create a job.
def create_reporting_job(youtube_reporting, report_type_id, name):
  reporting_job = youtube_reporting.jobs().create(
    body=dict(
      reportTypeId=report_type_id,
      name=name
    )
  ).execute()

  print ("Reporting job '%s' created for reporting type '%s' at '%s'"
         % (reporting_job["name"], reporting_job["reportTypeId"],
             reporting_job["createTime"]))


# Prompt the user to enter a report type id for the job. Then return the id.
def get_report_type_id_from_user():
  report_type_id = raw_input("Please enter the reportTypeId for the job: ")
  print ("You chose '%s' as the report type Id for the job." % report_type_id)
  return report_type_id


if __name__ == "__main__":
  # The "name" option specifies the name that will be used for the reporting job.
  argparser.add_argument("--name",
    help="Required; name for the reporting job.")
  args = argparser.parse_args()

  if not args.name:
    exit("Please specify name using the --name= parameter.")

  youtube_reporting = get_authenticated_service(args)
  try:
    if list_report_types(youtube_reporting):
      create_reporting_job(youtube_reporting, get_report_type_id_from_user(), args.name)
  except HttpError, e:
    print "An HTTP error %d occurred:\n%s" % (e.resp.status, e.content)
  else:
    print "Created reporting job."

Example 2: Retrieve a report

The code sample calls the jobs.list method to retrieve a list of reporting jobs. It then calls the reports.list method with the jobId parameter set to a specific job ID to retrieve reports created by that job. Finally, the sample prints out the download URL for each report.

#!/usr/bin/python

# Usage example:
# python retrieve_reports.py

import httplib2
import os
import sys

from apiclient.discovery import build
from apiclient.errors import HttpError
from apiclient.http import MediaIoBaseDownload
from io import FileIO
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
from oauth2client.tools import argparser, run_flow


# The CLIENT_SECRETS_FILE variable specifies the name of a file that contains

# the OAuth 2.0 information for this application, including its client_id and
# client_secret. You can acquire an OAuth 2.0 client ID and client secret from
# the Google API Console at
# https://console.developers.google.com/.
# Please ensure that you have enabled the YouTube Data API for your project.
# For more information about using OAuth2 to access the YouTube Data API, see:
#   https://developers.google.com/youtube/v3/guides/authentication
# For more information about the client_secrets.json file format, see:
#   https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
CLIENT_SECRETS_FILE = "client_secrets.json"

# This OAuth 2.0 access scope allows for read access to the YouTube Analytics monetary reports for
# authenticated user's account. Any request that retrieves earnings or ad performance metrics must
# use this scope.
YOUTUBE_ANALYTICS_MONETARY_READ_SCOPE = (
  "https://www.googleapis.com/auth/yt-analytics-monetary.readonly")
YOUTUBE_REPORTING_API_SERVICE_NAME = "youtubereporting"
YOUTUBE_REPORTING_API_VERSION = "v1"

# This variable defines a message to display if the CLIENT_SECRETS_FILE is
# missing.
MISSING_CLIENT_SECRETS_MESSAGE = """
WARNING: Please configure OAuth 2.0

To make this sample run you will need to populate the client_secrets.json file
found at:
   %s
with information from the APIs Console
https://console.developers.google.com

For more information about the client_secrets.json file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
""" % os.path.abspath(os.path.join(os.path.dirname(__file__),
                                   CLIENT_SECRETS_FILE))

# Authorize the request and store authorization credentials.
def get_authenticated_service(args):
  flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE, scope=YOUTUBE_ANALYTICS_MONETARY_READ_SCOPE,
    message=MISSING_CLIENT_SECRETS_MESSAGE)

  storage = Storage("%s-oauth2.json" % sys.argv[0])
  credentials = storage.get()

  if credentials is None or credentials.invalid:
    credentials = run_flow(flow, storage, args)

  return build(YOUTUBE_REPORTING_API_SERVICE_NAME, YOUTUBE_REPORTING_API_VERSION,
    http=credentials.authorize(httplib2.Http()))


# Call the YouTube Reporting API's jobs.list method to retrieve reporting jobs.
def list_reporting_jobs(youtube_reporting):
  results = youtube_reporting.jobs().list(
  ).execute()

  if "jobs" in results and results["jobs"]:
    jobs = results["jobs"]
    for job in jobs:
      print ("Reporting job id: %s\n name: %s\n for reporting type: %s\n"
        % (job["id"], job["name"], job["reportTypeId"]))
  else:
    print "No jobs found"
    return False

  return True


# Call the YouTube Reporting API's reports.list method to retrieve reports created by a job.
def retrieve_reports(youtube_reporting, job_id):
  results = youtube_reporting.jobs().reports().list(
    jobId=job_id
  ).execute()

  if "reports" in results and results["reports"]:
    reports = results["reports"]
    for report in reports:
      print ("Report from '%s' to '%s' downloadable at '%s'"
        % (report["startTime"], report["endTime"], report["downloadUrl"]))


# Call the YouTube Reporting API's media.download method to download the report.
def download_report(youtube_reporting, report_url):
  request = youtube_reporting.media().download(
    resourceName=""
  )
  request.uri = report_url
  fh = FileIO('report', mode='wb')
  # Stream/download the report in a single request.
  downloader = MediaIoBaseDownload(fh, request, chunksize=-1)

  done = False
  while done is False:
    status, done = downloader.next_chunk()
    if status:
      print "Download %d%%." % int(status.progress() * 100)
  print "Download Complete!"


# Prompt the user to enter a job id for report retrieval. Then return the id.
def get_job_id_from_user():
  job_id = raw_input("Please enter the job id for the report retrieval: ")
  print ("You chose '%s' as the job Id for the report retrieval." % job_id)
  return job_id


# Prompt the user to enter a report URL for download. Then return the URL.
def get_report_url_from_user():
  report_url = raw_input("Please enter the report URL to download: ")
  print ("You chose '%s' to download." % report_url)
  return report_url

if __name__ == "__main__":
  args = argparser.parse_args()

  youtube_reporting = get_authenticated_service(args)
  try:
    if list_reporting_jobs(youtube_reporting):
      retrieve_reports(youtube_reporting, get_job_id_from_user())
      download_report(youtube_reporting, get_report_url_from_user())
  except HttpError, e:
    print "An HTTP error %d occurred:\n%s" % (e.resp.status, e.content)
  else:
    print "Retrieved reports."