Two ways to get YouTube transcripts: pull on your schedule, or receive them instantly via webhook the moment a new video drops.
The Pull API uses API key authentication. Find your key in the dashboard settings. Pass it using any of these methods:
Authorization: Bearer tr_live_xxxxxxxxxxxx
X-Api-Key: tr_live_xxxxxxxxxxxx
GET /api/videos?api_key=tr_live_xxxxxxxxxxxx
Webhooks don't require authentication — you configure the endpoint URL when creating a subscription. VidProxy will POST to that URL whenever a new video is detected.
Fetch all transcripts detected for your subscriptions within a time window. Good for batch processing, scheduled jobs, or polling-based integrations.
| Parameter | Type | Default | Description |
|---|---|---|---|
since |
string | 24h |
Time window. Accepts Nh (hours) or Nd (days). Examples: 6h, 7d. |
tag |
string | — | Filter by subscription label. If omitted, returns videos from all subscriptions. |
curl https://vidproxy.pro/api/videos?since=24h&tag=my-channel \ -H "Authorization: Bearer tr_live_xxxxxxxxxxxx"
{
"videos": [
{
"id": 1234,
"subscription_id": 42,
"channel_id": "UCxxxxxxxxxxxxxxxxxxxxxx",
"channel_name": "HomeBrewers",
"tag": "my-channel",
"video_id": "dQw4w9WgXcQ",
"video_title": "How to Brew a West Coast IPA",
"published_at": "2026-04-30T10:00:00.000Z",
"transcript_available": true,
"transcript_text": "Welcome to today's brew session...",
"transcript_source": "proxy",
"detected_at": "2026-04-30T10:05:12.000Z",
"ai_summary": null,
"ai_key_takeaways": null,
"ai_topics": null
}
],
"query": {
"tag": "my-channel",
"since": "24h",
"cutoff": "2026-04-29T10:05:12.000Z"
}
}
| Field | Type | Description |
|---|---|---|
video_id |
string | YouTube video ID. Construct the full URL as https://www.youtube.com/watch?v={video_id}. Use this for deduplication. |
channel_id |
string | YouTube channel ID (e.g. UCxxxxxx). |
channel_name |
string | Channel display name, as shown on YouTube. |
tag |
string | null | The label you set on the subscription. Useful for routing videos to different pipelines. |
video_title |
string | Video title at time of detection. |
published_at |
ISO 8601 | When the video was published on YouTube. |
transcript_available |
boolean | false only when YouTube explicitly reports transcripts are disabled for this video. |
transcript_text |
string | null | Full transcript as plain text. null if unavailable. |
transcript_source |
string | proxy, direct, or scraperapi — which fetch method succeeded. |
detected_at |
ISO 8601 | When VidProxy first detected this video. |
ai_summary |
string | null | AI-generated 2–3 sentence summary of the video. Pro and Agency plans only. |
ai_key_takeaways |
string[] | null | Array of key takeaways extracted from the transcript. Pro and Agency plans only. |
ai_topics |
string[] | null | Array of topic tags for the video. Pro and Agency plans only. |
subscription_id |
integer | Internal subscription ID. Stable identifier for the channel subscription that triggered this. |
Fetch the transcript for any YouTube video by URL or video ID — no channel subscription required. Each successful fetch counts against your monthly lookup quota. Failed fetches (transcript unavailable) are free.
| Parameter | Type | Description |
|---|---|---|
url |
string | Full YouTube URL (e.g. https://youtube.com/watch?v=abc123 or https://youtu.be/abc123). Use either url or video_id, not both. |
video_id |
string | YouTube video ID (11-character string, e.g. dQw4w9WgXcQ). |
curl "https://vidproxy.pro/api/transcript?video_id=dQw4w9WgXcQ" \ -H "Authorization: Bearer tr_live_xxxxxxxxxxxx"
{
"video_id": "dQw4w9WgXcQ",
"available": true,
"text": "Full transcript as plain text...",
"segments": [
{ "start": 0, "duration": 4, "text": "Never gonna give you up" },
...
],
"credits_remaining": 2499
}
| Header | Description |
|---|---|
X-Credits-Remaining |
Lookups remaining this month. unlimited for Agency. |
| Status | Error | Meaning |
|---|---|---|
400 |
url or video_id is required |
Neither parameter was provided, or the URL couldn't be parsed. |
402 |
credits_exhausted |
Monthly lookup quota reached. Resets on the 1st of each month. |
| Plan | Lookups / month |
|---|---|
| Free | 10 |
| Starter | 500 |
| Pro | 2,500 |
| Agency | 10,000 |
VidProxy ships a Model Context Protocol server at https://vidproxy.pro/mcp. Add it to Claude Desktop, Cursor, VS Code, or any MCP-compatible client and your AI assistant can read your channel data, pull transcripts, check what's left on your plan, and manage which channels you monitor — no copy-pasting, no API calls to write.
Add the following to your MCP client config. For Claude Desktop that's ~/.config/claude/claude_desktop_config.json; for Cursor it's in Settings → MCP.
{
"mcpServers": {
"vidproxy": {
"url": "https://vidproxy.pro/mcp",
"headers": {
"Authorization": "Bearer tr_live_xxxxxxxxxxxx"
}
}
}
}
Your API key is in Dashboard → Settings. The MCP server uses the same key as the REST API.
| Tool | Description | Credits |
|---|---|---|
get_transcript |
Fetch the full transcript for any YouTube video by ID or URL | 1 per successful fetch |
get_recent_videos |
Get videos from your monitored channels within a time window (e.g. since: "24h"), including full transcripts |
Free |
search_videos |
Search your subscription history by keyword — matches video titles and transcript text | Free |
list_channels |
List your monitored channels with latest video info. Each row includes a subscription_id that the tools below accept |
Free |
get_account_status |
Your plan and everything metered against it — channels used vs. allowed, webhooks, remaining transcript credits and their reset date, rate limit, poll interval. Unlimited values come back as null |
Free |
add_channel |
Start monitoring a channel by @handle, URL or UC… ID. Already monitoring it? You get the existing subscription back rather than a duplicate. Counts against your plan's channel limit |
Free |
set_channel_active |
Pause or resume a channel. Paused channels are skipped by the poller but keep every stored video and transcript. A paused channel still occupies a slot against your channel limit | Free |
remove_channel |
Permanently delete a channel and all stored videos, transcripts and delivery records for it. Requires an explicit confirm: true, and is disabled unless you enable destructive tools for the key — see below |
Free |
Three of these tools write to your account — they add, pause and delete subscriptions using the same plan limits as the dashboard. Your API key is what authorises them, so treat it like a password: anyone holding it can change what you monitor. Rotate it any time in Dashboard → Settings. Webhook URLs are deliberately not settable over MCP — configure those in the dashboard.
remove_channel is disabled by default. Until you enable destructive tools for a key in Dashboard → Settings, the tool is not advertised to your assistant at all and is refused if called — every other tool works normally. Rotating a key switches it back off.
Why it works this way: the confirm: true flag is a speed bump for a well-behaved assistant, not a security boundary, because the model decides whether to set it. Transcripts are written by the channel owner, not by you, so a video whose captions contain instructions aimed at your assistant is a real possibility once that assistant can also delete things. The dashboard toggle is the part of the gate that no text your assistant reads can flip. Deletion is irreversible and we keep no per-account backup of transcripts.
Configure a webhook URL on any subscription and VidProxy will POST a JSON payload to it within minutes of a new video being published. No polling required.
| Header | Value |
|---|---|
Content-Type |
application/json |
User-Agent |
VidProxy/1.0 |
X-VidProxy-Event |
new_video |
{
"event": "new_video",
"timestamp": "2026-04-30T10:05:12.000Z",
"subscription": {
"id": 42,
"label": "my-channel",
"channel_id": "UCxxxxxxxxxxxxxxxxxxxxxx",
"channel_name": "HomeBrewers"
},
"video": {
"id": "dQw4w9WgXcQ",
"title": "How to Brew a West Coast IPA",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"published": "2026-04-30T10:00:00.000Z"
},
"transcript": {
"available": true,
"text": "Welcome to today's brew session...",
"segments": [
{ "text": "Welcome to today's brew session", "start": 0.5, "duration": 3.2 }
]
},
"enrichment": {
"summary": "A step-by-step guide to brewing a West Coast IPA...",
"key_takeaways": ["Use high-alpha hops for bittering", "Dry hop at 68°F for best aroma", "Cold crash before packaging"],
"topics": ["IPA", "hops", "dry hopping", "homebrewing"]
}
}
The enrichment field is only present on Pro and Agency plans with AI enrichment enabled. transcript.segments contains per-segment timing data when available.
If your endpoint returns a non-2xx status or times out (10s), VidProxy retries up to 3 times:
After 3 failed retries the delivery is marked failed and logged in your dashboard. Make your endpoint idempotent — use video.id for deduplication.
Return any 2xx status code within 10 seconds. If your processing takes longer, acknowledge immediately and handle async.
Keyword Alerts let you save keywords and receive an email whenever any new video across all your subscriptions mentions them — matched against the video title and transcript. This is separate from per-subscription keyword filters, which control webhook delivery.
Alerts are managed from the Keyword Alerts tab in your dashboard. Add a keyword, and VidProxy will email you each time a new video matches it. Up to 50 keywords per account.
gpt matches "GPT-4", "ChatGPT", etc.Subscriptions also support a per-subscription Keyword Filter (set in the subscription edit modal). This controls webhook delivery only — the webhook fires only when the video title or transcript matches one of the specified keywords. It does not send emails and is independent of Keyword Alerts.
| Method | Endpoint | Description |
|---|---|---|
GET | /api/keyword-alerts | List all keyword alerts |
POST | /api/keyword-alerts | Create a keyword alert — body: { "keyword": "string" } |
DELETE | /api/keyword-alerts/:id | Delete a keyword alert |
The Pull API is rate-limited per API key using a sliding 60-second window. Response headers tell you your current usage:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Max requests per minute for your plan. |
X-RateLimit-Remaining |
Requests remaining in the current window. |
Retry-After |
Seconds until the window resets (only on 429 responses). |
When you exceed the limit you receive a 429 Too Many Requests with a Retry-After header. Back off and retry after that delay.
| Plan | Channels | Webhooks | API req / min | Transcript lookups / mo |
|---|---|---|---|---|
| Free | 1 | 1 | 60 | 10 |
| Starter | 15 | 3 | 60 | 500 |
| Pro | 100 | Unlimited | 300 | 2,500 |
| Agency | 1,000 | Unlimited | 300 | 10,000 |
Authorization: Bearer header, an X-Api-Key header, or an api_key query parameter. The key lives in Dashboard → Settings. Keys are stored hashed, so the plaintext is shown only at the moment you create or rotate one — if you lose it, rotate rather than go looking for it. Rotating retires the old key immediately.
GET /api/videos?since=24h returns everything detected in that window with transcripts attached. Running both is reasonable: webhooks for the live path, a scheduled pull to catch anything a failed delivery dropped.
X-Credits-Remaining header, and the quota resets on the 1st of each month: 10 on Free, 500 on Starter, 2,500 on Pro, 10,000 on Agency. Past the quota the endpoint answers 402 credits_exhausted. The get_transcript MCP tool draws on the same pool; webhook deliveries and the pull API never touch it.
false means YouTube reports captions as disabled for that video, which no amount of retrying changes.
404 or 500 for channels that are plainly fine, then clears on its own. We currently see it between roughly 01:00 and 07:00 UTC, though the hours drift and other people using the same feed report a different window, so treat that range as an observation rather than a schedule. It's a long-standing YouTube issue, publicly reported since December 2025 and still open; it isn't specific to VidProxy and there's no setting on either side that avoids it.
429, with Retry-After giving the seconds until the window clears. The window is a sliding 60 seconds per API key: 60 requests a minute on Free and Starter, 300 on Pro and Agency. X-RateLimit-Remaining on every response lets you back off before you get there. Calls to /mcp draw from that same per-key budget rather than a separate one, so splitting traffic across the two transports doesn't buy you extra headroom.
get_transcript spends a credit, add_channel counts against your channel limit. The exception is remove_channel, which stays hidden from the tool list and is refused until you enable destructive tools for that key in Dashboard → Settings. Rotating the key switches that back off.