API reference
A REST API for YouTube transcripts, plus the search and listing endpoints for finding the videos worth transcribing. One envelope for every response, one credit per call.
https://api.transcriptout.com Data endpoints sit under /api, for example https://api.transcriptout.com/v1/transcript. The MCP endpoint (/mcp) is at the root.
- 1Create an API key
Open the API Keys page in your dashboard and create one. It's shown once, so store it somewhere safe.
- 2Call the API
Ask for a transcript as plain text. Any YouTube URL works in place of the id.
request.sh - 3Read the envelope
Check ok, then read data. On failure, branch on code, never on the wording of detail.
response.json
Pass your key as a Bearer token. A key is shown once. We keep only a hash, so a lost key is replaced, not recovered. Revoking is immediate.
Keep keys server-side. Anything shipped to a browser or a mobile app is readable by your users, so proxy the call through your own backend.
Every data endpoint above is also an MCP tool, so Claude, Cursor and other MCP clients can call it mid-conversation. Same key, same credits, free plan included.
https://api.transcriptout.com/mcp Streamable HTTP, stateless JSON-RPC: initialize , tools/list , tools/call , ping . No event stream and no session, so GET answers 405 by design. The key goes in every request. A bad key is a plain HTTP 401, not a tool error.
Connect it
Same URL everywhere, only the way of passing the key differs.
One command in the terminal, the key travels in a header.
Then ask the assistant something concrete, for example “get the transcript of this video and summarize it” with a link. It picks the right tool by the task.
Tools
Thin wrappers over the endpoints above: same parameters, same envelope inside the result, same errors.
| Tool | What it does |
|---|---|
| get_transcript | Transcript of a video by id or URL. Plain text by default, timed segments with format=json. Set segment= to size those segments, 500–1500 for retrieval chunks. Add video_metadata=true for the title and channel in the same call. |
| get_video_info | Title, channel, duration, views and available languages, without the subtitles, for when the transcript itself isn't wanted. |
| search_youtube | Search YouTube for videos or channels, with pagination. |
| list_channel_videos | Videos of a channel, newest first. Accepts @handle, channel id or URL. |
| search_channel_videos | Search inside one channel using YouTube's native relevance ranking. |
| latest_channel_videos | The most recent uploads of a channel, the fastest way to check what is new. |
| list_playlist_videos | Videos of a playlist in playlist order, by id or URL. |
| search_playlist_videos | Find videos in a playlist by a substring of the title. |
| submit_transcripts_job | Queue up to 4,000 videos at once and get a job id back immediately. Takes the same options as get_transcript, segment= included, one set for the whole job. 1 credit per video, charged on submit. Needs a user key. |
| get_transcripts_job | Progress of a batch job. Ready, failed and still pending. Free to poll. |
| get_transcripts_results | Finished transcripts from a batch job, readable while the rest are still running. Free. |
| cancel_transcripts_job | Stop a batch job. Only videos not started yet are refunded. Free. |
Credits and limits
- A tool call costs the same credits as the matching REST endpoint: 1 credit by default.
- Connecting, listing tools and pings are free. So are the batch job tools: submitting charges 1 credit per video, and checking progress, reading results and canceling cost nothing.
- Refund rules match REST: calls we reject before touching YouTube are credited back, and so is a 503. If we couldn't serve you, you don't pay for it.
- The balance after each call comes back in the X-Credits-Remaining header.
- Tool calls show up in Logs under their endpoint with method MCP.
- Rate limits are shared with the REST API: one pool per key.
Protocol problems come back as a JSON-RPC error, a failed call as isError with our usual envelope inside. MCP is part of every plan, free included: if a key works for REST, it works here.
Troubleshooting
The Authorization header is missing or the key is wrong. Check that the value starts with “Bearer sk_” and that the key is still active. Revoking a key takes effect immediately.
The balance is empty. Tool errors reach the model in the same envelope as REST errors, with a machine-readable code. Top up to continue. The free grant is one-time and isn't renewed. Connecting and listing tools keeps working either way.
Most clients enable tools per chat or per agent. Check that the server is switched on in the client settings, then ask something concrete that mentions a video or a channel: the model picks tools by the task.
It can't send custom headers natively, so the config goes through the mcp-remote bridge (see the Claude Desktop tab above). Node.js has to be installed, npx downloads the bridge on first run.
Expected. The server is stateless Streamable HTTP: clients POST JSON-RPC messages to the endpoint, there's no event stream and no session to subscribe to.
- ok
- Boolean success flag, the one check you always start with.
- data
- The payload, present only on success. Its shape is documented per endpoint below.
- code
- Machine-readable error slug, present only on failure. This is what your code should switch on.
- detail
- Human-readable message. Safe to log or surface, but the wording can change, so don't match on it.
- request_id
- Trace id for this call, on both success and failure. Also in the X-Request-ID header and in your Logs page.
Response headers
| X-Request-ID | The same trace id as in the body. Present on every response. |
| X-Credits-Remaining | Your balance after the call. Cheap way to monitor spend without polling anything. |
| X-Cache | HIT or MISS on transcript responses, telling you whether we served it from cache or fetched it live. |
| Retry-After | Seconds to wait before retrying. Sent with 429, 502 and 503. |
code is a stable machine-readable slug. detail is a human sentence we may reword at any time. The Refund column shows whether the call is credited back. See Credits .
| HTTP | code | When | Refund |
|---|---|---|---|
| 400 | bad_request | Invalid input. A malformed video id, an unknown type value. | Refunded |
| 401 | unauthorized | Missing or invalid Bearer token. Revoked keys land here too. | Refunded |
| 402 | insufficient_credits | Your balance is empty. The call isn't performed at all. | Not charged |
| 403 | forbidden | The key exists but isn't allowed to do this. | Not charged |
| 404 | not_found | The video has no subtitles at all, or none in the language you asked for, or the video / channel / playlist doesn't exist. Terminal. Retrying won't change it. | Charged |
| 409 | subscription_exists | This account already has a live subscription with another payment provider. Cancel it before subscribing again. | Not charged |
| 409 | key_limit_reached | You already hold the maximum number of API keys. Revoke an unused one first. | Not charged |
| 410 | gone | The video was removed or made private. | Charged |
| 413 | payload_too_large | The request body exceeds the allowed size, or a batch job lists more videos than one job may hold. | Refunded |
| 422 | validation_error | A parameter failed validation. Wrong type, out of range. | Refunded |
| 429 | rate_limited | Too many requests. Back off and retry after Retry-After seconds. | Refunded |
| 451 | unavailable_for_legal_reasons | Age-restricted, members-only or login-required video. | Charged |
| 500 | internal | Something broke on our side. Send us the request_id. | Charged |
| 501 | not_implemented | The feature is switched off on this deployment. Payments with no provider configured, for instance. | Not charged |
| 502 | upstream_error | YouTube or our proxy layer failed. Safe to retry. | Charged |
| 503 | service_unavailable | We couldn't serve it right now: queue full, upstream throttling or a backend down. We already retried internally before answering, so wait out Retry-After instead of retrying immediately. Not to be confused with 404, which is terminal. | Refunded |
Every response carries a request_id , in the body and in the X-Request-ID header. Include it when you write to support. It's what lets us find the exact call in our logs.
- Every call costs 1 credit, whether or not the answer is the one you wanted. Cached and freshly fetched responses cost the same.
- The balance left after a call comes back in the X-Credits-Remaining header.
- Free credits are granted once at signup and aren't renewed. Plan credits are spent FIRST. They are the ones that expire at the monthly boundary, so anything else would burn credits that would have survived.
- Refunded when the failure is on us: 400, 401, 413, 422, 429 and 503.
- Duplicate calls still cost a credit each, even when we serve them from one fetch.
- Running out returns 402 insufficient_credits without performing the call.
- Every charge, refund and error is itemized on the Logs page with its request_id.
A 404 or 410 is charged. Both mean we asked YouTube and got a real answer. "This video has no transcript" is a result, not a failure. Requests we reject before touching YouTube come back free, and so does a 503: if we couldn't serve you, you don't pay for it.
Exceeding the limit returns 429 rate_limited with a Retry-After telling you how many seconds are left. A throttled request is never charged, and the window slides, so bursting right after a rejection just gets rejected again.
How many requests to run in parallel. Take your rate times how long one answer takes: at 3 per second and roughly 3 seconds for a video we have not fetched before, that is about 10 in flight, and 8 is a safe default. Cached videos come back in milliseconds and cost you none of that budget, so a workload with repeats runs far ahead of this figure.
Opening many more connections than that doesn't go faster: your key holds a fixed share of our fetching capacity, and anything above it is answered 503 with Retry-After straight away rather than left to stall. Duplicates collapse, so the same video asked for twice at once is fetched once, and both 429 and 503 are refunded, so backing off costs you nothing.
For a long list, don't pace it yourself.POST /v1/transcripts takes up to 4,000 videos in one request and works through them at whatever rate is available, waiting for the window instead of refusing. Nothing is lost if your connection drops, progress is free to poll, and you collect the results when the job is done.
List endpoints return next_page_token and has_more . Pass the token back for the next page while has_more is true. There's no offset , and page 400 costs the same as page 1.
The token replaces the original query parameters, with no need to repeat id or q . The one endpoint without tokens is /v1/playlist/search , which reports scanned and truncated instead.
| Field | Type | Example | Notes |
|---|---|---|---|
| duration | string | "8:01" · "4:00:00" | "M:SS", or "H:MM:SS" once it passes an hour. |
| view_count | string | null | "91K views" · "501,281 views" | The string YouTube renders, passed through. Usually abbreviated (K/M/B), sometimes an exact count with separators, and exact below 1000. Handle both if you parse it into a number. |
| published | string | null | "5 days ago" · "2 years ago" | Relative date, as displayed by YouTube. The one exception is /v1/channel/latest, which returns an ISO timestamp. |
| thumbnails | array | [{ url, width, height }] | Five sizes from 120×90 to 1280×720. The two largest can 404 for some videos, so handle onerror. |
These are display strings, not numbers. They mirror what YouTube shows. If you need to sort or compute with durations, parse them on your side.
Endpoints
12 endpoints, one envelope. Parameters, an example call and the response for each.
/v1/transcript1 creditGet a transcript
The main endpoint: subtitles of a single video, by id or by any YouTube URL. Returns timed segments by default, or one flat string with format=text.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
video * | type string | default — | Video id (11 chars) or any YouTube URL (watch, youtu.be, shorts, embed). |
lang | type string | default en | Language code of the track: en, ru, es-419. No track in that language → 404. A video with no subtitles at all also answers 404, not 503. |
kind | type manual | auto | default manual if it exists | Which track to take. Omit it to prefer the human-made one and fall back to auto-generated. |
format | type json | text | srt | vtt | srv3 | default json | Changes only the type of data.transcript. See the note below. |
segment | type integer | default 180 (80 for srt/vtt), auto tracks only | Max characters per segment, 20–5000. Sentences are packed up to it and never cut mid-thought: 40–80 for subtitle lines, 500–1500 for embedding chunks. Pass it and it applies to manual tracks too, so one size means one granularity whichever track answers. Not valid with format=srv3. |
video_metadata | type boolean | default false | Also include data.metadata with the same fields GET /v1/video returns. |
download | type boolean | default false | Return the file itself instead of the envelope (srt, vtt, srv3 or text), with a Content-Disposition so curl -o writes it straight to disk. Refused with format=json and with video_metadata. Errors still come back as the usual JSON envelope. |
* required
Example
Response fields
Inside data
video_idstring- The id we resolved from your input.
languagestring- Language code of the track actually returned.
kindmanual | auto- Which track served the transcript.
transcriptarray | string- Segments for json, a string for text, srt, vtt and srv3.
available_langsarray- Every track the video has: { code, kind, name }.
metadataobject- Only with video_metadata=true.
Every format comes back in the same JSON envelope. Only data.transcript changes type. json gives an array of segments with start and duration in seconds, text gives one string with lines joined by \n, srt and vtt give a ready subtitle file body, cut to 80-character cues on an auto track unless segment= says otherwise. srv3 gives YouTube's raw timedtext XML as a string (404 if that track has no srv3 source).
By default segmentation follows the track: a manual one keeps the author's own line breaks (~40 characters), while an auto-generated one is rebuilt into sentences (~95). Pass segment= to decide the size yourself and get the same granularity from either.
Auto-generated tracks in many languages arrive with no punctuation at all. There we cut on the speaker's own pauses rather than on a character count, so a segment ends where the sentence did.
Responses carry X-Cache: HIT when the transcript came from our cache and MISS when we fetched it just now. A MISS takes a few seconds. A HIT is immediate. The price is the same either way.
Inside a transcript segment, duration is a number of seconds (4.12), not the "M:SS" string used in video listings.
/v1/transcripts1 credit per videoGet transcripts in bulk
Hand over up to 4,000 videos at once and collect the transcripts as they land. It's the single-video endpoint in a loop (same price, same cache, same options) with the loop written on our side. You're never left holding the request: the answer is a job id, and the results can be read as they arrive.
How it works
- 1POST
/v1/transcripts1 credit per videoHand over the list. Answers 202 with a job_id, not the transcripts. - 2GET
/v1/transcripts/{job_id}freeCheck on it whenever you like: done, ready, failed, pending. - 3GET
/v1/transcripts/{job_id}/resultsfreeRead the transcripts, page by page, already possible while it runs. - 4GET
/v1/transcripts/{job_id}/results/{video_id}freeOne video out of the job, by id. Add download=true to save it as a file. - 5POST
/v1/transcripts/{job_id}/cancelfreeChanged your mind: stops it and refunds whatever it has not reached.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
videos * | type string[] | default — | Video ids or YouTube URLs, up to 4,000 per job. Duplicates are collapsed before you're charged. |
lang | type string | default en | One language for the whole job. |
format | type json | text | srt | vtt | srv3 | default json | Exactly as on /v1/transcript. It changes only the type of transcript in each item. |
kind | type manual | auto | default manual if it exists | Which track to take, for every video in the job. |
segment | type integer | default 180 (80 for srt/vtt), auto tracks only | Max characters per segment, 20–5000, exactly as on /v1/transcript, one size for the whole job. Not valid with format=srv3. |
video_metadata | type boolean | default false | Add the video metadata to each item, the same fields GET /v1/video returns. |
Idempotency-Key | type header | default — | Optional. A retry with the same key and the same list returns the SAME job instead of opening a second one and charging twice. Kept for 24 hours. |
* required
Example
Response fields
Inside data
job_idstring- Use it to poll the job and to read its results.
statusstring- queued → running → done, or cancelled.
countinteger- How many videos the job holds, after duplicates.
langstring- The language it will fetch, echoed back.
formatstring- The format the results will come in, echoed back.
creditsobject- { charged, refunded }. The whole list is charged up front.
Nothing waits on the request. The transcripts are read from …/results, and that works while the job is still running. The first page is there long before the last video is.
The pace is your rate limit: a job spends the same requests-per-minute your direct calls do, so a thousand videos take about what a thousand single calls would. Handing the list in costs one unit of that allowance, not one per video.
Every video reports its own status, and the ones that failed through no fault of yours are refunded automatically. One bad video never sinks the rest.
A user key (sk_…) is required, and up to 3 jobs can be unfinished at a time. A finished job and its results stay readable for 24 hours.
POST /v1/transcripts/{job_id}/cancel refunds only what the job has not reached yet. Videos already fetched cost real work and are in your results, so they stay charged.
/v1/transcripts/{job_id}freeCheck a batch job
How far along a job is. Free and safe to call as often as you like. Polling work you have already paid for should not cost anything.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
job_id * | type string | default — | The id POST /v1/transcripts gave you. |
* required
Example
Response fields
Inside data
job_idstring- The job this is about, the same id you asked for.
statusstring- queued → running → done, or cancelled.
langstring- The language the job was submitted with.
formatstring- The format the job was submitted with.
countinteger- Videos in the job.
doneinteger- How many have an answer, ready or failed.
readyinteger- How many came back with a transcript.
failedinteger- How many ended in an error.
pendinginteger- Still to go.
creditsobject- { charged, refunded } as it stands right now.
created_atstring- When the job was accepted, ISO-8601.
finished_atstring | null- Null until the job ends.
Every 10–30 seconds is plenty. The job moves at your rate limit, so a thousand videos take about five minutes whatever you do.
You don't have to wait for done to start reading. …/results already has everything finished so far.
/v1/transcripts/{job_id}/resultsfreeRead batch results
The transcripts, in the order they landed, a page at a time. Works while the job is still running, so the first page is readable long before the last video is fetched.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
job_id * | type string | default — | The id POST /v1/transcripts gave you. |
limit | type integer | default 100 | Results per page, 1–500. |
next_page_token | type string | default — | The token from the previous page. Omit it for the first one. |
* required
Example
Response fields
Inside data
job_idstring- The job this page belongs to.
statusstring- The job status, so one call answers "is there more" and "is it over".
langstring- The language the job was submitted with.
resultsarray- The page, in the order the results landed. A ready entry is exactly what GET /v1/transcript returns in data (video_id, language, kind, transcript) plus status and cache, so one parser reads both. A failed one is status: "error" with a machine code. Every entry names its own video_id.
countinteger- How many results are on this page.
has_moreboolean- Whether another page exists right now.
next_page_tokenstring | null- Pass it back to get the next page.
formatstring- The format the job was submitted with.
has_more answers "is there another page right now", not "is the job finished". On a running job it goes false and then true again as more results land. The status field is what tells you the job is over.
Branch on the machine code, never on the text. The codes are the ones the single endpoint returns, so one error handler covers both.
/v1/transcripts/{job_id}/cancelfreeCancel a batch job
Stops a job and refunds the videos it never started. Anything already fetched stays charged. It cost real work and it stays in your results, which canceling doesn't touch. Answers with the job status, in the same shape GET /v1/transcripts/{job_id} returns.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
job_id * | type string | default — | The id POST /v1/transcripts gave you. |
* required
Example
Response fields
Inside data
job_idstring- The job that was stopped.
statusstring- cancelled, once it has been.
langstring- The language the job was submitted with.
formatstring- The format the job was submitted with.
countinteger- Videos the job held.
doneinteger- What it managed to fetch. Those results remain readable.
readyinteger- How many of those came back with a transcript.
failedinteger- How many ended in an error before the stop.
pendinginteger- What it never reached. This is what was refunded.
creditsobject- { charged, refunded }. Here refunded includes every video the job never started.
created_atstring- When the job was accepted, ISO-8601.
finished_atstring- When it was stopped.
The line is whether a video produced a result, not whether it was still queued, so the batch a worker is holding when you cancel is delivered and stays charged.
Your results aren't deleted. Everything fetched before the cancel stays readable from …/results for the usual 24 hours.
Canceling twice is safe: the second call changes nothing and refunds nothing again.
/v1/video1 creditGet video info
Metadata for one video plus the list of transcript languages it has, without downloading any subtitles. Use it to check what exists before asking for a transcript.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
id * | type string | default — | Video id or any YouTube URL. |
* required
Example
Response fields
Inside data
video_idstring- Resolved video id.
titlestring- Video title.
channelstring- Channel name.
channel_idstring- Channel id (UC…).
durationstring- "M:SS" or "H:MM:SS".
view_countstring | null- View count as YouTube renders it: usually abbreviated, sometimes exact.
keywordsstring[]- Tags the author set, may be empty.
thumbnailsarray- Five sizes: { url, width, height }.
is_liveboolean- Whether this is (or was) a live broadcast.
available_langsarray- Every transcript track: { code, kind, name }.
available_langs here is authoritative: if a language is listed, GET /v1/transcript will return it. This is the reliable way to check for subtitles. The has_captions flag in search results is only a hint.
Fresh data is cached for an hour, since titles and view counts move.
/v1/search1 creditSearch YouTube
Search YouTube for videos or channels, exactly as the site would rank them.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
q * | type string | default — | Query, 1–200 chars. Optional if you pass next_page_token instead. |
type | type video | channel | default video | What to look for. |
limit | type integer | default 20 | Results per page, 1–50. |
next_page_token | type string | default — | Token from a previous response. See Pagination. |
* required
Example
Response fields
Inside data
querystring- The query these results answer.
typestring- video or channel.
countinteger- Number of results on this page.
resultsarray- Videos: id, title, channel, channel_id, duration, view_count, published, has_captions, url, thumbnails. Channels: id, title, handle, subscribers, description, verified, url, thumbnail.
next_page_tokenstring | null- Token for the next page, null when there's none.
has_moreboolean- Whether another page exists.
estimated_resultsinteger | null- YouTube's rough total. Informational. Don't compute page counts from it. Always null for type=channel.
has_captions is true when YouTube shows the "CC" badge and null when it doesn't. null means unknown, not "no subtitles". YouTube often omits the badge, especially for auto-generated tracks. There's deliberately no false value. For a definitive answer call GET /v1/video.
/v1/channel/videos1 creditList channel videos
Everything on a channel's Videos tab, newest first, paginated. Accepts a handle, a channel id or a URL.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
name * | type string | default — | @handle, channel name, UC… channel id, or channel URL. Optional if you pass next_page_token. |
limit | type integer | default 100 · 500 with ids_only | Page size, up to 100, or up to 500 in ids_only mode. |
next_page_token | type string | default — | Token from a previous response. |
ids_only | type boolean | default false | Return video_ids[] instead of full objects, up to 500 per page. |
* required
Example
Response fields
Inside data
channelstring- The identifier you passed in.
titlestring- Channel name.
countinteger- Items on this page.
videosarray- video_id, title, channel, duration, view_count, published, url, thumbnails. Replaced by video_ids with ids_only.
next_page_tokenstring | null- Token for the next page.
has_moreboolean- Whether another page exists.
A pattern worth knowing: enumerate a channel with ids_only=true (500 ids per call), then fetch transcripts only for the ids you actually need. That keeps the number of billed calls proportional to the work you want, not to the size of the channel.
/v1/channel/search1 creditSearch inside a channel
YouTube's own relevance search, scoped to one channel. Matches titles, descriptions, tags and even spoken words.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
name * | type string | default — | @handle, channel name, UC… channel id, or channel URL. |
q * | type string | default — | Query, 1–200 chars. |
limit | type integer | default 30 | Results per page, 1–100. |
next_page_token | type string | default — | Token from a previous response. Replaces id and q when paginating. |
* required
Example
Response fields
Inside data
channelstring- The identifier you passed in.
querystring- The query.
titlestring- Channel name.
countinteger- Items on this page.
videosarray- Same video shape as /v1/channel/videos.
next_page_tokenstring | null- Token for the next page.
has_moreboolean- Whether another page exists.
Because this is relevance search, results whose titles don't contain your query are normal. The match may be in the description, tags or the spoken audio. If you need a strict title match, filter the results yourself.
/v1/channel/latest1 creditA channel's latest videos
The ~15 most recent uploads of a channel, straight from its RSS feed. The fastest and lightest way to poll a channel for new videos.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
name * | type string | default — | @handle, channel name, UC… channel id, or channel URL. |
* required
Example
Response fields
Inside data
channelstring- The identifier you passed in.
channel_idstring- Resolved UC… id.
countinteger- Number of entries, normally 15.
videosarray- video_id, title, published (ISO 8601), thumbnails.
video_idsstring[]- The same ids as a flat array, for convenience.
Use this instead of /v1/channel/videos when you only care about what is new. It's the cheapest endpoint we have and doesn't go through our proxy layer at all.
This endpoint is the exception to the field formats: published is a full ISO 8601 timestamp rather than "5 days ago", and there's no duration or view_count. The RSS feed doesn't carry them.
/v1/playlist/videos1 creditList playlist videos
Videos of a playlist in playlist order, paginated. Accepts a playlist id or any URL containing list=.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
id * | type string | default — | PL… playlist id, or a URL with a list= parameter. Optional if you pass next_page_token. |
limit | type integer | default 100 · 500 with ids_only | Page size, up to 100, or up to 500 in ids_only mode. |
next_page_token | type string | default — | Token from a previous response. |
ids_only | type boolean | default false | Return video_ids[] instead of full objects, up to 500 per page. |
* required
Example
Response fields
Inside data
playliststring- The identifier you passed in.
titlestring- Playlist title.
countinteger- Items on this page.
videosarray- Same video shape as /v1/channel/videos. Replaced by video_ids with ids_only.
next_page_tokenstring | null- Token for the next page.
has_moreboolean- Whether another page exists.
/v1/playlist/search1 creditSearch inside a playlist
Find videos in a playlist by a substring of the title. YouTube has no native playlist search, so we page through the playlist and filter.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
id * | type string | default — | PL… playlist id, or a URL with a list= parameter. |
q * | type string | default — | Substring to match in the title, case-insensitive. |
limit | type integer | default 30 | Maximum matches to return, 1–100. |
* required
Example
Response fields
Inside data
playliststring- The identifier you passed in.
querystring- The substring you searched for.
titlestring- Playlist title.
countinteger- Number of matches returned.
scannedinteger- How many playlist items we looked at.
truncatedboolean- true means we hit the 500-item scan ceiling and more matches may exist beyond it.
videosarray- Matching videos, same shape as /v1/playlist/videos.
This one matches titles only, and it has no next_page_token. One call scans up to 500 playlist items and returns everything it found. Check truncated: if it's true, matches may exist past the window, and you should page through /v1/playlist/videos yourself instead.
Something missing or wrong here? Write to support@transcriptout.com , we keep these docs in sync with the API.