YouTube search
GET /v1/search finds videos without a URL. Search all of YouTube, select channel or playlist results, or narrow a video search to a channel. The keyed MCP tool is search_youtube(query, type?, channel?, continuation?).
Each delivered page costs 1 credit, including a page with no matches. A failed request costs 0 credits. An identical query, type, channel, and continuation made with the same key within 60 seconds returns the same page for 0 credits. Beyond that retry window, results are fetched again because YouTube search changes. Inspect X-Credits-Charged and X-Credits-Balance on every response.
Request
| Parameter | Meaning |
|---|---|
q | Required search text, 1–200 characters. |
type | video (default), channel, or playlist. |
channel | Optional channel handle, public YouTube channel URL, or UC channel ID. Resolution costs no credits, is cached, and counts against the daily channel-resolution allowance. |
continuation | Optional opaque token returned by the preceding page, at most 2,048 characters. Send it unchanged with the same query, type, and channel. |
Queries are trimmed and must not contain control characters. Continuation tokens contain only letters, digits, _, %, +, =, /, or -; URL-encode the complete token as a query value.
Free keys can receive 50 search pages per UTC day; paid keys can receive 2,000. A separate allowance admits at most 150 search attempts per free key, or 6,000 per paid key, each UTC day. An admitted attempt counts even if it later fails; failed attempts still cost zero credits. Denied admission, an identical request still in progress, and a successful 60-second replay do not consume another attempt or delivered-page allowance. These counters are separate from cold transcript fetches. Search also shares the key's approximate requests-per-minute limit and daily credit cap. See credits and limits.
An identical request already in progress returns 429 QuotaExceeded with code search_in_progress, retryable: true, and Retry-After: 1. Wait at least one second, then repeat the same query, type, channel, continuation, and key. Once the first request delivers, that retry can receive its free replay. A failed first request has no delivered page to replay. Exhausting the attempt allowance returns search_attempt_cap with retryable: false and Retry-After until UTC midnight.
Response
This example illustrates the response shape; result metadata comes from YouTube and can be absent.
{
"ok": true,
"results": [
{
"type": "video",
"video_id": "jNQXAC9IVRw",
"title": "Me at the zoo",
"channel_id": "UC4QobU6STFB0P71PMvOGN5A",
"channel_title": "jawed",
"duration_seconds": 19,
"view_count": null,
"published_text": null,
"has_captions": false,
"thumbnail_url": "https://i.ytimg.com/vi/jNQXAC9IVRw/hqdefault.jpg",
"cached": true
}
],
"continuation": null,
"has_more": false
}
Each result has type and its video_id, channel_id, or playlist_id. Video results also carry cached, a recorded shared-cache hint. Search does not check the stored transcript separately for each result, so a missing object can briefly leave a stale hint until background indexing or backfill corrects it. The later transcript response reports the actual cache result and charge. has_captions reports a search caption badge: false does not establish that captions are unavailable, and a badge does not guarantee that a requested language can be delivered. published_text is YouTube's display text, not an exact date. Unknown durations, counts, and thumbnails may be null; channel metadata may be absent or empty when YouTube does not report it.
Malformed individual results are omitted while valid results on the page remain available. Recommendation shelves such as “Latest from” and “People also watched”, including their nested results and continuation tokens, are excluded from the organic results. Search playlist IDs contain 2–100 letters, digits, underscores, or hyphens; listing and latest-upload endpoints retain their own source validation. An empty delivered page still costs one credit.
When has_more is true, request the next page with continuation. The token is not a URL or an offset; do not decode or modify it. Each new delivered page costs one credit. Search finds YouTube results; listing enumerates a channel or playlist and reports the count of uncached videos for a bulk job.
Search in four languages
These examples find videos about climate inside TED's channel. Remove channel to search all of YouTube; set type=channel or type=playlist for those result types. Maintained runnable variants are in the examples directory.
curl --get https://api.scripthaul.com/v1/search \
-H "Authorization: Bearer $SCRIPTHAUL_API_KEY" \
--data-urlencode "q=climate" --data-urlencode "channel=@TED"
import os, urllib.parse, urllib.request
query = urllib.parse.urlencode({"q": "climate", "channel": "@TED"})
request = urllib.request.Request("https://api.scripthaul.com/v1/search?" + query,
headers={"Authorization": f"Bearer {os.environ['SCRIPTHAUL_API_KEY']}"})
print(urllib.request.urlopen(request).read().decode())
const url = new URL("https://api.scripthaul.com/v1/search");
url.search = new URLSearchParams({ q: "climate", channel: "@TED" });
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.SCRIPTHAUL_API_KEY}` },
});
console.log(await response.json());
query := url.Values{"q": {"climate"}, "channel": {"@TED"}}
req, _ := http.NewRequest("GET", "https://api.scripthaul.com/v1/search?"+query.Encode(), nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("SCRIPTHAUL_API_KEY"))
response, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer response.Body.Close()
io.Copy(os.Stdout, response.Body)
Errors and retries
The JavaScript and Python SDKs retry a failed YouTube or channel search at most once, across network and retryable HTTP failures combined. A configured retry limit of zero disables retries; raising it above one does not increase search attempts. Retries preserve the query, type, channel, and continuation, allowing a lost successful response to use the same-key free replay. Library search retains the SDK's ordinary configured retry limit.
Malformed queries, result types, channels, or continuation tokens return 400. Missing or invalid credentials return 401; a frozen account returns 403. An insufficient balance returns 402 with required, available, shortfall, and buy_url. Rate or daily limits return 429. Upstream and capacity failures return a typed error with retryable; all failures cost zero. Respect Retry-After and use bounded backoff. The error reference lists the emitted codes.
If the page cannot be settled, the response is 503 ConfigurationError with code search_settlement_failed and retryable: false. No page is returned and its credit reservation is released; a database outage can delay release until background cleanup succeeds. Contact support with X-Request-Id if this persists. If the database confirms that settlement already committed despite a lost acknowledgement, the API returns the saved successful page with X-Credits-Charged: 1 instead.