Skip to content

Repository files navigation

YouTube Suggest Scraper

YouTube Suggest Scraper

YouTube Suggest Scraper for extracting autocomplete keyword suggestions and related search terms, in any language, from YouTube. This repo has a free YouTube suggest script you can run right now, and a YouTube keyword data API that returns real structured JSON.

Last updated: 2026-07-20. Working against YouTube's suggest endpoint as of July 2026, and re-verified whenever the response shape changes.

The uncut responses behind every block on this page are committed in youtube_suggest_api_data/, captured 2026-07-20, and each block states what was trimmed. Every code example calls the API and is runnable from youtube_suggest_api_codes/.

This repo covers one endpoint in depth. For YouTube search results, video pages, channels, playlists, transcripts and comments, see the YouTube Scraper.

pip install requests
export CHOCODATA_API_KEY="your_key"     # free: 1,000 requests, one-time, no card
python youtube_suggest_api_codes/suggest.py "python"

Those three lines return this, live from YouTube's suggest endpoint (related_searches cut to 3 of 10, verbatim, and the full sample is uncut):

{
  "query": "python",
  "related_searches": ["python", "python for beginners", "python cowboy"],
  "related_count": 10
}

...and the same call across seven seed keywords:

Retrieved YouTube suggestion data

That is the whole point of this repo. The rest of this page is the free script, the things that actually bite you, and the endpoint with its parameters and response.


Contents


Free YouTube Suggest Scraper

YouTube exposes no "related searches" block in its search HTML. The source every keyword tool uses is Google's autocomplete ("suggest") endpoint scoped to the YouTube dataset (ds=yt), and the client=firefox variant returns clean JSON with no key and no JSONP wrapper. No headless browser, no JavaScript rendering:

python free_scraper/youtube_suggest_free_scraper.py "python" en

Source: free_scraper/youtube_suggest_free_scraper.py. It builds suggestqueries.google.com/complete/search?client=firefox&ds=yt&q=<query>, adds hl/gl if you pass a language and country, and reads the suggestion list out of the ["<query>", ["s1","s2", ...], ...] array.

After running the command, your terminal should look something like this:

Free YouTube suggest scraper returning suggestions

It works. A plain requests.get returns HTTP 200 with a suggestion list: three seeds (python, react, minecraft) each returned 10 suggestions, and a burst of 40 rapid calls with no delay returned 40 of 40 at HTTP 200, none throttled, on 2026-07-20 (survey). So blocking is not the failure mode on this surface. The things that actually bite you are the subject of the next section.

Avoid getting blocked when scraping YouTube suggestions

Getting blocked is not the interesting failure mode here, because the endpoint answers a plain client. These are, and all of them are measured against YouTube's suggest endpoint on 2026-07-20.

First, with no language the locale is not pinned. Google's suggest endpoint localises to the caller, so a request with no language/country is not tied to any one locale. Called six times for the bare seed python in 16 seconds, it came back in a different language on nearly every call:

The same bare seed returns a different language per call

That is one seed, six consecutive calls, no locale pinned (full capture). A keyword list built without pinning the locale is a mix of German, Spanish, Japanese and English terms that describes no single audience. The fix is to pass language, and to pass country with it: language=en alone still lets the country bleed in, while language=en and country=us together returned a byte-identical list on every call.

Second, that same lever is a feature. Pin language and you get YouTube's suggestions for that audience. The same seed, five languages, each list localised end to end:

The same seed across five languages

Third, the seed echoes back. related_searches[0] is usually the seed itself ("python" for the python query), so related_count includes it. Dedupe against your seed or every list double-counts the term you already had.

Here is the full picture, and what each item costs you:

What bites you Why What it costs you
No language means no fixed locale Google localises suggestions to the caller, so an unpinned call is not tied to one locale and the language shifts between calls. A keyword list that is a mix of four languages and describes no single market. Pin language and country.
language alone is not deterministic The country (gl) is still unset, so it is not pinned either. language=en on its own drifts; language=en with country=us was byte-identical across calls. Pin both.
The seed is echoed as row 0 YouTube returns the typed query back as the first suggestion. related_count counts it. Filter s == seed or your expansion re-queries the seed and inflates the list.
These are YouTube-search terms, not web terms python surfaces python cowboy, python funk and python got7, which are creators and music, because this is YouTube's own search autocomplete. Excellent for video-title and tag research, different from a Google web keyword tool. Do not treat it as one.
It is an undocumented endpoint Google's autocomplete is an internal endpoint, not a supported API, and it occasionally answers a 502 before serving. It can change shape or refuse automated traffic without notice, so build a retry in and alert on an empty list.
The list caps at 10 The client=firefox response returns up to ten suggestions per call and no pagination. It is a seed expander, not a keyword database. Go wider by expanding the terms it returns, not by asking for more per call.

yt-dlp and similar tools reach this same endpoint, so a free path exists and works. What most free wrappers do not do is pin the locale for you, retry the occasional refusal, or carry a large crawl so it is not running from your own machine, which is what the section below is about.


Using the Chocodata YouTube Suggest Scraper API

The managed option, and the one this repo is built around. The Chocodata YouTube Suggest Scraper API returns YouTube's autocomplete suggestions as parsed JSON for YouTube keyword data extraction at scale, with language and country as parameters, a ~99% success rate, retries on the occasional refusal, and a large crawl that does not run from your own machine. Free for the first 1,000 requests.


YouTube Suggest Scraper API reference

Below is the YouTube Suggest Scraper API reference to get you started: authentication, the parameters, errors and rate limits, then the endpoint with its response.

Quickstart

curl "https://api.chocodata.com/api/v1/youtube/suggest?api_key=YOUR_KEY&query=python&language=en&country=us"
import requests

r = requests.get(
    "https://api.chocodata.com/api/v1/youtube/suggest",
    params={"api_key": "YOUR_KEY", "query": "python", "language": "en", "country": "us"},
    timeout=90,
)
print(r.json()["related_searches"])
# ['python', 'python for beginners', 'python cowboy', 'python full course', ...]

After running the command, your terminal should look something like this:

Running the YouTube Suggest Scraper API

Get a key at chocodata.com (1,000 requests, one-time, no card).

Authentication

Pass api_key as a query parameter on every request. That is the whole auth model. There is no header form and no OAuth step.

Parameters

Param Type Required Default Description
api_key string yes - Your Chocodata API key.
query string yes - Seed keyword. The parameter is named query: q is not accepted and returns 400.
language string no unset The hl language code, e.g. en, de, es, ja. Changes the returned suggestions. Unset, the language is not pinned and is not stable between calls.
country string no unset The gl country code, e.g. us, de. Pin it together with language for a deterministic list.

Each request costs 5 credits (= 1 request). Responses are billed only on success (2xx).

language and country both change the terms, verified across en, es, de, ja, fr and pt on 2026-07-20 (matrix). language is the stronger lever; set country alongside it for a result that does not drift.

Errors

Nothing below is billed: you are only charged on a 2xx.

Status error code Meaning Billed What to do
400 invalid_params query is missing or the wrong type. The body names the offending path. no Fix the query string. Use query, not q.
401 INVALID_API_KEY Key missing, unrecognised, or revoked. no Check api_key. Get one at chocodata.com.
402 INSUFFICIENT_CREDITS Balance exhausted. no Top up or upgrade at chocodata.com.
429 RATE_LIMITED Over the rate limit or your plan's concurrency. no Back off and retry; see Rate limits.
502 target_unreachable The suggest endpoint returned nothing parseable for this request. retryable: true. no Retry after a few seconds.

Two response shapes exist: auth and billing errors nest under error.code (uppercase), while scrape-layer errors are flat with a lowercase error string plus retryable.

A bad key, verbatim:

curl "https://api.chocodata.com/api/v1/youtube/suggest?api_key=totally_invalid_key_123&query=python"
{"error":{"code":"INVALID_API_KEY","message":"Api key not recognised."}}

Calling it with no query, verbatim:

curl "https://api.chocodata.com/api/v1/youtube/suggest?api_key=YOUR_KEY"
{"error":"invalid_params","issues":[{"code":"invalid_type","expected":"string","received":"undefined","path":["query"],"message":"Required"}]}

The scripts in this repo turn each documented status into an actionable message, so a typo'd key does not hand you a stack trace:

YouTube Suggest Scraper API error handling

Build the retry in. A 502 here is retryable and uncharged, and it does happen, because Google occasionally refuses the request. Both scripts in this repo retry once after 8 seconds before giving up on a request, and that is the pattern to copy.

Rate limits and concurrency

Two limits apply at once, and the one that binds first is usually the rate limit, not the concurrency cap.

Rate limit: 120 requests per 60 seconds, per key (sliding window). That is a hard ceiling of 2 requests/second sustained, whatever your plan.

Concurrency: how many requests you may have in flight at once, which varies by plan:

Plan Concurrent requests
Free 10
Vibe 30
Pro 50
Custom 100 to 500+

Exceed either and you get 429, not a queue. Every request is a synchronous GET: there is no webhook, callback, or async job to poll. The examples use timeout=90 for headroom, because a request that runs into a refusal and is re-attempted takes longer than the median suggests.

Size against the rate limit. A breadth-first expansion of one seed is 1 call at depth 1, up to 9 at depth 2 and up to 81 at depth 3, so about 91 calls at the ceiling, roughly 46 seconds of rate-limited pulling. Keep your pool at or under your plan's concurrency and pace it to the rate limit:

from concurrent.futures import ThreadPoolExecutor
import requests

def one(q):
    r = requests.get("https://api.chocodata.com/api/v1/youtube/suggest",
                     params={"api_key": KEY, "query": q, "language": "en", "country": "us"}, timeout=90)
    return q, r.json().get("related_searches", []) if r.ok else []

seeds = ["python", "react", "minecraft", "guitar lesson", "yoga for beginners"]
with ThreadPoolExecutor(max_workers=10) as pool:   # <= your plan's concurrency
    for q, terms in pool.map(one, seeds):
        print(q, terms)

Suggest: YouTube autocomplete keyword suggestions

YouTube's own autocomplete suggestions for a seed keyword, as a flat list of query strings. This is keyword research straight from the platform's search box: it reflects what viewers actually type on YouTube rather than what a keyword tool models, and it is the demand signal for video titles and tags.

Param Type Required Default Description
query string yes - Seed keyword. Named query, not q.
language string no unset hl code. Changes the suggestions.
country string no unset gl code. Pin with language for determinism.
curl "https://api.chocodata.com/api/v1/youtube/suggest?api_key=YOUR_KEY&query=python&language=en&country=us"

Real response. Complete and verbatim, all 10 suggestions, language=en and country=us (full sample):

{
  "query": "python",
  "related_searches": [
    "python",
    "python for beginners",
    "python cowboy",
    "python full course",
    "python funk",
    "python got7",
    "python commands",
    "python tutorial",
    "python projects",
    "python course"
  ],
  "related_count": 10
}

related_searches is the field you came for: a flat list of strings, not objects, ordered as YouTube ranks them. Two things to code around, both real on this sample. related_searches[0] is the seed echoed back ("python"), so filter it if you do not want the term you already had, and related_count includes it. And the list is YouTube-search-shaped: python cowboy, python funk and python got7 are a creator, a music genre and a K-pop group, not programming topics, because this is YouTube's search autocomplete rather than a web keyword tool.

There is no page parameter and the response caps at 10, so this is a seed expander rather than a keyword database. To go wider, expand the terms it returns, which is what the next section does.

Runnable: youtube_suggest_api_codes/suggest.py


Build a YouTube keyword list from one seed

Expanding a seed through YouTube's own autocomplete is the main reason people scrape suggestions, so that use case is in the repo end to end rather than as a snippet. keyword_expander.py walks the graph breadth-first, pins the locale with --language, drops the seed echo, dedupes, and writes the result to CSV:

python youtube_suggest_api_codes/keyword_expander.py "python" --depth 2 --language en

Expanding one YouTube seed keyword into a keyword list

That is a real run: 91 unique keywords from 10 API calls. Because the seed echo is dropped, each node contributes 9 rather than 10, and depth 2 stays productive here rather than looping back on itself the way a retailer's related-search graph does. Pin --language (and --country) so every level is harvested in the same locale rather than drifting between them.

The CSV carries keyword, parent, depth, youtube_search_url, where youtube_search_url is the standard results URL for the term, built locally, so each row can feed a downstream search scraper.


Measured latency

Real end-to-end wall-clock, measured from a laptop against the live API on 2026-07-20:

Endpoint Median Range n
/youtube/suggest 1.8s 1.2 to 4.0s 6

Read the range, not just the median. This is the fastest endpoint in the suite because the upstream is a small JSON document rather than a rendered page, but a call that runs into a refusal and is re-attempted lands in the tail. Small sample (n=6); reproduce it yourself with the scripts here.


License

MIT. See LICENSE.