Access YouTube Analytics reports and manage analytics groups via managed OAuth, proxied through Maton.
Documents
YouTube Reporting
Try itSchedule bulk YouTube Analytics jobs and download daily channel and playlist reports as CSV through managed OAuth.
What it does
Access the YouTube Reporting API through managed OAuth and bulk-schedule analytics jobs that produce downloadable CSV reports. The skill lists available report types, creates or deletes jobs, lists and retrieves generated reports, and routes CSV downloads through an authenticated proxy. All requests use a Maton API key as a Bearer token against the api.maton.ai base URL, and multiple Google accounts can be disambiguated with a header. Job creation and deletion require explicit user approval before the skill acts.
When to use it
- List channel and playlist report types before scheduling
- Schedule a daily CSV report job for traffic or user activity
- Download a previously generated daily report as CSV
- Manage multiple OAuth connections for different YouTube channels
The skill document
YouTube Reporting
Access the YouTube Reporting API with managed OAuth authentication. Schedule bulk reporting jobs that generate daily downloadable CSV reports containing channel or playlist analytics data.
Quick Start
# List available report types
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/youtube-reporting/v1/reportTypes')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Base URL
https://api.maton.ai/youtube-reporting/{native-api-path}
Maton proxies requests to youtubereporting.googleapis.com and automatically injects your OAuth token.
Authentication
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your YouTube Reporting OAuth connections at https://api.maton.ai.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections?app=youtube-reporting&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Create Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'youtube-reporting'}).encode()
req = urllib.request.Request('https://api.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Get Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "{connection_id}",
"status": "ACTIVE",
"creation_time": "2025-12-08T07:20:53.488460Z",
"last_updated_time": "2026-01-31T20:03:32.593153Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "youtube-reporting",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
Delete Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Specifying Connection
If you have multiple YouTube Reporting connections, specify which one to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/youtube-reporting/v1/reportTypes')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple connections, always include this header to ensure requests go to the intended account.
Security & Permissions
- Access is scoped to the YouTube channel(s) associated with the connected Google account.
- Report data is read-only (downloaded CSV files).
- Job creation and deletion require explicit user approval. Before creating or deleting a reporting job, confirm the report type and intended effect with the user.
API Reference
Report Types
List Report Types
GET /youtube-reporting/v1/reportTypes
Optional Parameters:
| Parameter | Type | Description |
|---|---|---|
pageSize | number | Number of results per page |
pageToken | string | Token for retrieving next page |
includeSystemManaged | boolean | Include system-managed report types (default: false) |
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/youtube-reporting/v1/reportTypes')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"reportTypes": [
{
"id": "channel_basic_a3",
"name": "User activity"
},
{
"id": "channel_demographics_a1",
"name": "Demographics"
},
{
"id": "channel_device_os_a3",
"name": "Device and OS"
},
{
"id": "channel_traffic_source_a3",
"name": "Traffic sources"
}
],
"nextPageToken": "..."
}
Available Channel Report Types:
| Report Type ID | Name |
|---|---|
channel_basic_a3 | User activity |
channel_combined_a3 | Combined |
channel_demographics_a1 | Demographics |
channel_device_os_a3 | Device and OS |
channel_annotations_a1 | Annotations |
channel_cards_a1 | Cards |
channel_end_screens_a1 | End screens |
channel_playback_location_a3 | Playback locations |
channel_province_a3 | Province |
channel_reach_basic_a1 | Reach basic |
channel_reach_combined_a1 | Reach combined |
channel_sharing_service_a1 | Sharing service |
channel_subtitles_a3 | Subtitles |
channel_traffic_source_a3 | Traffic sources |
Available Playlist Report Types:
| Report Type ID | Name |
|---|---|
playlist_basic_a2 | Playlist user activity |
playlist_combined_a2 | Playlist combined |
playlist_device_os_a2 | Playlist device and OS |
playlist_playback_location_a2 | Playlist playback locations |
playlist_province_a2 | Playlist province |
playlist_traffic_source_a2 | Playlist traffic sources |
Jobs
List Jobs
GET /youtube-reporting/v1/jobs
Optional Parameters:
| Parameter | Type | Description |
|---|---|---|
pageSize | number | Number of results per page |
pageToken | string | Token for retrieving next page |
includeSystemManaged | boolean | Include system-managed jobs (default: false) |
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/youtube-reporting/v1/jobs')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"jobs": [
{
"id": "92f0f65f-18c4-4d15-a815-82223ae93ead",
"reportTypeId": "channel_basic_a3",
"name": "Test User Activity Report",
"createTime": "2026-05-04T22:21:48Z"
}
],
"nextPageToken": "..."
}
Get Job
GET /youtube-reporting/v1/jobs/{jobId}
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/youtube-reporting/v1/jobs/{job_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"id": "92f0f65f-18c4-4d15-a815-82223ae93ead",
"reportTypeId": "channel_basic_a3",
"name": "Test User Activity Report",
"createTime": "2026-05-04T22:21:48Z"
}
Create Job
POST /youtube-reporting/v1/jobs
Content-Type: application/json
{
"reportTypeId": "channel_basic_a3",
"name": "My Daily User Activity Report"
}
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
reportTypeId | string | Yes | Report type ID from reportTypes.list |
name | string | Yes | Display name for the job |
Example:
python <<'EOF'
import urllib.request, os, json
data = json.dumps({
"reportTypeId": "channel_basic_a3",
"name": "Daily User Activity"
}).encode()
req = urllib.request.Request('https://api.maton.ai/youtube-reporting/v1/jobs', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"id": "92f0f65f-18c4-4d15-a815-82223ae93ead",
"reportTypeId": "channel_basic_a3",
"name": "Daily User Activity",
"createTime": "2026-05-04T22:21:48.331114Z"
}
Delete Job
DELETE /youtube-reporting/v1/jobs/{jobId}
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/youtube-reporting/v1/jobs/{job_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
resp = urllib.request.urlopen(req)
print(f"Status: {resp.status}")
EOF
Returns empty response on success.
Reports
List Reports for a Job
GET /youtube-reporting/v1/jobs/{jobId}/reports
Optional Parameters:
| Parameter | Type | Description |
|---|---|---|
createdAfter | string | Filter reports created after this timestamp (RFC3339 UTC) |
startTimeAtOrAfter | string | Filter by report data start time (on or after) |
startTimeBefore | string | Filter by report data start time (before) |
pageSize | number | Number of results per page |
pageToken | string | Token for retrieving next page |
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/youtube-reporting/v1/jobs/{job_id}/reports')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"reports": [
{
"id": "report-id-123",
"startTime": "2025-04-01T07:00:00Z",
"endTime": "2025-04-02T07:00:00Z",
"downloadUrl": "https://youtubereporting.googleapis.com/...",
"createTime": "2025-04-02T10:00:00Z"
}
],
"nextPageToken": "..."
}
Get Report
GET /youtube-reporting/v1/jobs/{jobId}/reports/{reportId}
Example:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/youtube-reporting/v1/jobs/{job_id}/reports/{report_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Download Report
Reports provide a downloadUrl pointing at https://youtubereporting.googleapis.com/.... Do not send your MATON_API_KEY to that raw Google host — the key is a Maton credential and must only ever be sent to api.maton.ai. Instead, route the download through the Maton proxy by replacing the Google host with the skill's base URL, so Maton injects the correct Google OAuth token:
python <<'EOF'
import urllib.request, os
# downloadUrl looks like: https://youtubereporting.googleapis.com/v1/media/...
download_url = '{download_url}'
# Rewrite the Google host to the Maton proxy so the request is authenticated
# with your Google OAuth connection (never send MATON_API_KEY to googleapis.com).
proxied_url = download_url.replace(
'https://youtubereporting.googleapis.com/',
'https://api.maton.ai/youtube-reporting/',
)
req = urllib.request.Request(proxied_url)
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
with urllib.request.urlopen(req) as resp:
data = resp.read().decode('utf-8')
print(data[:2000])
EOF
Pagination
All list endpoints use token-based pagination:
GET /youtube-reporting/v1/reportTypes?pageSize=5&pageToken={nextPageToken}
Response includes nextPageToken when more results exist:
{
"reportTypes": [...],
"nextPageToken": "channel_device_os_a3"
}
Pass the nextPageToken value as pageToken in the next request to retrieve subsequent pages.
Code Examples
JavaScript
const response = await fetch(
'https://api.maton.ai/youtube-reporting/v1/jobs',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
console.log(data.jobs);
Python
import os
import requests
# List all jobs
response = requests.get(
'https://api.maton.ai/youtube-reporting/v1/jobs',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
jobs = response.json().get('jobs', [])
# List reports for first job
if jobs:
reports_resp = requests.get(
f'https://api.maton.ai/youtube-reporting/v1/jobs/{jobs[0]["id"]}/reports',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
print(reports_resp.json())
Notes
- Reports are generated daily; the first report is available within 24 hours of job creation
- Report data covers a single day (startTime to endTime spans 24 hours)
- Downloaded reports are CSV files with headers in the first row
- A job with a given
reportTypeIdcan only exist once; creating a duplicate returns 409 Conflict - System-managed jobs are auto-generated by YouTube and cannot be created or deleted (403 Forbidden)
- After deleting a job, previously generated reports remain downloadable for up to 60 days
- IMPORTANT: When using curl commands, use
curl -gwhen URLs contain brackets to disable glob parsing - IMPORTANT: When piping curl output to
jqor other commands, environment variables like$MATON_API_KEYmay not expand correctly in some shell environments
Error Handling
| Status | Meaning |
|---|---|
| 400 | Bad request (missing name or reportTypeId, invalid reportTypeId, deprecated report type) |
| 401 | Invalid or missing Maton API key |
| 403 | Forbidden (cannot create/delete system-managed jobs) |
| 404 | Job or report not found |
| 409 | Conflict (job with same reportTypeId already exists) |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from YouTube Reporting API |
Troubleshooting: API Key Issues
- Check that the
MATON_API_KEYenvironment variable is set:
echo $MATON_API_KEY
- Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Troubleshooting: Invalid App Name
- Ensure your URL path starts with
youtube-reporting. For example:
- Correct:
https://api.maton.ai/youtube-reporting/v1/jobs - Incorrect:
https://api.maton.ai/v1/jobs
Resources
Questions people ask
- How are API requests authenticated?
- All requests use a Bearer token set as the MATON_API_KEY environment variable, sent to the api.maton.ai base URL. When more than one Google account is connected, the skill accepts a Maton-Connection header to pick a specific connection.
- Does this skill write to YouTube?
- No. Report data is read-only. The only writes are creating or deleting reporting jobs, and both require explicit user confirmation before the skill proceeds.
- Can reports be downloaded directly from Google?
- The downloadUrl returned by Google points at youtubereporting.googleapis.com. The skill rewrites that URL to the Maton proxy so the Google OAuth token is injected; sending the MATON_API_KEY directly to Google's host is not supported.
Related skills
Search, read, and manage YouTube videos, playlists, channels, subscriptions, and comments via managed OAuth.
Connect to Google Search Console via a managed OAuth proxy to query search analytics, manage sitemaps, and inspect site performance.
Create and manage Google Apps Script projects, deployments, versions, and remote script execution via an OAuth-managed API.
Query and manage Google BigQuery datasets, tables, and jobs via a managed OAuth API.
Query Google Ads campaigns, keywords, and metrics via GAQL with managed OAuth.