Skip to main content
RapidDev - Software Development Agency
API AutomationsYouTubeAPI Key

How to Automate YouTube Upload Webhooks & Cross-Channel Alerts using the API

Get real-time YouTube upload notifications using PubSubHubbub (PuSH) — the only webhook mechanism YouTube offers, and it costs zero quota units. POST to https://pubsubhubbub.appspot.com/subscribe with your callback URL and channel ID. Your server receives Atom XML on uploads and title/description changes. Subscriptions expire (~10 days) and must be renewed. Use videos.list (1 quota unit) to enrich the notification with full metadata before cross-posting to Discord, Slack, or email.

Need help automating? Talk to an expert
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate7 min read30-60 minutesYouTubeMay 2026RapidDev Engineering Team
TL;DR

Get real-time YouTube upload notifications using PubSubHubbub (PuSH) — the only webhook mechanism YouTube offers, and it costs zero quota units. POST to https://pubsubhubbub.appspot.com/subscribe with your callback URL and channel ID. Your server receives Atom XML on uploads and title/description changes. Subscriptions expire (~10 days) and must be renewed. Use videos.list (1 quota unit) to enrich the notification with full metadata before cross-posting to Discord, Slack, or email.

API Quick Reference

Auth

None (PuSH) / API Key (videos.list enrichment)

Rate limit

PuSH: no quota cost; videos.list: 1 unit per request

Format

Atom XML (PuSH payload); JSON (videos.list response)

SDK

REST only

YouTube PubSubHubbub Webhooks for Upload Notifications

YouTube Data API v3 (https://www.googleapis.com/youtube/v3) is primarily a pull-based REST API, but it does support one push mechanism: PubSubHubbub (PuSH), a webhook protocol based on the W3C WebSub specification. PuSH costs zero quota units — it's completely separate from the 10,000 units/day quota. When a channel uploads a video or changes a video's title/description, the PuSH hub pushes an Atom XML payload to your callback URL.

To receive push notifications, POST a subscription request to https://pubsubhubbub.appspot.com/subscribe with your callback URL and the topic URL for the channel you want to monitor. The hub immediately sends a GET verification request to your callback with a hub.challenge parameter — your server must respond with 200 OK and the challenge value as the raw response body within the verification timeout, or the subscription is rejected.

Subscriptions have a finite lease (~10 days, though Google does not document a fixed number). You must re-subscribe periodically — implement a scheduler that re-POSTs subscriptions before they expire. PuSH only fires on uploads and title/description changes — it does NOT notify on deletes, view count changes, comments, likes, or live stream state changes. The official documentation is at https://developers.google.com/youtube/v3/guides/push_notifications.

Base URLhttps://pubsubhubbub.appspot.com

Setting Up YouTube PubSubHubbub Authentication

PubSubHubbub subscriptions require no authentication — they are publicly accessible. You only need a public HTTPS URL as your callback. For videos.list enrichment calls, use an API key (not OAuth) since you are only reading public video metadata. If you want to monitor private videos or analytics, you would need OAuth, but for public upload notifications an API key is sufficient.

  1. 1Go to console.cloud.google.com and create a project
  2. 2Enable YouTube Data API v3: APIs & Services > Library > YouTube Data API v3 > Enable
  3. 3Create an API key: APIs & Services > Credentials > Create Credentials > API key
  4. 4Optionally restrict the key to YouTube Data API v3 only under Key restrictions
  5. 5Get your callback URL ready — it must be a public HTTPS endpoint (e.g., https://yourapp.com/youtube/callback)
  6. 6For local development, use ngrok or a similar tunneling tool to expose localhost with HTTPS
  7. 7Find the channel ID you want to monitor — it looks like UCxxxxxxxxxxxxxxxxxxxxxxxx and can be found in the channel's About page URL
auth_youtube_push.py
1# Store your API key in environment variable
2import os
3API_KEY = os.environ['YOUTUBE_API_KEY'] # for videos.list enrichment
4
5# No authentication needed for PuSH subscription itself
6# Callback URL must be public HTTPS
7CALLBACK_URL = os.environ['PUSH_CALLBACK_URL'] # e.g. https://yourapp.com/yt/callback

Security notes

  • Store API keys in environment variables — never hardcode in source files
  • Use hub.secret (a shared secret) when subscribing to validate push notification authenticity via HMAC-SHA1 signature
  • Your callback URL must use HTTPS — HTTP callbacks are rejected by the PuSH hub
  • Validate the hub.challenge response carefully — returning anything other than the exact challenge string rejects the subscription
  • Implement idempotency in your callback handler — the PuSH hub may retry delivery on 5xx responses

Key endpoints

POSThttps://pubsubhubbub.appspot.com/subscribe

Subscribe to upload notifications for a YouTube channel. Sends an application/x-www-form-urlencoded body. The hub immediately sends a GET to your callback for verification.

ParameterTypeRequiredDescription
hub.callbackstringrequiredYour public HTTPS callback URL
hub.modestringrequiredsubscribe or unsubscribe
hub.topicstringrequiredhttps://www.youtube.com/xml/feeds/videos.xml?channel_id=CHANNEL_ID
hub.verifystringoptionalsync or async (async is recommended)
hub.lease_secondsnumberoptionalRequested lease duration in seconds (hub may override; ~10 days max)
hub.secretstringoptionalShared secret for HMAC-SHA1 signature validation of push payloads

Request

json
1hub.callback=https://yourapp.com/yt/callback&hub.mode=subscribe&hub.topic=https://www.youtube.com/xml/feeds/videos.xml?channel_id=UCxxxxxxxxxxxxxxxxxxxxxxxx&hub.verify=async&hub.lease_seconds=864000

Response

json
1HTTP 202 Accepted (empty body) verification handshake happens asynchronously via GET to callback
GET{your_callback_url}?hub.mode=subscribe&hub.topic=...&hub.challenge=...&hub.lease_seconds=...

Verification handshake sent by the PuSH hub to your callback immediately after subscription. Your server must respond 200 OK with the hub.challenge value as the raw body.

ParameterTypeRequiredDescription
hub.modestringrequiredsubscribe or unsubscribe
hub.challengestringrequiredRandom string your server must echo back as the response body
hub.lease_secondsnumberoptionalActual lease duration granted by the hub

Response

json
1HTTP 200 OK
2Content-Type: text/plain
3
4{hub.challenge_value}
POST{your_callback_url} (Atom XML payload from hub)

Push notification sent to your callback when a channel uploads a video or changes title/description. Body is Atom XML containing videoId and channelId.

ParameterTypeRequiredDescription
yt:videoIdstringrequiredThe uploaded video ID — use this to call videos.list for full metadata
yt:channelIdstringrequiredThe channel that uploaded the video

Request

json
1<?xml version='1.0' encoding='UTF-8'?><feed xmlns:yt="http://www.youtube.com/xml/schemas/2015" xmlns="http://www.w3.org/2005/Atom"><link rel="hub" href="https://pubsubhubbub.appspot.com"/><entry><yt:videoId>dQw4w9WgXcQ</yt:videoId><yt:channelId>UCxxxxxxxxxxxxxxxxxxxxxxxx</yt:channelId><title>Video Title</title><published>2026-01-15T10:00:00+00:00</published></entry></feed>

Response

json
1Your server must return 2xx to acknowledge. Non-2xx triggers hub retries.
GEThttps://www.googleapis.com/youtube/v3/videos?part=snippet,statistics&id={videoId}&key={apiKey}

Fetches full video metadata after receiving a PuSH notification. The Atom payload only contains videoId, title, and channelId — use this endpoint to get description, tags, thumbnail, view count, and more. Costs 1 quota unit.

ParameterTypeRequiredDescription
idstringrequiredVideo ID from the PuSH notification
partstringrequiredsnippet,statistics for full metadata
keystringrequiredYour YouTube Data API v3 API key

Response

json
1{"items": [{"id": "dQw4w9WgXcQ", "snippet": {"title": "Video Title", "description": "...", "thumbnails": {"high": {"url": "https://..."}}, "tags": ["tutorial"]}, "statistics": {"viewCount": "0"}}]}

Step-by-step automation

1

Subscribe to YouTube Channel Upload Notifications

Why: Without subscribing, the PuSH hub will not send any notifications — you must register your callback for each channel you want to monitor.

POST to https://pubsubhubbub.appspot.com/subscribe with form-encoded parameters. The hub responds with 202 Accepted and then sends a verification GET to your callback. Store the subscription topic and renewal time so you can re-subscribe before expiry (~10 days).

subscribe.sh
1curl -X POST https://pubsubhubbub.appspot.com/subscribe \
2 -d 'hub.callback=https://yourapp.com/yt/callback' \
3 -d 'hub.mode=subscribe' \
4 -d 'hub.topic=https://www.youtube.com/xml/feeds/videos.xml?channel_id=UCxxxxxxxxxxxxxxxxxxxxxxxx' \
5 -d 'hub.verify=async' \
6 -d 'hub.lease_seconds=864000' \
7 -d 'hub.secret=YOUR_SHARED_SECRET'

Pro tip: Store the subscription expiry time (current time + lease_seconds) in a database. Run a daily job to check and renew any subscriptions expiring within 24 hours.

Expected result: HTTP 202 Accepted from the hub. Shortly after, your callback receives a GET with hub.challenge — your server must echo it back to complete subscription.

2

Handle the Verification Handshake

Why: The PuSH hub will not send any notifications until your callback successfully echoes the hub.challenge — verification is mandatory.

When the hub sends a GET to your callback URL, parse the hub.challenge query parameter and return it as the raw response body with HTTP 200. Any other response rejects the subscription. This must happen within the verification timeout (typically a few seconds).

verification_example.sh
1# This is what the hub sends to your callback (you handle this in your web framework)
2# GET /yt/callback?hub.mode=subscribe&hub.topic=https%3A%2F%2F...&hub.challenge=RANDOM_STRING&hub.lease_seconds=864000
3# Your server MUST return 200 OK with body = RANDOM_STRING

Pro tip: Always use hub.secret and validate HMAC-SHA1 signatures on incoming POSTs — without this, anyone who knows your callback URL can send fake notifications.

Expected result: The hub confirms the subscription and starts sending POST notifications to your callback when videos are uploaded.

3

Parse the Atom XML Upload Notification

Why: The PuSH payload is Atom XML, not JSON — you must parse it to extract the videoId and channelId for enrichment.

When a channel uploads a video, the hub POSTs Atom XML to your callback. Parse the yt:videoId and yt:channelId elements. Then call videos.list with the videoId to get the full metadata (title, description, thumbnail, tags) needed for cross-channel alerts.

atom_example.sh
1# Example of what the hub POSTs to your callback (Atom XML):
2# POST /yt/callback
3# Content-Type: application/atom+xml
4#
5# <?xml version='1.0' encoding='UTF-8'?>
6# <feed xmlns:yt="http://www.youtube.com/xml/schemas/2015" xmlns="http://www.w3.org/2005/Atom">
7# <entry>
8# <yt:videoId>dQw4w9WgXcQ</yt:videoId>
9# <yt:channelId>UCxxxxxxxxxxxxxxxxxxxxxxxx</yt:channelId>
10# <title>Video Title</title>
11# <published>2026-01-15T10:00:00+00:00</published>
12# </entry>
13# </feed>

Pro tip: The PuSH notification fires on BOTH new uploads AND title/description changes. Check snippet.publishedAt vs snippet.updatedAt in the videos.list response to distinguish new uploads from edits.

Expected result: Parsed videoId and channelId from Atom XML, plus enriched metadata from videos.list including thumbnail URL, description, and tags.

4

Send Cross-Channel Alerts (Slack, Discord, Email)

Why: The whole point of upload detection is to cross-post alerts — format the video metadata and post to whichever channels your audience uses.

After parsing the notification and enriching with videos.list, format a message with the video title, thumbnail, URL, and channel name, then POST to your notification destination. For Discord, POST JSON to a webhook URL. For Slack, use chat.postMessage or an Incoming Webhook. For email, use SendGrid or similar.

send_alert.sh
1# Discord webhook alert
2curl -X POST "https://discord.com/api/webhooks/WEBHOOK_ID/WEBHOOK_TOKEN" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "embeds": [{
6 "title": "New Video: My Video Title",
7 "url": "https://youtu.be/VIDEO_ID",
8 "description": "Video description here...",
9 "image": {"url": "https://img.youtube.com/vi/VIDEO_ID/hqdefault.jpg"},
10 "color": 16711680
11 }]
12 }'

Pro tip: For Discord, use embeds with image thumbnails for much better visual presentation. Avoid plain text links — they generate poor previews.

Expected result: A formatted message appears in your Discord server, Slack channel, or email with the video title, thumbnail, and direct YouTube link.

5

Renew Subscriptions Before They Expire

Why: PuSH subscriptions expire (~10 days) — without renewal, you silently stop receiving notifications.

When you subscribe, store the channel ID and expiry timestamp. Run a daily cron job that checks for subscriptions expiring within 48 hours and re-subscribes them. Re-subscribing sends a new verification GET to your callback, which you handle the same way as the initial subscription.

renew_subscription.sh
1# Re-subscribe is identical to initial subscribe just POST again
2curl -X POST https://pubsubhubbub.appspot.com/subscribe \
3 -d 'hub.callback=https://yourapp.com/yt/callback' \
4 -d 'hub.mode=subscribe' \
5 -d 'hub.topic=https://www.youtube.com/xml/feeds/videos.xml?channel_id=UCxxxxxxxxxxxxxxxxxxxxxxxx' \
6 -d 'hub.verify=async' \
7 -d 'hub.lease_seconds=864000'

Pro tip: Use a cron job running every 24 hours with a 48-hour renewal window — this gives you a full day of buffer if the renewal cron fails once.

Expected result: All subscriptions are renewed before expiry and continue delivering upload notifications without interruption.

Complete working code

Complete webhook server that subscribes to multiple YouTube channels, handles PuSH verification, validates signatures, parses Atom XML upload notifications, enriches with videos.list metadata, and sends cross-channel alerts to Discord and Slack. Includes subscription renewal logic.

automate_youtube_webhooks.py
1from flask import Flask, request, Response
2import requests, hmac, hashlib, xml.etree.ElementTree as ET
3import os, json, time, logging
4from datetime import datetime, timedelta
5
6app = Flask(__name__)
7logging.basicConfig(level=logging.INFO)
8
9PUSH_SECRET = os.environ.get('PUSH_SECRET', '')
10YT_API_KEY = os.environ['YOUTUBE_API_KEY']
11DISCORD_WEBHOOK = os.environ.get('DISCORD_WEBHOOK_URL')
12CALLBACK_URL = os.environ['PUSH_CALLBACK_URL']
13YT_NS = 'http://www.youtube.com/xml/schemas/2015'
14ATOM_NS = 'http://www.w3.org/2005/Atom'
15
16@app.route('/yt/callback', methods=['GET', 'POST'])
17def youtube_callback():
18 if request.method == 'GET':
19 challenge = request.args.get('hub.challenge')
20 if challenge:
21 return Response(challenge, status=200, mimetype='text/plain')
22 return Response('Bad request', status=400)
23
24 if PUSH_SECRET:
25 sig = request.headers.get('X-Hub-Signature', '')
26 expected = 'sha1=' + hmac.new(PUSH_SECRET.encode(), request.data, hashlib.sha1).hexdigest()
27 if not hmac.compare_digest(sig, expected):
28 return Response('Forbidden', status=403)
29
30 try:
31 root = ET.fromstring(request.data.decode())
32 entry = root.find(f'{{{ATOM_NS}}}entry')
33 if not entry:
34 return Response('OK', status=200)
35 video_id = entry.find(f'{{{YT_NS}}}videoId').text
36 channel_id = entry.find(f'{{{YT_NS}}}channelId').text
37 logging.info(f'Upload notification: videoId={video_id} channelId={channel_id}')
38 process_upload_notification(video_id, channel_id)
39 except Exception as e:
40 logging.error(f'Error processing notification: {e}')
41
42 return Response('OK', status=200)
43
44def process_upload_notification(video_id, channel_id):
45 r = requests.get('https://www.googleapis.com/youtube/v3/videos',
46 params={'part': 'snippet', 'id': video_id, 'key': YT_API_KEY})
47 items = r.json().get('items', [])
48 if not items:
49 return
50 snippet = items[0]['snippet']
51 title = snippet.get('title', 'Untitled')
52 description = snippet.get('description', '')[:200]
53 thumbnail = snippet.get('thumbnails', {}).get('high', {}).get('url', '')
54 channel_name = snippet.get('channelTitle', channel_id)
55 if DISCORD_WEBHOOK:
56 requests.post(DISCORD_WEBHOOK, json={'embeds': [{
57 'title': title, 'url': f'https://youtu.be/{video_id}',
58 'description': description, 'image': {'url': thumbnail}, 'color': 16711680
59 }]})
60 logging.info(f'Discord alert sent for {title}')
61
62def subscribe(channel_id):
63 requests.post('https://pubsubhubbub.appspot.com/subscribe', data={
64 'hub.callback': CALLBACK_URL, 'hub.mode': 'subscribe',
65 'hub.topic': f'https://www.youtube.com/xml/feeds/videos.xml?channel_id={channel_id}',
66 'hub.verify': 'async', 'hub.lease_seconds': '864000',
67 **({'hub.secret': PUSH_SECRET} if PUSH_SECRET else {})
68 })
69
70if __name__ == '__main__':
71 CHANNELS = os.environ.get('YOUTUBE_CHANNEL_IDS', '').split(',')
72 for ch in CHANNELS:
73 if ch.strip():
74 subscribe(ch.strip())
75 app.run(port=int(os.environ.get('PORT', 3000)))

Error handling

422Subscription verification failed / hub rejected the subscription
Cause

Your callback did not return 200 with the exact hub.challenge value during verification. The hub sends a GET immediately after your subscribe POST — if your server is down or returns wrong content, the subscription is rejected.

Fix

Ensure your callback server is running and publicly accessible before subscribing. Test it manually: GET /yt/callback?hub.mode=subscribe&hub.challenge=TEST should return 200 with body 'TEST'.

Retry strategy

Fix the callback and re-POST the subscription request. There is no grace period — the subscription simply does not activate.

N/ANotifications stopped arriving (silent subscription expiry)
Cause

PuSH subscriptions expire after ~10 days. There is no expiry notification — they just silently stop.

Fix

Implement subscription renewal: store the expiry timestamp when subscribing and run a daily job that re-subscribes any subscriptions within 48 hours of expiry.

Retry strategy

Re-POST the subscription request. Treat all subscriptions as expired if you haven't renewed in 10+ days.

403YouTube Data API: quotaExceeded
Cause

The 10,000 daily quota units for videos.list enrichment have been exhausted. Each notification triggers one videos.list call (1 unit). This only happens if you're monitoring many high-volume channels.

Fix

Reduce channels monitored, or request a quota increase via Google's API Compliance Audit. For 100+ channels, consider processing notifications in batches using videos.list with multiple IDs per call.

Retry strategy

Queue notifications and process them after midnight Pacific Time when quota resets.

403Invalid signature on push notification
Cause

The HMAC-SHA1 signature in X-Hub-Signature does not match the expected value. Either the hub.secret is wrong or you are parsing the request body before reading it for signature validation (parsed body != raw bytes).

Fix

Use raw body middleware (express.raw() in Node.js, request.data in Flask) before validating HMAC. Never parse the body as JSON before signature validation.

Retry strategy

Return 403 to the hub. The hub will retry delivery — fix the signature validation and allow retries to succeed.

Rate Limits for YouTube PuSH + Data API

ScopeLimitWindow
PuSH notificationsNo quota costZero YouTube Data API quota units consumed
videos.list enrichment1 quota unit per callFrom 10,000 units/day project quota; resets midnight Pacific Time
Subscribe requests to hubNo documented limitRe-subscribe periodically (~every 9 days) per channel
retry-handler.ts
1import time
2import requests
3
4def videos_list_with_retry(video_id, api_key, max_retries=3):
5 for attempt in range(max_retries):
6 r = requests.get('https://www.googleapis.com/youtube/v3/videos',
7 params={'part': 'snippet', 'id': video_id, 'key': api_key})
8 if r.status_code == 200:
9 return r.json()
10 if r.status_code == 403 and 'quotaExceeded' in r.text:
11 raise Exception('Daily quota exhausted')
12 if attempt < max_retries - 1:
13 time.sleep(2 ** attempt)
14 return None
  • Monitor 100 channels at 5 uploads/day each = 500 quota units/day — well within the free 10,000 limit
  • For high-volume monitoring (1,000+ channels), batch multiple video IDs in a single videos.list call (max 50 IDs)
  • Use hub.secret for all subscriptions to validate notification authenticity and avoid processing spoofed requests
  • Set hub.lease_seconds to 864000 (10 days) even though the hub may override it — this is the maximum value Google reliably honors
  • Implement a deduplication check using videoId+publishedAt before sending alerts — PuSH fires on both uploads AND title/description edits

Security checklist

  • Always set hub.secret and validate HMAC-SHA1 signatures on all incoming POST notifications
  • Use express.raw() or equivalent to preserve raw request body for HMAC validation — parsed bodies break signature verification
  • Store API keys and webhook secrets in environment variables, never hardcoded
  • Your callback URL must use HTTPS — HTTP callbacks are rejected by the PuSH hub
  • Implement request deduplication to prevent processing the same videoId twice if the hub retries delivery
  • Return 2xx responses quickly from your callback (under 5 seconds) — long-running processing should be async
  • Log all incoming notifications with timestamps for debugging subscription issues and missed alerts

Automation use cases

Cross-Post New Videos to Discord Community

intermediate

When a channel uploads a video, automatically post a formatted embed to a Discord server with thumbnail, description, and direct YouTube link.

Multi-Channel Upload Aggregator

intermediate

Subscribe to 50+ YouTube channels in your niche and aggregate all uploads into a single Slack channel for easy monitoring.

Content Calendar Sync

beginner

When a new video is uploaded, automatically log it to a Google Sheet or Notion database with title, URL, and publish time for content tracking.

Competitor Upload Monitor

advanced

Subscribe to competitor channels and receive immediate Slack alerts when they publish new content, triggering a competitive analysis workflow.

No-code alternatives

Don't want to write code? These platforms can automate the same workflows visually.

Zapier

Free tier (100 tasks/month); paid from $20/month

Zapier has a native YouTube trigger 'New Video in Channel' that polls for uploads every 15 minutes (not real-time) and connects to Discord, Slack, Gmail, and 7,000+ other apps.

Pros
  • + No code required
  • + Connects to all major platforms
  • + Simple visual setup
Cons
  • - 15-minute polling delay (not instant like PuSH)
  • - Costs $20+/month for multi-step Zaps
  • - Limited to YouTube channels you can monitor

Make (formerly Integromat)

Free tier (1,000 operations/month); paid from $9/month

Make's YouTube 'Watch Videos' trigger polls for new uploads and can trigger cross-posting workflows to multiple platforms with better data transformation than Zapier.

Pros
  • + More flexible than Zapier
  • + Lower cost for complex flows
  • + Visual scenario builder
Cons
  • - Polling-based (5-15 min delay), not webhook real-time
  • - Learning curve for complex scenarios

n8n

Free self-hosted; Cloud from $20/month

n8n's YouTube trigger node can monitor channels for new uploads and trigger multi-step workflows including Discord, Slack, and custom HTTP requests — supports self-hosting for zero cost.

Pros
  • + Self-hosted = free
  • + Supports multiple output channels in one workflow
  • + Highly customizable
Cons
  • - Polling-based in most configurations
  • - Requires self-hosting setup
  • - Less YouTube-specific features

Best practices

  • Use PuSH instead of polling videos.list for new uploads — it costs zero quota and is near-real-time
  • Implement HMAC-SHA1 signature validation using hub.secret — it is the only way to verify notifications are genuine
  • Schedule subscription renewals at 9-day intervals to stay ahead of the ~10-day expiry
  • Parse yt:videoId from the Atom payload and call videos.list separately — the Atom payload has minimal metadata
  • Handle both new uploads and title/description edits gracefully — the PuSH hub sends both
  • Return HTTP 200 from your callback within 5 seconds — defer heavy processing to an async queue
  • Test your callback server locally with ngrok before deploying to catch verification failures early

Ask AI to help

Copy one of these prompts to get a personalized, working implementation.

ChatGPT / Claude Prompt

I'm building a YouTube PubSubHubbub notification server in Python (Flask) to cross-post new video uploads to Discord. My callback is receiving the verification GET but the hub.challenge echo is failing. Here is my current Flask route: [paste code]. The hub returns 422 when I try to subscribe. How should I correctly handle both GET verification and POST notification routing in the same endpoint?

Lovable / V0 Prompt

Build a YouTube Upload Monitor dashboard using PubSubHubbub webhooks. The dashboard should show a list of subscribed YouTube channels, allow adding new channel IDs to monitor, display a real-time log of upload notifications received with video title, thumbnail, and timestamp, and have a Discord/Slack alert toggle per channel. Use Supabase to persist subscriptions and notification history, and implement the PuSH callback server as a Supabase Edge Function.

Frequently asked questions

Is YouTube PubSubHubbub free?

Yes. PuSH subscriptions and notifications are completely free and do not consume any YouTube Data API quota units. The only quota cost is if you use videos.list to enrich notifications with metadata (1 unit per call), which is within the free 10,000 units/day limit.

How real-time are PuSH notifications?

Very fast — typically within seconds to a few minutes of a channel uploading. This is far faster than polling videos.list on a schedule. However, YouTube does not guarantee a maximum delivery time, and occasional delays of 5-10 minutes have been reported in practice.

Does PuSH notify me when a video is deleted?

No. PuSH only fires on uploads and title/description changes. Deletions, view count changes, comment activity, and live stream state changes do not trigger PuSH notifications. For these events, you must poll the relevant YouTube Data API endpoints.

What happens when I hit the rate limit?

PuSH itself has no rate limit — it is push-based and the hub controls delivery. If you hit the YouTube Data API 403 quotaExceeded error on videos.list enrichment, queue unprocessed notifications and retry after midnight Pacific Time when quota resets.

How do I monitor private videos?

PuSH does not support private video notifications — it only works for public channel uploads. To monitor your own channel's uploads including private videos, poll videos.list with OAuth 2.0 authentication and the youtube.readonly scope.

Can I subscribe to multiple channels at once?

Yes, but you must POST a separate subscription request to the hub for each channel_id. There is no batch subscription endpoint. Store each subscription's expiry time and renew them individually on a schedule.

Can RapidDev help build a cross-channel YouTube alert system?

Yes — RapidDev has built 600+ integrations including YouTube monitoring pipelines that cross-post to Discord, Slack, Telegram, and email. We can build a complete multi-channel monitoring dashboard with subscription management and alert routing. Book a free consultation at rapidevelopers.com.

What is the difference between PuSH and polling videos.list?

PuSH is push-based (YouTube notifies you within seconds), costs zero quota units, and requires a public HTTPS callback URL. Polling videos.list costs 1 quota unit per call and has up to 15-minute delays depending on your polling interval. Use PuSH for real-time alerts; use polling only if you cannot expose a public URL.

RapidDev

Need this automated?

Our team has built 600+ apps with API automations. We can build this for you.

Book a free consultation

Skip the coding — we'll build it for you

Our experts have built 600+ API automations. From prototype to production in days, not weeks.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.