Use Treasure Data's Customer Data Platform to ingest your YouTube Analytics data.


Basic knowledge of

Use from TD Console

You can create the data connector from the TD Console. This is the most common approach.

Create a new connection

Visit the Treasure Data Catalog, search and select YouTube.

The following dialog opens.

Choose one of the Authentication Modes:

Your custom OAuth app is the preferable mode. See this Q&A Section for more information.

If you choose Your custom OAuth app, provide the following parameters:

If you choose OAuth, select an existing OAuth connection, or click Click Here to connect a new account.

Click Continue. Provide a name for your connector. Click Done.

Create a New Source

Click New Source from the saved authentications

Provide your account CMS ID and Channel IDs if you participate in the program. Otherwise, leave the parameters empty.

Choose the Report Type (Video, Playlist, or Channel), and choose the report preset.



Data Placement

Using the Command Line

You can create the data connector from the CLI instead of the TD Console if you want.

Install the prerequisites

Install the latest td tool via Ruby gem:

$ gem install td
$ td --version

There are other install methods. For more information, see Treasure Data Toolbelt.

Create the config file (config.yml)

Example (config.yml)

The following is an example configuration file to request daily basic statistics for all videos on the YouTube channel.

  type: youtube
  client_id: xxxxxxxxxxxxx
  client_secret: xxxxxxxxxxxxx
  access_token: xxxxxxxxxxxxx
  refresh_token: xxxxxxxxxxxxx
  report_type: video
  video_report_preset: basic_statistics
  skip_dimension: false
  dimension_video_basic_statistics: country
  metric_video_basic_statistics: views,comments,likes,dislikes
  begin_date: 2018-01-01
  end_date: 2018-02-01
  duration: 30
  interval: 1
  ignore_empty_playlist: true
  mode: append


Specify the client_id, client_secret, access_token, and refresh_token for authenticating with Google App. For more information see Appendix.

Report Type

Specify the target of retrieving analytics in the report_type parameter:

Choose a Preset or Individual Dimensions and Metrics

A preset is a predefined group of parameters. Following are the available enumerators for this parameter along with the equivalent group of parameters.

For video and channel report_type:


dimension: country
metric: views,comments,likes,dislikes,videosAddedToPlaylists,videosRemovedFromPlaylists,shares,estimatedMinutesWatched,averageViewDuration,averageViewPercentage,annotationClickThroughRate,annotationCloseRate,annotationImpressions,annotationClickableImpressions,annotationClosableImpressions,annotationClicks,annotationCloses,cardClickRate,cardTeaserClickRate,cardImpressions,cardTeaserImpressions,cardClicks,cardTeaserClicks,subscribersGained,subscribersLost


dimension: country
metric: views,comments,likes,dislikes,videosAddedToPlaylists,videosRemovedFromPlaylists,shares,estimatedMinutesWatched,averageViewDuration,averageViewPercentage,annotationClickThroughRate,annotationCloseRate,annotationImpressions,annotationClickableImpressions,annotationClosableImpressions,annotationClicks,annotationCloses,cardClickRate,cardTeaserClickRate,cardImpressions,cardTeaserImpressions,cardClicks,cardTeaserClicks,subscribersGained,subscribersLost,estimatedRevenue,estimatedAdRevenue,grossRevenue,estimatedRedPartnerRevenue,monetizedPlaybacks,playbackBasedCpm,adImpressions,cpm


dimension: province,subscribedStatus
metric: views,redViews,estimatedMinutesWatched,estimatedRedMinutesWatched,averageViewDuration,averageViewPercentage,annotationClickThroughRate,annotationCloseRate,annotationImpressions,annotationClickableImpressions,annotationClosableImpressions,annotationClicks,annotationCloses,cardClickRate,cardTeaserClickRate,cardImpressions,cardTeaserImpressions,cardClicks,cardTeaserClicks
filter: country==US


dimension: country,liveOrOnDemand,subscribedStatus,youtubeProduct
metric: views,estimatedMinutesWatched,averageViewDuration


dimension: province,liveOrOnDemand,subscribedStatus,youtubeProduct
metric: views,redViews,estimatedMinutesWatched,estimatedRedMinutesWatched,averageViewDuration
filter: country==US


dimension: insightPlaybackLocationType,liveOrOnDemand,subscribedStatus
metric: views,estimatedMinutesWatched


dimension: insightPlaybackLocationDetail
metric: views,estimatedMinutesWatched
filter: insightPlaybackLocationType==EMBEDDED
max_results: 25
sort: -views


dimension: insightTrafficSourceType,liveOrOnDemand,subscribedStatus
metric: views,estimatedMinutesWatched


dimension: insightTrafficSourceDetail
metric: views,estimatedMinutesWatched
filter: insightTrafficSourceType==YT_SEARCH
max_results: 25
sort: -views


dimension: deviceType,operatingSystem,liveOrOnDemand,subscribedStatus,youtubeProduct
metric: views,estimatedMinutesWatched


dimension: ageGroup,gender
metric: viewerPercentage


dimension: sharingService,subscribedStatus
metric: shares

audience_retention (only for video)

dimension: elapsedVideoTimeRatio
metric: audienceWatchRatio,relativeRetentionPerformance
filter: audienceType==ORGANIC

For playlist report_type:


dimension: country,subscribedStatus,youtubeProduct
metric: views,estimatedMinutesWatched,averageViewDuration,playlistStarts,viewsPerPlaylistStart,averageTimeInPlaylist
filter: isCurated==1


dimension: province,subscribedStatus,youtubeProduct
metric: views,redViews,estimatedMinutesWatched,estimatedRedMinutesWatched,averageViewDuration,playlistStarts,viewsPerPlaylistStart,averageTimeInPlaylist
filter: isCurated==1;country==US


dimension: insightPlaybackLocationType,subscribedStatus
metric: views,estimatedMinutesWatched,playlistStarts,viewsPerPlaylistStart,averageTimeInPlaylist
filter: isCurated==1


dimension: insightTrafficSourceType,subscribedStatus
metric: views,estimatedMinutesWatched,playlistStarts,viewsPerPlaylistStart,averageTimeInPlaylist
filter: isCurated==1


dimension: deviceType,operatingSystem,subscribedStatus,youtubeProduct
metric: views,estimatedMinutesWatched,playlistStarts,viewsPerPlaylistStart,averageTimeInPlaylist
filter: isCurated==1


dimension: ageGroup,gender
metric: viewerPercentage
filter: isCurated==1

You can find the value for dimension and metric parameters (as well as other required parameters) in the following articles from Google:

Other Parameters


CMS ID (required if your YouTube account is a Content Owner)


List of Channel IDs. Effective only if your account is Content Owner (array, optional, if empty, the plugin fetches analytics from channels that Content Owner has access to). This parameter applies to only content owner reports.


List of Playlist IDs, only effective for `video` report type (array, optional, if blank, the plugin fetches analytics of all videos)


A comma-separated list of YouTube Analytics metrics, such as views or likes, dislikes (string, required if `report_preset` is omitted)


A comma-separated list of YouTube Analytics dimensions (string, optional)


A list of filters that should be applied when retrieving YouTube Analytics data, separated by semi-colons (string, optional)


The maximum number of rows to include in the response, required for only some reports (integer, optional)


A comma-separated list of dimensions or metrics that determine the sort order for YouTube Analytics data. By default, the sort order is ascending. The - prefix causes descending sort order (string, optional)


Incremental loading, for example, when running on schedule, the time window of the fetched data automatically shifts forward on each run (boolean, optional, default: `true`). For example, if the initial config is January 1, with ten days in duration, the first run fetches data modified from January 1 to January 10, the second run fetches from January 11 to January 20, and so on.


The earliest published date of specified content (Report Type) becomes the start date to retrieve analytics (boolean, optional)


The start date to retrieve analytics, supported format: "yyyy-MM-dd" (string, optional). Specify either:

  • load_from_published_date

  • begin_date


The end date to retrieve analytics, supported format: "yyyy-MM-dd" (string, required).


The number of days to retrieve analytics from the current end date in the next incremental run (integer, required in incremental mode).


The number of days to break down analytics (integer, optional, default: `1`, for example, daily)


Indicates whether to include channels' watch time and view data from the time period prior to when the channels were linked to the content owner. The default parameter value is `false` which means importing only watch time and viewing data from the dates that channels were linked to the content owner (boolean, optional, default: `false`). This parameter only applies to content owner reports.


The number of retries before giving up (integer, optional, default: `7`)


The initial waiting duration between retries, in millisecond (integer, optional, default: `30000`, ie. 30 seconds)


The maximum waiting duration between retries, in milliseconds (integer, optional, default: `1800000`, ie. 30 minutes)


Ignore empty playlist instead of throwing error


$ td connector:preview config.yml
| country:string |...| views:double | red_views:double | title:string                                                  | time:timestamp            |
| "JP"           |...| 1.0          | 0.0              | "Hands On: End to End Marketo Integration with Treasure Data" | "2018-07-17 00:00:00 UTC" |
| "US"           |...| 1.0          | 0.0              | "Hands On: End to End Marketo Integration with Treasure Data" | "2018-07-14 00:00:00 UTC" |
| ""             |...| 0.0          | 0.0              | "Hands On: End to End Marketo Integration with Treasure Data" | "2018-07-16 00:00:00 UTC" |
| ""             |...| 0.0          | 0.0              | "Hands On: End to End Marketo Integration with Treasure Data" | "2018-07-15 00:00:00 UTC" |
| "IN"           |...| 0.0          | 0.0              | "Hands On: End to End Marketo Integration with Treasure Data" | "2018-07-19 00:00:00 UTC" |
| "IN"           |...| 1.0          | 0.0              | "Hands On: End to End Marketo Integration with Treasure Data" | "2018-07-13 00:00:00 UTC" |
| "JP"           |...| 0.0          | 0.0              | "Hands On: End to End Marketo Integration with Treasure Data" | "2018-07-13 00:00:00 UTC" |
| "US"           |...| 1.0          | 0.0              | "Hands On: End to End Marketo Integration with Treasure Data" | "2018-07-13 00:00:00 UTC" |
| "US"           |...| 2.0          | 0.0              | "Hands On: End to End Marketo Integration with Treasure Data" | "2018-07-18 00:00:00 UTC" |

Execute Load Job

You must specify the database and table to store the data.

The option --time-column is preferred because Treasure Data partitions the storage by time. If this option is not available, the data connector selects the first long or timestamp column as the partitioning time. The type of the column specified by --time-column must be either of long or timestamp type (use Preview results to check for the available column name and type). A time column is available at the end of the output.

If your data doesn’t have a time column, you can add the column by using the add_time filter option. See details at add_time filter function.

Submit the load job. It may take a couple of hours depending on the data size. You need to specify the database and table to store the data.

$ td connector:issue config.yml \
  --database sample_db \
  --table sample_table

td connector:issue assumes you have already created a database (sample_db) and a table (sample_table). If the database or the table do not exist in TD, td connector:issue will fail. Therefore you must create the database and table manually or use --auto-create-table with td connector:issue to automatically create the database and table:

$ td connector:issue config.yml \
  --database sample_db \
  --table sample_table \

Scheduled execution

You can schedule a periodic data connector execution for a periodic YouTube import. By using this feature, you no longer need a cron daemon on your local data center.

Create the schedule

td connector:create creates a new schedule. The name of the schedule, the cron-style schedule, the database and table to store the data, and the Data Connector configuration file are mandatory.

$ td connector:create \
  daily_youtube_import \
  "10 0 * * *" \
  sample_db \
  sample_table \

The cron parameter also accepts three options: @hourly, @daily, and @monthly. For details, see Scheduled Jobs.

By default, the schedule is set up in the UTC timezone. You can set the timezone using --timezone or -t option. The --timezone option supports only extended timezone formats like Asia/Tokyo, America/Los_Angeles, and so on. Timezone abbreviations like PST, CST are not supported and might lead to unexpected schedules.

List the Schedules

You can see the list of currently scheduled entries by td connector:list

$ td connector:list
  | Name                 | Cron       | Timezone | Delay | Database        | Table        |
  | daily_youtube_import | 10 0 * * * | UTC      | 0     | sample_database | sample_table |

Show the Setting and History of Schedules

td connector:show shows the execution setting of a schedule entry.

$ td connector:show daily_youtube_import
  Name     : daily_youtube_import
  Cron     : 10 0 * * *
  Timezone : UTC
  Delay    : 0
  Database : sample_db
  Table    : sample_table

td connector:history shows the execution history of a schedule entry. To investigate the results of an individual execution, use td job <jobid>.

  | JobID  | Status  | Records | Database  | Table        | Priority | Started                   | Duration |
  | 578066 | success | 20000   | sample_db | sample_table | 0        | 2015-04-18 00:10:05 +0000 | 160      |
  | 577968 | success | 20000   | sample_db | sample_table | 0        | 2015-04-17 00:10:07 +0000 | 161      |
  | 577914 | success | 20000   | sample_db | sample_table | 0        | 2015-04-16 00:10:03 +0000 | 152      |
  | 577872 | success | 20000   | sample_db | sample_table | 0        | 2015-04-15 00:10:04 +0000 | 163      |
  | 577810 | success | 20000   | sample_db | sample_table | 0        | 2015-04-14 00:10:04 +0000 | 164      |
  | 577766 | success | 20000   | sample_db | sample_table | 0        | 2015-04-13 00:10:04 +0000 | 155      |
  | 577710 | success | 20000   | sample_db | sample_table | 0        | 2015-04-12 00:10:05 +0000 | 156      |
  | 577610 | success | 20000   | sample_db | sample_table | 0        | 2015-04-11 00:10:04 +0000 | 157      |

Delete the Schedule

td connector:delete removes the schedule.

$ td connector:delete daily_youtube_import


YouTube Analytics data is based on PST and has a delay of up to 72 hours (

You cannot retrieve analytics for removed Videos and Playlists.

About YouTube API Quotas and Your custom OAuth app

YouTube has the following limitations on the YouTube Analytics API Quotas:

The connector makes many YouTube API calls to ingest daily YouTube analytics. For example, if your channel has 1000 videos, and you want to import 100 days of historical data, the total number of API calls is 1,000x100 = 100,000.

The connector estimates the total number of executed requests before executing further to Youtube API. If the estimation is higher than 100,000, the job stops. You must either increase the Aggregation Period or bring in (make earlier) the End Date. Here is how estimation is calculated.

Let say there are 4 videos:

In the following example, the begin date is the date that the data connector starts to fetch analytics data from YouTube Analytics. The publish date is the date that the video is published in YouTube. The end date is the date that you specify in Treasure Data to end the ingestion session.

If the end date is 2017-06-14, and the begin date is 2010-01-01, then the aggregation period is 7. The formula to calculate total number of calls for 1 video is: End Date - Begin Date + 1 / Aggregation_Period (with begin date being the same date as the published date).

The end date is inclusive, meaning that the analytics data that is available on that end_date is ingested as well. With 4 videos:

The estimation of total requests is: 435 + 326 + 61 = 822 requests which is good because it is still < 100,000 requests.

When using the same connection (see Create a New Connection) for multiple inputs, the quotas will run out quickly, and the import job will stop. A workaround is to create multiple connections with different OAuth apps (see Appendix).


Why I can't get analytics for Content Owner report presets?

Content Owner reports are only available to the accounts that join program. If you are not participating in this YouTube program, retrieving content owner analytics causes an exception.

Why don't the TD analytics for the last day and today match with the one displayed on the YouTube site and YouTube Analytics Dashboard?

Be aware that it takes up to 72 hours for the analytics to accumulate. For more information, see

Why are there discrepancies between the data display on the YouTube Creator Studio Dashboard and the data ingested?

During our detail testing, there are some discrepancies between the analytics displayed on the YouTube Creator Studio and the one ingested. We investigated and found some reasons:

  1. When presenting, Creator Studio reports round some numbers before showing the data on the website.

  2. YouTube Analytics API applies some limitation on the returned analytics. To match with Creator Studio, you may need to remove specific dimensions and filters. For more information, refer to

Why does the basic statistics preset does not include the redViews metric?

During the testing phase, we recognize that if the request contains some combination of dimensions and metrics, the YouTube Analytics API returns an empty record without any further notices or exceptions. For example, if the country and the redViews metrics are in the same request, there will be no analytics returned.

To retrieve the redViews metric, you must remove the country dimension and vice versa. Therefore there is no report presets that has this pair of dimension and metric.

How do I ingest analytics for users reaching Youtube videos through TrueView ?

To retrieve the views count that come from TrueView, use the Preset: Playback Traffic Source Detail but change the parameter Filter from insightTrafficSourceType==YT_SEARCH to insightTrafficSourceType==ADVERTISING. The ingested insightTrafficSourceDetail column will hold the values, including "TrueView in-search and in-display" and "TrueView in-stream". See the list of possible values for insightTrafficSourceType and insightTrafficSourceDetail here:

The default preset Playback Traffic Source Detail retrieves the top 25 search terms that lead to the video or channel. Changing from YT_SEARCH to ADVERTISING results in a different set of values for insightTrafficSourceDetail.

Why is my video, uploaded 1 month ago and published 1 week ago, show only analytic data starting from published date?

The video creation and video upload are the same. However, there is a difference between video creation and publish date. If video is uploaded to Youtube channel, it is still private video and no one is able to watch that video until it is published. There is no interaction data from creation time until published time, therefore, only analytic data from the published time and onward can be returned from Youtube Analytics API.


How to create a custom OAuth app

The following steps show how to set up a custom OAuth app on

Create a new Google project

Follow to create a new project on your Google Cloud Console.

Enable APIs and Services

Follow and enable the following APIs:

Create an OAuth Client ID

Adding to the Authorized redirect URIs helps to get the refresh_token using the OAuth Playground. You don't have to add OAuth Playground if you have other methods.

Save the client id and secret

Copy and save the client id and secret somewhere to use in the next steps.

How to get the refresh_token

The following steps show how to use OAuth Playground to retrieve the refresh_token for creating a YouTube data connection. You don't have to use OAuth Playground if you have other ways to retrieve this information.

Setup Client Id and Secret on OAuth Playground

Go to

Enter the ID and Secret set up in Appendix.

Specify the scopes

Paste the following scopes into Input your own scopes box:

Allow the scopes

Click on Authorize APIs and follow the usual Google steps to log in and allow the scopes.

Request the refresh_token

Click on Exchange authorization code for tokens.

Wait until the process finishes and then copy the Refresh token to use in your Treasure Data YouTube connector configuration.