How to use YouTube Reporting API
The most important thing to understand about the YouTube Reporting API is that it’s asynchronous.
Unlike the Analytics API (where you ask a question and get an answer), the Reporting API works in a multi-step process:
- You: Ask YouTube to create a “report job” (e.g., “Please start generating a daily report of my video stats”).
- YouTube: Says “Okay” and, over the next 24-48 hours, starts generating these reports as CSV files.
- You: Periodically check back and ask, “Are any new reports from my job ready?”
- YouTube: “Yes, here is the download URL for yesterday’s report.”
- You: Download the CSV file and import it into your database or spreadsheet.
The code is split into two main parts: (1) Creating the job and (2) Downloading the reports.
🐍 Python Code Examples
You’ll need the Google API client library and the authentication library.
Bash
pip install google-api-python-client google-auth-oauthlib requests
You must first have an OAuth 2.0 client_secrets.json file from your Google Cloud project with the YouTube Reporting API enabled.
Part 1: Creating a Reporting Job
This is a one-time setup. You run this script once to tell YouTube what report you want it to start generating.
Python
import os
import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors
SCOPES = ["https.www.googleapis.com/auth/yt-analytics.readonly"]
API_SERVICE_NAME = "youtubereporting"
API_VERSION = "v1"
CLIENT_SECRETS_FILE = "client_secrets.json" # Your downloaded credentials
def get_authenticated_service():
"""Authenticates the user and returns the YouTube Reporting API service."""
flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
CLIENT_SECRETS_FILE, SCOPES)
credentials = flow.run_local_server(port=0)
return googleapiclient.discovery.build(
API_SERVICE_NAME, API_VERSION, credentials=credentials)
def list_report_types(youtube_reporting):
"""Lists available report types."""
print("Listing available report types...")
results = youtube_reporting.reportTypes().list().execute()
report_types = results.get("reportTypes", [])
if not report_types:
print("No report types found.")
else:
for report_type in report_types:
print(f" ID: {report_type['id']}, Name: {report_type['name']}")
return report_types
def create_reporting_job(youtube_reporting, report_type_id, job_name):
"""Creates a new reporting job."""
print(f"Creating job for report type '{report_type_id}'...")
reporting_job = {
"reportTypeId": report_type_id,
"name": job_name
}
try:
job = youtube_reporting.jobs().create(body=reporting_job).execute()
print("Job created successfully:")
print(f" Job ID: {job['id']}")
print(f" Report Type ID: {job['reportTypeId']}")
print(f" Name: {job['name']}")
print(f" Create Time: {job['createTime']}")
except googleapiclient.errors.HttpError as e:
print(f"An HTTP error {e.resp.status} occurred: {e.content}")
if __name__ == "__main__":
# Remove OAUTHLIB_INSECURE_TRANSPORT warning for local testing
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
service = get_authenticated_service()
# First, see what reports are available
list_report_types(service)
# --- Example: Create a "Basic Channel Video Stats" job ---
# You would get this ID from the list_report_types() output.
# 'channel_basic_a2' is a common one for daily video stats.
REPORT_ID_TO_CREATE = "channel_basic_a2"
JOB_NAME = "Daily Video Stats Job"
create_reporting_job(service, REPORT_ID_TO_CREATE, JOB_NAME)
Part 2: Downloading an Available Report
You would run this script daily (or hourly) to check for and download new report files.
Python
import os
import requests
import google_auth_oauthlib.flow
import googleapiclient.discovery
from io import FileIO
# (Use the same get_authenticated_service() function from Part 1)
# ... (get_authenticated_service code here) ...
def list_jobs(youtube_reporting):
"""Lists all created reporting jobs."""
print("Listing all reporting jobs...")
results = youtube_reporting.jobs().list().execute()
jobs = results.get("jobs", [])
if not jobs:
print("No jobs found.")
else:
for job in jobs:
print(f" Job ID: {job['id']}, Name: {job['name']}, Report Type: {job['reportTypeId']}")
return jobs
def get_latest_report_url(youtube_reporting, job_id):
"""Gets the download URL for the most recent report from a job."""
print(f"Finding reports for job ID: {job_id}...")
results = youtube_reporting.jobs().reports().list(jobId=job_id).execute()
reports = results.get("reports", [])
if not reports:
print("No reports found for this job yet. (It can take 24-48 hours to generate the first one)")
return None
# Reports are listed newest-first, so index 0 is the latest
latest_report = reports[0]
print(f"Found report: {latest_report['id']}")
print(f" Covers period: {latest_report['startTime']} to {latest_report['endTime']}")
print(f" Download URL: {latest_report['downloadUrl']}")
return latest_report['downloadUrl']
def download_report_file(report_url, local_file_name):
"""Downloads the report file from the given URL."""
print(f"Downloading report to '{local_file_name}'...")
# Note: The download URL requires the same OAuth credentials
# For simplicity, this example uses 'requests', but a real app
# would add the auth headers from the 'credentials' object.
# A simple GET request often works for testing.
response = requests.get(report_url)
if response.status_code == 200:
with open(local_file_name, "wb") as f:
f.write(response.content)
print("Download complete.")
else:
print(f"Download failed with status code: {response.status_code}")
print(response.text)
if __name__ == "__main__":
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
service = get_authenticated_service()
# First, list your jobs to find the ID you want
jobs = list_jobs(service)
if jobs:
# Use the ID of the job you created in Part 1
JOB_ID_TO_DOWNLOAD = jobs[0]['id'] # Just using the first job for this example
report_url = get_latest_report_url(service, JOB_ID_TO_DOWNLOAD)
if report_url:
download_report_file(report_url, "my_youtube_report.csv")
📊 Typical Output (The CSV File)
The API calls themselves just return JSON about the jobs and reports (like the text you see in the console output). The real output is the .csv file you download.
Here is a sample of what the channel_basic_a2 report (a common one) looks like:
코드 스니펫
date,channel_id,video_id,views,red_views,comments,likes,dislikes,shares,estimated_minutes_watched,average_view_duration,average_view_percentage,subscribers_gained,subscribers_lost,videos_added_to_playlists,videos_removed_from_playlists,annotation_click_through_rate,annotation_close_rate
2025-10-24,UCxxxxxxxxxxxxxxxxxA,VIDEO_ID_ONE,1025,150,5,25,1,3,2840,166,45.2,12,1,3,0,0,0
2025-10-24,UCxxxxxxxxxxxxxxxxxA,VIDEO_ID_TWO,5034,780,42,150,8,15,19870,236,52.1,45,3,10,2,0,0
2025-10-24,UCxxxxxxxxxxxxxxxxxA,VIDEO_ID_THREE,890,40,1,12,0,1,1500,101,30.9,3,0,0,1,0,0
Key takeaways from the output:
- It’s a standard CSV file.
- Each row represents one video for one day.
- It’s incredibly detailed, providing metrics like
views,estimated_minutes_watched,subscribers_gained, andsharesall broken down byvideo_id. - This format is designed to be loaded directly into a database (like Google BigQuery, PostgreSQL, etc.) for analysis.
https://developers.google.com/youtube/reporting/v1/reference/rest/v1/jobs/create#python
