Quick start · YouTube
YouTube WebSocket API quick start for new uploads
Connect to your YouTube stream and receive an event for every new upload and Short from the channels you track. This page gets a consumer running; the API reference holds every message type.
- WebSocket
ws_url from GET /v1/dashboard- REST base
https://youtube.1322.io- Auth
- X-Api-Key or Authorization: Bearer
- Updated
What you get
New uploads and Shorts from the YouTube channels on your list. Three message types share one stream: upload (the one to build on, subtype is video or short), upgrade (a better thumbnail for a video you already have) and deletion (a tracked video confirmed unavailable).
Uploads arrive in under a second. You do not need a YouTube Data API key or quota of your own.
Price: From $150/month for 5 channels; WebSocket +$100/month. Plans: Lite $150/month (5 channels), Standard $300/month (15 channels), Premium $400/month (50 channels). Pay monthly, or prepay 3 months (10% off), 6 months (15% off) or a year (25% off). See YouTube pricing.
Authentication
- REST: send your API key as
X-Api-Key: <api_key>orAuthorization: Bearer <api_key>. GET /v1/dashboardreturns a completews_urland a separatews_key. Connect tows_url?key=<ws_key>, or send the key in theX-WS-KEYheader instead: one or the other, not both.- The API access page in your dashboard shows the same API key, WebSocket URL and WS key.
Connect to the WebSocket
Set these environment variables, then run either consumer from your server:
YT_WS_URL: ws_url from GET /v1/dashboardYT_WS_KEY: ws_key from GET /v1/dashboardYT_API_KEY: your API key (REST only)
# pip install "websockets>=13" (Python 3.9+)
import asyncio, json, os
from websockets.asyncio.client import connect
# ws_url and ws_key from GET /v1/dashboard (or the API access page)
WS_URL = os.environ["YT_WS_URL"] + "?key=" + os.environ["YT_WS_KEY"]
def handle(msg):
kind = msg.get("type")
if kind == "upload":
video = msg.get("video") or {}
channel = msg.get("channel") or {}
print(msg.get("subtype"), channel.get("name"), video.get("title"), video.get("url"))
elif kind == "upgrade":
upgrade = msg.get("upgrade") or {}
print("better thumbnail for", upgrade.get("video_id"), upgrade.get("url"))
elif kind == "deletion":
print("deleted", (msg.get("video") or {}).get("id"))
async def main():
backoff = 1
while True:
try:
async with connect(WS_URL) as ws:
backoff = 1
async for raw in ws:
try:
event = json.loads(raw)
except ValueError:
continue
if isinstance(event, dict):
handle(event)
except Exception as exc:
print("disconnected:", exc)
await asyncio.sleep(backoff)
backoff = min(backoff * 2, 30)
asyncio.run(main())// npm install ws, save as consumer.mjs, run: node consumer.mjs (Node 18+)
import WebSocket from "ws"
// ws_url and ws_key from GET /v1/dashboard (or the API access page)
const BASE_URL = process.env.YT_WS_URL
const WS_KEY = process.env.YT_WS_KEY
if (!BASE_URL || !WS_KEY) throw new Error("Set YT_WS_URL and YT_WS_KEY")
const WS_URL = BASE_URL + "?key=" + encodeURIComponent(WS_KEY)
function handle(msg) {
if (msg.type === "upload") {
console.log(msg.subtype, msg.channel?.name, msg.video?.title, msg.video?.url)
} else if (msg.type === "upgrade") {
console.log("better thumbnail for", msg.upgrade?.video_id, msg.upgrade?.url)
} else if (msg.type === "deletion") {
console.log("deleted", msg.video?.id)
}
}
let backoff = 1000
function connect() {
const ws = new WebSocket(WS_URL)
ws.on("open", () => { backoff = 1000 })
ws.on("message", (raw) => {
let event
try { event = JSON.parse(raw.toString()) } catch { return }
if (event && typeof event === "object" && !Array.isArray(event)) handle(event)
})
ws.on("error", (err) => console.error("socket error:", err.message))
ws.on("close", () => {
setTimeout(connect, backoff)
backoff = Math.min(backoff * 2, 30000)
})
}
connect()- One UTF-8 JSON object per text frame. The server pings every 30 seconds; both libraries answer automatically.
- Treat every message as standalone: most videos never produce an
upgradeor adeletion, so nothing should wait for one. Events are not replayed while you are offline.
One example frame
Illustrative values for fictional accounts, trimmed to the fields most consumers read first. A real frame carries the full set for your tracked accounts.
{
"type": "upload",
"subtype": "video",
"detected_at": "2026-10-15T14:03:22Z",
"channel": {
"id": "UCexampleChannel0000001a",
"name": "Example Studio",
"url": "https://www.youtube.com/channel/UCexampleChannel0000001a",
"avatar": null
},
"video": {
"id": "aBcDeF12345",
"url": "https://www.youtube.com/watch?v=aBcDeF12345",
"title": "Studio tour, part 2",
"description": null,
"published_at": "2026-10-15T14:03:00Z",
"updated_at": null
},
"images": {
"seed": null,
"chosen": "https://example.com/thumbs/aBcDeF12345.jpg",
"thumbnail_small": null
}
}- Every
channelandimagesfield can be null;images.chosenis the best image available when the upload is sent. - An
upgradecarriesupgrade.video_idand a replacement image URL: match it to the upload you already rendered.
Manage the list over REST
REST base https://youtube.1322.io. Each call below sends X-Api-Key with your API key.
| Method | Path | What it does |
|---|---|---|
| GET | /v1/dashboard | Status, term, slots, tracked channels, ws_url and ws_key (flat). |
| GET | /v1/list | Just the tracked channels: channel_id, name, added_at. |
| POST | /v1/track | Add a channel by UC… ID, @handle or channel URL. |
| POST | /v1/untrack | Remove a channel. |
| GET | /v1/limits | Tracked count, max_tracked and your per-key request rate (rpm). |
curl "https://youtube.1322.io/v1/dashboard" \
-H "X-Api-Key: $YT_API_KEY"
curl "https://youtube.1322.io/v1/list" \
-H "X-Api-Key: $YT_API_KEY"
curl -X POST "https://youtube.1322.io/v1/track" \
-H "Content-Type: application/json" \
-H "X-Api-Key: $YT_API_KEY" \
-d '{ "channel": "@examplestudio" }'
curl -X POST "https://youtube.1322.io/v1/untrack" \
-H "Content-Type: application/json" \
-H "X-Api-Key: $YT_API_KEY" \
-d '{ "channel": "UCexampleChannel0000001a" }'
curl "https://youtube.1322.io/v1/limits" \
-H "X-Api-Key: $YT_API_KEY"GET /v1/healthis a liveness check for uptime monitors;/openapi.jsonserves the machine-readable schema.
Full reference
Every field, endpoint and edge case for YouTube lives in the API reference. Jump straight to the part you need:
Common questions
Do upload, upgrade and deletion always arrive together?
No. upload is sent for every detected video. upgrade is sent only when a better thumbnail becomes available, and deletion only when a video is confirmed unavailable, at any later time or never.
Can I add a channel by @handle?
Yes. POST /v1/track accepts a UC-prefixed channel ID, an @handle or a channel URL; they resolve to the same channel.
Other quick starts
- X (Twitter) WebSocket quick start: Tweets, deletes, profile and follow events
- Truth Social WebSocket quick start: Posts, retruths, quotes and media
- Instagram WebSocket quick start: Posts, Reels, carousels and Stories
- TikTok WebSocket quick start: Uploads, reposts, LIVE start and end
- Binance Square WebSocket quick start: Posts, pin changes and sentiment
- News WebSocket quick start: Full-text articles from news feeds
Related: