Make your first request
Three checks prevent nearly every first-call error.
- 1Copy your token
Find it in your profile. API access is included on every plan.
- 2Verify your email
Open the confirmation link before making your first call.
- 3Send 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
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 theRetry-Afterduration before retrying.- Balance too low
402. The request stops before processing and consumes nothing.
/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.
| Location | Field | Meaning |
|---|---|---|
| Request | ids Required | Array of 1–50 video IDs or full YouTube URLs. |
| Request | lang | Preferred caption language code. Defaults to en. |
| Response | results | One transcript or per-video error for every requested ID. |
| Response | cloudRequestsRemaining | Cloud Requests available after this call. |
| Response | aiUnitsRemaining | AI Processing Units available after this call. |
Need generated types or every nested field? Use the exhaustive OpenAPI schema.
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
}
/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.
| Location | Field | Meaning |
|---|---|---|
| Request | ids Required | Channel handles, URLs, or UC… IDs; the batch limit depends on your plan. |
| Response | results | One channel result or per-channel error for every requested ID. |
| Response | videos | Newest uploads with each video’s ID, title, and author. |
| Response | videoCount | Number of uploads found for the channel. |
| Response | truncated | true when more uploads exist beyond the returned list. |
Need generated types or every nested field? Use the exhaustive OpenAPI schema.
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": "…" }, …]
}
]
}
/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);
| Field | Meaning |
|---|---|
id | YouTube video URL or id. Public videos only. |
mode | quick, smart (default), deep, or auto. auto picks one from the video’s length and visual pace; deep needs Pro or Team. |
lang | Preferred caption language. Defaults to en. |
partial | If 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_analysisandask_video.
/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
| Tool | What it does | Cost |
|---|---|---|
search_transcript | Find 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_transcript | Full transcript text for up to 25 videos at once. | 1 Cloud Request/video |
list_channel_videos | Recent uploads for a channel handle, URL, or id. Starter & up | 1 Cloud Request |
list_watchlists | Your Radar watchlists and how much each has recorded. | Free |
watchlist_activity | Newest uploads Radar has recorded for one watchlist. | Free |
analyze_video | Start a frame-level analysis of a video. Reads slides, charts and demos, not just captions. | 3 AI Units/minute (Standard); 30 (Deep) |
get_analysis | Read a finished analysis: chapters, key points, timestamped evidence. | Free |
ask_video | Ask about an analyzed video. Citations are verified against stored evidence or dropped. | 1 AI Unit |
account | Your 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_transcriptoverget_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
fromandtotimecodes — seconds (615),m:ss(10:20) orh: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
accountor 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.
Errors and recovery
Errors use stable codes so your integration can choose the next action.
| HTTP | Code | Meaning |
|---|---|---|
400 | bad_request | Malformed body, too many ids, or invalid input |
401 | unauthorized | Missing or invalid API token |
402 | insufficient_cloud_requests / insufficient_ai_units | Not enough of the required balance |
403 | plan_required | Endpoint needs a higher plan (channels API) |
429 | rate_limited | Too many requests — honor Retry-After |
403 | unsupported_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.