YouTube Owner Analytics Platform Architecture Brief

Date: 2026-07-01 Status: investigation only; no OAuth credentials, scripts, schedulers, or skill contracts were created or changed.

Executive Summary

Build a small local-first CLI wrapper, but do it in two layers:

  1. MVP: YouTube Analytics API targeted pulls + YouTube Data API enrichment. This satisfies current youtube-video-audit --owner-analytics <path> usage for views, watch time, average view duration, average view percentage, traffic source, device, geography, subscribers gained/lost, and audience-retention curves where the Analytics API exposes them.
  2. Optional scheduled add-on: YouTube Reporting API jobs. Add this only after the MVP works, primarily for daily bulk history and thumbnail reach metrics (video_thumbnail_impressions, video_thumbnail_impressions_ctr) that are documented in Reporting API channel reach reports but not exposed in the currently documented YouTube Analytics API channel reports.

The wrapper should not try to mirror YouTube Studio wholesale. It should produce auditable local files with raw API evidence, normalized summaries, field provenance, and explicit gaps[] entries. Existing agents can then consume the per-video normalized JSON via --owner-analytics <path> without needing screenshots or immediate skill rewrites.

Current Repo And Skill Data Needs

Verified Current Contracts

The active canonical YouTube pack is packs/youtube-ops/{codex,claude}; there is no active installed .codex/skills/youtube* copy in this checkout. The relevant contracts are:

Implication

The wrapper should output both:

The normalized JSON should include top-level friendly field names, not only a deeply nested schema, because the current audit skill promises flexible CSV/JSON parsing rather than a formal contract.

API Capability Matrix

Need Best API Fit Notes
Public video metadataYouTube Data API v3 videos.listStrongvideos.list returns requested parts such as snippet, contentDetails, statistics, status, topicDetails, and liveStreamingDetails; cost is 1 quota unit per call.
Public commentsYouTube Data API v3 commentThreads.listStrong, when comments are enabledReturns comment threads by videoId or channel; max page size is 100; cost is 1 quota unit per call; disabled/private comments must become gaps.
Owner core metricsYouTube Analytics API reports.queryStrongSupports authorized channel reports with ids=channel==MINE or owned channel ID, start/end dates, metrics, dimensions, filters, and sorting.
Views, watch time, average view duration, average view percentageYouTube Analytics APIStrongOfficial metric definitions include views, estimatedMinutesWatched, averageViewDuration, and averageViewPercentage.
Subscribers gained/lostYouTube Analytics APIStrongsubscribersGained and subscribersLost are core metrics; video-filtered reports only include subscriptions/unsubscriptions from the specified video's watch page.
Traffic sourceYouTube Analytics API; Reporting API for bulkStrong, with privacy thresholdsAnalytics API supports traffic source reports; specific traffic details can be omitted when thresholds are not met.
Device and OSYouTube Analytics API; Reporting API for bulkStrongBoth targeted and bulk reports support device/OS breakdowns for views and watch time.
GeographyYouTube Analytics API; Reporting API for bulkStrong, with privacy thresholdsCountry/province dimensions may be limited or anonymized when thresholds are not met.
Audience retentionYouTube Analytics APIStrong for targeted video pullsAudience retention reports use elapsedVideoTimeRatio with audienceWatchRatio and relativeRetentionPerformance.
Thumbnail impressions and CTRYouTube Reporting API channel reach reports; manual Studio export fallbackPartialReporting API documents channel reach reports with video_thumbnail_impressions and video_thumbnail_impressions_ctr. The currently documented Analytics API metric list does not show general thumbnail impression/CTR metrics.
End screens/cardsAnalytics API and Reporting APIPartialCards/end screens have API metrics, but they are not substitutes for all Studio UI detail.
Revenue/ad metricsAnalytics or Reporting API monetary scopesOut of MVPRequires yt-analytics-monetary.readonly; avoid unless explicitly needed.
Bulk daily snapshotsYouTube Reporting APIStrong after setupReports are versioned CSV files, updated daily, one 24-hour period per report, available 60 days after generation; historical reports for a new job cover only the prior 30 days and are available for 30 days.
Real-time or newest-day accuracyNeither API is instantPartialAnalytics API responses include data only through the last day for which all requested metrics are available. Reporting API starts producing reports within 48 hours after job creation.

Official API Findings

OAuth And Scopes

Use OAuth 2.0, not service accounts. YouTube Data API documentation states that service accounts are not supported for YouTube accounts and will fail with NoLinkedYouTubeAccount.

Minimum scopes for the local wrapper:

Avoid these scopes in MVP:

Google OAuth refresh tokens can stop working when users revoke access, tokens are unused for six months, account/token limits are exceeded, time-based access expires, admin policies apply, or the OAuth consent screen is external/testing. External/testing projects issue refresh tokens that expire in seven days unless only basic profile/email scopes are requested.

Public apps using sensitive or restricted Google user-data scopes may need Google verification before production release, but Google documents development/testing/staging projects as an exception to submission. For a single owner using a local-only tool, keep the OAuth app in testing and expect periodic reauth, or publish/verify only if the tool is shared beyond the owner.

Quotas And Cost

For a single channel, quota risk is low if the CLI batches video filters where possible and avoids repeated Data API searches. The higher risk is a poorly designed historical backfill that issues one query per video per metric family instead of using multi-value video filters or Analytics groups where the selected report supports them.

Data Delay, Retention, And Privacy Limits

Components

yt-owner CLI
  auth module
    browser OAuth flow for one channel owner
    token refresh and revoke handling
  collectors
    data-api collector: videos.list, commentThreads.list
    analytics collector: reports.query targeted pulls
    reporting collector: optional jobs/reports/download CSV
  normalizer
    maps API names to current skill-friendly field aliases
    writes provenance and gaps
  local store
    secrets outside repo or in gitignored local paths
    raw API responses separated from normalized summaries
    agent-readable summaries under research/youtube/data/

Preferred Flow

  1. Owner runs yt-owner auth login --channel <slug> manually. Browser opens for OAuth consent; no agent sees the authorization code or token.
  2. CLI stores refresh/access token material outside the repo by default, with file permissions 0600 and optional macOS Keychain support.
  3. Owner or scheduler runs a pull command.
  4. Collector writes raw API evidence separately from normalized agent summaries.
  5. Normalizer writes per-video JSON that can be passed directly to youtube-video-audit --owner-analytics <path>.
  6. Agents read only normalized paths and raw evidence paths, never token/client-secret paths.

Why This Shape

Proposed CLI Surface

These are proposed command shapes, not implemented commands.

# One-time owner bootstrap.
yt-owner auth login --channel weeklysota --scopes analytics-readonly,youtube-readonly
yt-owner auth status --channel weeklysota
yt-owner auth revoke --channel weeklysota

# Pull one video for an audit date range and print the owner-analytics path.
yt-owner pull video VIDEO_ID \
  --channel weeklysota \
  --start 2026-06-01 \
  --end 2026-06-30 \
  --include core,traffic,device,geo,retention,reach \
  --emit owner-analytics

# Pull channel-level rollups for metrics review.
yt-owner pull channel weeklysota \
  --start 2026-06-01 \
  --end 2026-06-30 \
  --dimensions day,video \
  --include core,traffic,device,geo

# Refresh recent windows before an audit.
yt-owner refresh latest --channel weeklysota --days 30
yt-owner refresh recent-videos --channel weeklysota --published-after 2026-06-01 --days-since-publish 14

# Optional Reporting API setup and harvest.
yt-owner reporting list-types --channel weeklysota
yt-owner reporting ensure-jobs --channel weeklysota --reports channel_basic_a3,channel_traffic_source_a3,channel_device_os_a3,channel_reach_basic_a1
yt-owner reporting harvest --channel weeklysota --created-after last-success

# Diagnostics for agent-safe use.
yt-owner doctor --channel weeklysota
yt-owner paths --channel weeklysota --video VIDEO_ID

Failure behavior should be deterministic:

Proposed File And Data Layout

Keep credentials out of the repo:

~/.config/youtube-owner-analytics/
  clients/<channel-slug>.json
  config.yaml

~/.local/state/youtube-owner-analytics/
  tokens/<channel-slug>.json
  runs/<run-id>.json

Use repo artifacts only for evidence and summaries:

research/youtube/data/
  _owner-private/
    <channel-slug>/
      raw/
        analytics/reports-query/<run-id>/*.json
        reporting/<job-id>/<report-id>.csv
        data-api/<run-id>/*.json
      manifests/
        pull-<run-id>.json
  <channel-slug>/
    owner-analytics/
      channel-summary-YYYY-MM-DD.json
      channel-timeseries-YYYY-MM-DD.jsonl
      videos-YYYY-MM-DD.jsonl
      gaps-YYYY-MM-DD.json
  <video-id>/
    owner-analytics/
      summary-YYYY-MM-DD.json
      timeseries-YYYY-MM-DD.jsonl
      retention-YYYY-MM-DD.json
      traffic-sources-YYYY-MM-DD.jsonl
      device-YYYY-MM-DD.jsonl
      geography-YYYY-MM-DD.jsonl
      source-manifest-YYYY-MM-DD.json

Recommended implementation guard: before writing _owner-private/, the CLI should verify it is gitignored or require --allow-tracked-private-raw. The normalized summaries can be committed only if the owner chooses to treat those metrics as project evidence. The default posture should be local/private because owner analytics are not public YouTube data.

Normalized Evidence Contract

Per-Video Summary JSON

The file passed to youtube-video-audit --owner-analytics should be a JSON object with both direct aliases and structured provenance:

{
  "schema_version": "youtube-owner-analytics.normalized.v0",
  "artifact_type": "video_owner_analytics_summary",
  "generated_at": "2026-07-01T13:00:00Z",
  "channel_slug": "weeklysota",
  "channel_id": "UC...",
  "video_id": "VIDEO_ID",
  "date_range": {
    "start": "2026-06-01",
    "end": "2026-06-30",
    "timezone": "America/Los_Angeles"
  },
  "views": 1234,
  "estimatedMinutesWatched": 4567,
  "watch_time_minutes": 4567,
  "averageViewDuration": 222,
  "averageViewPercentage": 41.2,
  "subscribersGained": 12,
  "subscribersLost": 1,
  "video_thumbnail_impressions": null,
  "video_thumbnail_impressions_ctr": null,
  "trafficSource": [
    {"type": "YT_SEARCH", "views": 400, "estimatedMinutesWatched": 1200}
  ],
  "deviceType": [
    {"deviceType": "MOBILE", "views": 800, "estimatedMinutesWatched": 2500}
  ],
  "geography": [
    {"country": "US", "views": 700, "estimatedMinutesWatched": 2100}
  ],
  "audienceRetention": [
    {"elapsedVideoTimeRatio": 0.0, "audienceWatchRatio": 1.0, "relativeRetentionPerformance": 0.52}
  ],
  "timeSeries": [
    {"date": "2026-06-01", "views": 100, "estimatedMinutesWatched": 300}
  ],
  "provenance": {
    "views": {
      "api": "youtubeAnalytics.reports.query",
      "metric": "views",
      "raw_path": "research/youtube/data/_owner-private/weeklysota/raw/analytics/reports-query/<run-id>/core.json"
    },
    "video_thumbnail_impressions": {
      "api": "youtubeReporting.jobs.reports.download",
      "metric": "video_thumbnail_impressions",
      "raw_path": null
    }
  },
  "gaps": [
    {
      "field": "video_thumbnail_impressions",
      "status": "unavailable",
      "reason": "Reporting API reach job not configured and Analytics API targeted reports reviewed here do not document general thumbnail impression metrics.",
      "fallback": "manual Studio export or enable Reporting API channel_reach_basic_a1 going forward"
    }
  ]
}

Field Mapping

Audit field Analytics API Reporting API Normalized aliases Gap behavior
ViewsviewsviewsviewsGap only if API returns no rows.
Watch timeestimatedMinutesWatchedwatch_time_minutesestimatedMinutesWatched, watch_time_minutesPreserve unit in minutes.
Average view durationaverageViewDurationaverage_view_duration_secondsaverageViewDuration, average_view_duration_secondsPreserve unit in seconds.
Average view percentageaverageViewPercentageaverage_view_duration_percentageaverageViewPercentage, average_view_duration_percentageMark limited if incompatible dimensions omit it.
Subscribers gained/lostsubscribersGained, subscribersLostsubscribers_gained, subscribers_lostboth camelCase and snake_caseFor video-filtered data, note it reflects video watch-page activity only.
Traffic sourceinsightTrafficSourceType, insightTrafficSourceDetailtraffic_source_type, traffic_source_detailtrafficSource[]Threshold-limited rows become NULL/gap entries.
DevicedeviceType, operatingSystemdevice_type, operating_systemdeviceType[], operatingSystem[]Gap if dimension not requested or row suppressed.
Geographycountry, provincecountry_code, province_codegeography[]Mark anonymized/suppressed when thresholds apply.
RetentionelapsedVideoTimeRatio, audienceWatchRatio, relativeRetentionPerformanceNot in basic Reporting API channel reports reviewedaudienceRetention[]Gap if too new, too small, not exposed, or request fails.
Thumbnail impressions/CTRNot documented in Analytics API channel reports reviewedchannel_reach_basic_a1: video_thumbnail_impressions, video_thumbnail_impressions_ctrvideo_thumbnail_impressions, video_thumbnail_impressions_ctr, impressions, clickThroughRateGap until Reporting API reach job exists or manual export supplied.

Evidence Rules

Scheduling Strategy

Manual One-Off Pull Before An Audit

Use Analytics API targeted pulls:

yt-owner pull video VIDEO_ID --channel weeklysota --start 2026-06-01 --end 2026-06-30 --emit owner-analytics

This is the default path for youtube-video-audit. It avoids waiting 48 hours for Reporting API job output and can backfill historical ranges if the Analytics API returns data for the requested dates.

Daily Channel Refresh

Use local scheduling, not GitHub Actions:

Recommended daily job:

yt-owner refresh latest --channel weeklysota --days 45

The refresh window should overlap prior runs because YouTube data can update after first availability. The normalizer should replace same-date normalized rows when a newer raw response/report supersedes an older one.

Recent Upload Refresh

For newly published videos:

Reporting API Schedule

Reporting API is worth enabling only after one of these is true:

If enabled, create jobs once for:

Then harvest new reports daily using createdAfter, track report_id, startTime, endTime, and createTime, and re-import backfills when a newer report replaces a previous period.

Security Model

Secrets

Revocation And Recovery

Failure Scenarios

Scenario Expected behavior
Expired/revoked tokenStop before collection, write failed-run manifest, return auth_required, point to yt-owner auth login --channel <slug>.
API quota exceededStop current run, preserve previous artifacts, mark refresh incomplete, retry only with bounded backoff.
Metric unavailableAdd gaps[] entry with requested metric, API, dimensions, date range, and fallback.
Threshold-suppressed breakdownPreserve available aggregate totals; mark dimension breakdown as limited/anonymized.
Private/deleted/unlisted mismatchRecord Data API/Analytics status separately. Avoid treating missing public metadata as missing owner analytics.
Reporting API job too newState first report is expected within 48 hours; use Analytics API targeted pull meanwhile.
Backfilled report replaces old reportRe-import newer createTime, archive superseded normalized manifest, and update provenance.
Agent attempts to read secretsRefuse and print normalized summary/raw evidence artifact paths only.

Validation Against Current youtube-video-audit

The proposed per-video summary satisfies the existing skill without changes because:

Example invocation after a pull:

$youtube-video-audit https://youtu.be/VIDEO_ID \
  --owner-analytics research/youtube/data/VIDEO_ID/owner-analytics/summary-2026-07-01.json \
  --compare-channel weeklysota

Build / No-Build Recommendation

Build the local wrapper, but keep the MVP small.

Recommended MVP:

Defer:

Reasoning: current repo skills already accept owner analytics as flexible CSV/JSON and tolerate missing private metrics. The highest-value gap is repeatable local normalized evidence, not full Studio parity. Reporting API adds setup delay, bulk CSV import logic, report retention/backfill complexity, and job state management; it is justified only for scheduled accumulation and reach metrics.

Open Questions

Sources

Official documentation reviewed: