YouTube API Quota
Lumio uses InnerTube — YouTube's own internal API — as the primary transport for chat reception and broadcast discovery. InnerTube costs 0 Data API quota. The YouTube Data API v3 is only used for one-time liveChatId resolution, chat sending, moderation, and channel enrichment.
The YouTube Data API v3 enforces a daily quota of 10,000 units per project (resets at midnight Pacific Time).
Transport Overview
| Transport | Purpose | Quota Cost |
|---|---|---|
| InnerTube (primary) | Chat polling, broadcast discovery, badge/emote enrichment, statistics (likes, views) | 0 |
| gRPC streamList (fallback 1) | Chat reception via persistent server-push connection | 0 |
| REST Data API v3 (fallback 2) | Chat polling when InnerTube and gRPC both fail | 5 units per poll |
Fallback cascade: InnerTube → gRPC (if grpc_fallback_enabled) → REST (if rest_fallback_enabled). Each transition triggers after 3 consecutive failures within 60 seconds. Both fallbacks are disabled by default.
Quota Cost Reference
Official costs per YouTube Data API v3 operation:
| Operation | Endpoint | Cost (units) |
|---|---|---|
| List broadcasts | liveBroadcasts.list | 1 |
| List chat messages | liveChatMessages.list | 5 |
| Send chat message | liveChatMessages.insert | 200 |
| List channels | channels.list | 1 |
| List videos | videos.list | 1 |
| Search | search.list | 100 |
| List subscriptions | subscriptions.list | 1 |
| Transition broadcast | liveBroadcasts.transition | 50 |
The default daily limit is 10,000 units. Higher limits require passing Google's Quota and Compliance Audit.
Quota-Free Operations (InnerTube + gRPC)
These operations cost 0 Data API quota:
| Operation | Transport | Source |
|---|---|---|
| Chat polling | InnerTube get_live_chat | crates/lo-youtube-api/src/innertube/mod.rs |
| Broadcast discovery | InnerTube browse | crates/lo-youtube-api/src/innertube/browse.rs |
| Badge + emote enrichment | InnerTube (inline in chat responses) | crates/lo-youtube-api/src/innertube/parser.rs |
| Like count + view count | InnerTube updated_metadata / player | crates/lo-youtube-api/src/innertube/browse.rs |
| Chat streaming (fallback) | gRPC liveChatMessages.streamList | crates/lo-youtube-api/src/streaming.rs |
InnerTube is unauthenticated — no OAuth token required for chat reception.
Quota-Consuming Operations (Data API v3)
liveChatId Resolution
| Aspect | Details |
|---|---|
| Endpoint | liveBroadcasts.list |
| Cost | 5 units |
| Frequency | Once per broadcast, cached in channel_status table |
| Source | crates/lo-youtube-api/src/innertube/browse.rs resolve_broadcast_chat_ids() |
InnerTube browse returns broadcast IDs but not liveChatId. For newly discovered broadcasts, liveChatId is resolved once via Data API and cached permanently. This call runs regardless of the rest_fallback_enabled setting.
Chat Sending + Moderation
| Operation | Endpoint | Cost | Frequency |
|---|---|---|---|
| Send chat message | liveChatMessages.insert | 200 | Per message sent |
| Ban / timeout user | liveChat/bans | 200 | Per action |
| Delete message | liveChatMessages.delete | 200 | Per action |
These use the user's login OAuth connection (get_provider_token("google")), not the channel connection.
Channel Info Lookup
| Aspect | Details |
|---|---|
| Endpoint | channels.list |
| Cost | 1 unit |
| Source | apps/api/src/routes/connections.rs fetch_channel_info() |
| Trigger | Once per YouTube channel connection (OAuth callback) |
| Daily Units | Negligible (< 10) |
REST Fallback (Disabled by Default)
Only active when both rest_fallback_enabled = true and InnerTube + gRPC have failed:
| Operation | Endpoint | Cost | Interval |
|---|---|---|---|
| Broadcast discovery | liveBroadcasts.list | 1 | Every 60 s |
| Chat polling | liveChatMessages.list | 5 | ~10 s active / 30 s idle |
| Caller | File | Daily Units (per channel, worst case) |
|---|---|---|
| YouTube Worker (broadcast poll) | apps/api/src/workers/youtube.rs | 1,440 |
| YouTube Worker (chat poll) | apps/api/src/workers/youtube.rs | up to 43,200 |
Configuration
[youtube]
# Enable gRPC streamList as first fallback when InnerTube fails
# ENV: LUMIO__YOUTUBE__GRPC_FALLBACK_ENABLED
grpc_fallback_enabled = false # default
# Enable REST polling as last resort when InnerTube + gRPC both fail
# Also gates Data API broadcast discovery fallback
# ENV: LUMIO__YOUTUBE__REST_FALLBACK_ENABLED
rest_fallback_enabled = false # default
Daily Quota Calculator
Default Mode (InnerTube Only)
With both fallbacks disabled (default), quota consumption is minimal:
Per active channel (stream is live):
| Component | Quota per call | Calls | Quota |
|---|---|---|---|
| InnerTube chat polling | 0 | ~28,800/day (3 s interval) | 0 |
| InnerTube broadcast discovery | 0 | 1,440/day (60 s interval) | 0 |
| InnerTube statistics | 0 | 1,440/day (60 s interval) | 0 |
| liveChatId resolution | 5 | 1 per broadcast (cached) | 5 |
| Total | 5 |
Per idle channel (no stream):
| Component | Quota per call | Calls | Quota |
|---|---|---|---|
| InnerTube broadcast discovery | 0 | 1,440/day | 0 |
| Total | 0 |
Example: 1 Channel, 8h Stream (Default InnerTube Mode)
Idle hours (16h): 0 units
Stream hours (8h): 0 units
liveChatId resolution (once): 5 units
─────────
Total: 5 units / 10,000 limit (< 0.1%)
Example: 1 Channel, 8h Stream (REST Fallback Active)
Idle hours (16h): 16 * 60 = 960 units (broadcast poll)
Stream hours (8h): 8 * 1860 = 14,880 units (broadcast + chat poll)
─────────
Total: 15,840 units / 10,000 limit (158% -- OVER QUOTA)
REST fallback is disabled by default for this reason.
Quota Safeguards
| Mechanism | Details |
|---|---|
| InnerTube primary | 0 quota for all chat reception and broadcast discovery |
| Detection | HTTP 403 (REST) or RESOURCE_EXHAUSTED (gRPC) |
| Backoff | 5-minute pause on quota exhaustion (QUOTA_BACKOFF_SECS = 300) |
| Fallback chain | InnerTube → gRPC → REST (each after 3 failures in 60 s) |
| REST toggle | rest_fallback_enabled = false disables REST fallback entirely |
| Shared cache | Copyright Worker and Bot read broadcast status from Redis, not YouTube API |
| Adaptive polling | REST fallback respects polling_interval_millis from YouTube's response |
Requesting Higher Quota
To request a quota increase from Google:
- Pass the Quota and Compliance Audit
- Demonstrate compliance with YouTube API Terms of Service
- If previously audited within 12 months, use the Audited Developer Requests Form
- There is no published maximum — increases are granted case-by-case