Skip to API reference

YouTube transcripts, ready for code.

Extract transcripts, inspect whole channels, or analyze the visual evidence inside a video. One REST API, predictable JSON, and no scraping infrastructure to maintain.

Building for an AI assistant? Connect through MCP and skip the client code.

First request REST · JSON
curl -X POST __BASE__/api/transcripts \
  -H "Authorization: Basic YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids":["dQw4w9WgXcQ"],"lang":"en"}'
200 { "results": [{ "text": "…", "segments": […] }] }
Base URL
https://vidwords.com
Authentication
Basic token
Response format
JSON
Free plan
Included

Make your first request

Three checks prevent nearly every first-call error.

  1. 1
    Copy your token

    Find it in your profile. API access is included on every plan.

  2. 2
    Verify your email

    Open the confirmation link before making your first call.

  3. 3
    Send a video ID

    Use a YouTube ID or full URL. The response includes text and timestamps.

Authentication

Send your API token in the Authorization header of every request:

Authorization: Basic <your-api-token>
Content-Type: application/json
Confirm your email first.Until you click the verification link we sent you, every API call returns 403 with {"error":"email_unverified"}. This is the most common first-call failure on a new account.

Rate limits & usage

Request ceiling
60/min on Free, 200 on Starter, 500 on Pro, and 1,000 on Team.
Delivered transcript
1 Cloud Request. Failed lookups, including no captions or an invalid ID, are free.
Rate limit reached
429 rate_limited. Wait for the Retry-After duration before retrying.
Balance too low
402. The request stops before processing and consumes nothing.
Captionless videosA fresh transcript also uses 3 AI Units per audio minute and needs a paid plan or an AI Unit top-up; the Free monthly allowance does not cover audio transcription. Paid plans may use ElevenLabs as a premium final fallback. Replays cost the usual 1 Cloud Request without a fresh ASR charge.
POST

/api/transcripts

Fetch timestamped transcripts for as many as 50 videos in one request.

ids accepts video IDs or full YouTube URLs. Optional lang sets the preferred caption language and falls back to whatever the video has.

Transcript request and response fields
LocationFieldMeaning
Requestids RequiredArray of 1–50 video IDs or full YouTube URLs.
RequestlangPreferred caption language code. Defaults to en.
ResponseresultsOne transcript or per-video error for every requested ID.
ResponsecloudRequestsRemainingCloud Requests available after this call.
ResponseaiUnitsRemainingAI Processing Units available after this call.
curl -X POST https://vidwords.com/api/transcripts \
  -H "Authorization: Basic YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["dQw4w9WgXcQ", "https://youtu.be/jNQXAC9IVRw"], "lang": "en"}'
const res = await fetch("https://vidwords.com/api/transcripts", {
  method: "POST",
  headers: {
    "Authorization": "Basic YOUR_API_TOKEN",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ ids: ["dQw4w9WgXcQ", "jNQXAC9IVRw"], lang: "en" }),
});
const data = await res.json();
console.log(data.results);
import requests

res = requests.post(
    "https://vidwords.com/api/transcripts",
    headers={
        "Authorization": "Basic YOUR_API_TOKEN",
        "Content-Type": "application/json",
    },
    json={"ids": ["dQw4w9WgXcQ", "jNQXAC9IVRw"], "lang": "en"},
)
print(res.json()["results"])

Response:

{
  "results": [
    {
      "id": "dQw4w9WgXcQ",
      "title": "…",
      "author": "…",
      "language": "English",
      "languageCode": "en",
      "isGenerated": false,
      "text": "full transcript as one string…",
      "segments": [{ "text": "…", "start": 1.36, "duration": 1.68 }, …]
    },
    { "id": "…", "error": "no_transcript", "message": "No transcript is available for this video" }
  ],
  "cloudRequestsRemaining": 123,
  "aiUnitsRemaining": 580
}
POST

/api/channels Starter & up

Resolve channels into structured upload lists.

Available on Starter (up to 10 channels/request), Pro (100), and Team (500). ids accepts @handles, channel URLs, or UC… channel IDs. Each successful channel listing uses one Cloud Request; retrieving transcripts uses one more per delivered video.

Channel request and response fields
LocationFieldMeaning
Requestids RequiredChannel handles, URLs, or UC… IDs; the batch limit depends on your plan.
ResponseresultsOne channel result or per-channel error for every requested ID.
ResponsevideosNewest uploads with each video’s ID, title, and author.
ResponsevideoCountNumber of uploads found for the channel.
Responsetruncatedtrue when more uploads exist beyond the returned list.
curl -X POST https://vidwords.com/api/channels \
  -H "Authorization: Basic YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids": ["@mkbhd"]}'
const res = await fetch("https://vidwords.com/api/channels", {
  method: "POST",
  headers: {
    "Authorization": "Basic YOUR_API_TOKEN",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ ids: ["@mkbhd"] }),
});
console.log((await res.json()).results);
import requests

res = requests.post(
    "https://vidwords.com/api/channels",
    headers={"Authorization": "Basic YOUR_API_TOKEN", "Content-Type": "application/json"},
    json={"ids": ["@mkbhd"]},
)
print(res.json()["results"])

Response:

{
  "results": [
    {
      "id": "@mkbhd",
      "channelId": "UCBJycsmduvYEL83R_U4JriQ",
      "title": "…",
      "videoCount": 500,
      "truncated": true,
      "videos": [{ "id": "…", "title": "…", "author": "…" }, …]
    }
  ]
}
POST

/api/watch Video evidence

Analyze speech and on-screen evidence with timestamped citations.

Everything above reads captions. This reads the picture — slides, charts, demos, on-screen text a transcript never contains — and returns evidence tied to exact timestamps.

The part worth paying for is what it refuses to say. Every citation is checked against the stored analysis before you see it: a visual claim must match a real recorded frame, a spoken one a real transcript segment. When nothing survives the check, the answer says the evidence is insufficient instead of guessing.

Analysis takes minutes, so this is asynchronous — start a job, then poll it. Standard Watch uses 3 AI Processing Units per minute of video; Deep Watch uses 30.

Start an analysis

curl -X POST https://vidwords.com/api/watch \
  -H "Authorization: Basic YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"id": "dQw4w9WgXcQ", "mode": "smart"}'

# -> 202 {"jobId": 1234, "status": "queued", ...}
# (200 with status "ready" if this video was analyzed before)
const auth = { "Authorization": "Basic YOUR_API_TOKEN" };

const start = await fetch("https://vidwords.com/api/watch", {
  method: "POST",
  headers: { ...auth, "Content-Type": "application/json" },
  body: JSON.stringify({ id: "dQw4w9WgXcQ", mode: "smart" }),
});
// Check this. A refusal (402 out of minutes, 422 too long, 503 unavailable)
// returns {error, message} and no jobId — polling it would just 404 forever.
if (!start.ok) {
  const { error, message } = await start.json();
  throw new Error(`${error}: ${message}`);
}
const { jobId } = await start.json();

// Poll until ready — usually a few minutes.
let job;
do {
  await new Promise((r) => setTimeout(r, 10_000));
  job = await (await fetch(`https://vidwords.com/api/watch/${jobId}`, { headers: auth })).json();
} while (job.status === "queued" || job.status === "processing");

if (job.status === "failed") throw new Error(job.error ?? "analysis failed");
console.log(job.artifact);
AI Watch request fields
FieldMeaning
idYouTube video URL or id. Public videos only.
modequick, smart (default), deep, or auto. auto picks one from the video’s length and visual pace; deep needs Pro or Team.
langPreferred caption language. Defaults to en.
partialIf the video is longer than your plan allows, analyze the first allowed minutes and charge only for those. Never implied — you have to ask.

GET/api/watch/:jobId

Poll this until status is ready, then read artifact — the summary, chapters, key points and timestamped evidence. While queued or processing there is no artifact yet. A failed job carries errorCode and retryable. Free.

POST/api/watch/:jobId/ask

Ask a question against a finished analysis. Returns the answer with its verified citations, or says the evidence is insufficient. Uses 1 AI Processing Unit.

curl -X POST https://vidwords.com/api/watch/1234/ask \
  -H "Authorization: Basic YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"question": "What database did they benchmark against?"}'

Good to know

  • Starting the same video twice does not charge twice. A run already queued, running or finished on your account comes back as that same job.
  • Standard Watch uses 3 AI Processing Units per minute of video; Deep Watch uses 30. Free includes 200 AI Units/month, Starter 3,000, Pro 10,000 and Team 30,000, followed by any purchased AI top-up balance.
  • A run refused before it starts — video not public, age-restricted, too long, out of minutes — costs nothing.
  • Start is limited to 10 requests/minute and polling to 30 per 10 seconds, so a polling loop can’t lock you out of starting the next job.
  • Agents can reach all of this through the MCP server as analyze_video, get_analysis and ask_video.
POST

/mcp MCP server

Give an AI assistant direct, authenticated access to VidWords tools.

Point Claude, ChatGPT, Cursor, or any MCP-compatible agent at https://vidwords.com/mcp and it can read and search YouTube videos on its own — same API token, same Cloud and AI balances, no glue code to write.

The tool worth knowing about is search_transcript. It returns the moments matching your question with timestamps and youtube.com/watch?v=…&t=…s links already attached, so the assistant answers with a citation you can click instead of pasting the whole transcript at you.

Connect it

claude mcp add --transport http vidwords https://vidwords.com/mcp \
  --header "Authorization: Basic YOUR_API_TOKEN"
// claude_desktop_config.json
{
  "mcpServers": {
    "vidwords": {
      "type": "http",
      "url": "https://vidwords.com/mcp",
      "headers": { "Authorization": "Basic YOUR_API_TOKEN" }
    }
  }
}
# .cursor/mcp.json
{
  "mcpServers": {
    "vidwords": {
      "url": "https://vidwords.com/mcp",
      "headers": { "Authorization": "Basic YOUR_API_TOKEN" }
    }
  }
}

Tools

MCP tools and usage costs
ToolWhat it doesCost
search_transcriptFind where a video discusses something. Takes one video or a list of up to 25, so one call can search a whole channel. Returns timestamps, quoted context, and deep links.1 Cloud Request/video
get_transcriptFull transcript text for up to 25 videos at once.1 Cloud Request/video
list_channel_videosRecent uploads for a channel handle, URL, or id. Starter & up1 Cloud Request
list_watchlistsYour Radar watchlists and how much each has recorded.Free
watchlist_activityNewest uploads Radar has recorded for one watchlist.Free
analyze_videoStart a frame-level analysis of a video. Reads slides, charts and demos, not just captions.3 AI Units/minute (Standard); 30 (Deep)
get_analysisRead a finished analysis: chapters, key points, timestamped evidence.Free
ask_videoAsk about an analyzed video. Citations are verified against stored evidence or dropped.1 AI Unit
accountYour plan and remaining Cloud and AI balances, so the agent can tell you what a job costs before running it.Free

Good to know

  • Prefer search_transcript over get_transcript. Both cost the same, but a full transcript of a two-hour video fills the assistant's context with material irrelevant to your question. Search returns only the parts that answer it.
  • Ask for a time span instead of a whole video. Both transcript tools take optional from and to timecodes — seconds (615), m:ss (10:20) or h:mm:ss (1:02:13). It's the same format the tools print back, so a timestamp from one answer can be pasted straight into the next question.
  • The MCP endpoint uses the same plan-based request limit as REST: 60 requests/minute on Free, 200 on Starter, 500 on Pro, and 1,000 on Team. Starting an analysis has its own 10 requests/minute safety limit.
  • Cloud and AI balances work exactly as they do over REST: one Cloud Request per delivered transcript, AI Units for analysis, nothing for a failed lookup, and a readable error before a job exceeds its balance.
  • Ran out mid-conversation? The assistant gets a readable error telling it to check account or top up, rather than an opaque failure it will just retry.
  • This endpoint needs a VidWords API token. RapidAPI keys don't work here — grab a token from your profile.

Automation tools

Start with an importable workflow instead of wiring each request yourself.

Plug VidWords into no-code automation platforms — import a template, set your API token, and you're running.

n8n workflow

Ready-to-use n8n workflow for automated transcript extraction. Import and configure with your API token.

Download

Make blueprint

Ready-to-use Make.com blueprint for automated transcript extraction. Import and configure with your API token.

Download

Errors and recovery

Errors use stable codes so your integration can choose the next action.

HTTP errors and recommended recovery
HTTPCodeMeaning
400bad_requestMalformed body, too many ids, or invalid input
401unauthorizedMissing or invalid API token
402insufficient_cloud_requests / insufficient_ai_unitsNot enough of the required balance
403plan_requiredEndpoint needs a higher plan (channels API)
429rate_limitedToo many requests — honor Retry-After
403unsupported_transport/mcp only — a RapidAPI key was used. Use a VidWords API token.

On /mcp, problems the assistant can act on — no balance left, plan too low, bad video id — come back inside a successful tool result rather than as a transport error, so it can tell you what went wrong instead of silently retrying.

Per-video errors inside results: invalid_id, no_transcript, transcripts_disabled, video_unavailable, age_restricted, video_unplayable, ip_blocked, request_blocked, fetch_failed, internal. Per-channel errors: invalid_id, channel_not_found, internal.