Back to blog

YouTube API · Search · JavaScript

YouTube Search Suggestions API: Autocomplete Without the Data API

The YouTube Data API has no autocomplete method. Here is the undocumented endpoint behind YouTube's search box, what it returns, and how to get the same suggestions as plain JSON.

· 2 min read · YTAPI

Type "how to make" into YouTube's search box and it offers "how to make a paper airplane", "how to make slime", "how to make pancakes". Those suggestions are useful outside the search box too: keyword ideas for video titles, a list of questions an audience is asking, or autocomplete in your own app.

The YouTube Data API doesn't have them. search.list returns search results, costs 100 quota units a call, and has no autocomplete mode.

YouTube's own suggestions come from Google's suggest service, with ds=yt to restrict them to YouTube:

curl "https://suggestqueries.google.com/complete/search?client=youtube&ds=yt&q=how%20to%20make"

The answer is JavaScript, not JSON, in ISO-8859-1:

window.google.ac.h(["how to make",[["how to make a paper airplane",0,[512,433,131]],["how to make slime",0,[512,433]],...

With client=firefox you get a plain JSON array in UTF-8 instead:

["how to make", ["how to make a paper airplane", "how to make slime", "how to make pancakes"], [], {}]

hl sets the language and gl the country: hl=ko with a Korean query returns Korean suggestions.

It works, and it needs no key. It is also undocumented. There is no published rate limit, no guarantee about the client values, and the contract is an array index (data[1]). That is fine for a script you run yourself. For a product feature, plan for the day the format changes.

The same suggestions as JSON

YTAPI returns them from GET /v1/search/suggestions, with the key you use for transcripts. It costs no credits.

curl -H "Authorization: Bearer $YTAPI_KEY" \
  "https://api.ytapi.dev/v1/search/suggestions?q=how%20to%20make"
{
  "query": "how to make",
  "suggestions": ["how to make a paper airplane", "how to make slime", "how to make pancakes"]
}

The same hl and gl parameters target an audience. Here is "futbol" as searched in Mexico:

curl -H "Authorization: Bearer $YTAPI_KEY" \
  "https://api.ytapi.dev/v1/search/suggestions?q=futbol&hl=es&gl=MX"

Keep the key on your server and give the browser a small route to call. In Next.js:

// app/api/suggest/route.ts
export async function GET(req: Request) {
  const q = new URL(req.url).searchParams.get("q")?.trim();
  if (!q) return Response.json({ suggestions: [] });

  const res = await fetch(
    `https://api.ytapi.dev/v1/search/suggestions?q=${encodeURIComponent(q)}`,
    { headers: { Authorization: `Bearer ${process.env.YTAPI_KEY}` } },
  );
  if (!res.ok) return Response.json({ suggestions: [] });
  const { suggestions } = await res.json();
  return Response.json({ suggestions });
}

In the browser, wait until typing pauses and cancel the previous request, so an old answer never overwrites a newer one:

let timer: ReturnType<typeof setTimeout> | undefined;
let inflight: AbortController | undefined;

function onInput(value: string, show: (s: string[]) => void) {
  clearTimeout(timer);
  timer = setTimeout(async () => {
    inflight?.abort();
    inflight = new AbortController();
    try {
      const res = await fetch(`/api/suggest?q=${encodeURIComponent(value)}`, { signal: inflight.signal });
      show((await res.json()).suggestions);
    } catch {
      // aborted by a newer keystroke
    }
  }, 150);
}

Keyword research: expand a seed

Suggestions only show the beginning of the long tail. Adding one letter at a time surfaces more of it:

import os, string, time
import requests

def expand(seed: str) -> list[str]:
    found = []
    for suffix in [""] + list(string.ascii_lowercase):
        res = requests.get(
            "https://api.ytapi.dev/v1/search/suggestions",
            headers={"Authorization": f"Bearer {os.environ['YTAPI_KEY']}"},
            params={"q": f"{seed} {suffix}".strip()},
            timeout=10,
        )
        res.raise_for_status()
        found += [s for s in res.json()["suggestions"] if s not in found]
        time.sleep(0.2)
    return found

print(expand("python tutorial"))

27 requests give you a few hundred phrases people actually type. To see what already ranks for one of them, GET /v1/search returns the videos (1 credit per page), and the Python guide covers fetching their transcripts.

FAQ

Is there an official YouTube autocomplete API?

No. The Data API's search.list returns videos, channels and playlists for a finished query. The suggestions in YouTube's search box come from Google's undocumented suggest service shown above.

Do suggestions come with search volume?

No. They're ranked by YouTube, with no numbers attached. Treat the order as a rough signal of what people search for, not a measurement.

Why don't my results match what I see on youtube.com?

When you're signed in, YouTube mixes your own history into the suggestions. Language and country change them too. Pass hl and gl to match the audience you care about, and compare against a signed-out browser.