YouTube Shorts Trend Research API: A Python Workflow
Research YouTube Shorts trends with keyword search, continuation pagination, video details, channel context, comments, and honest local scoring.

YouTube Shorts trend research often starts with a misleading shortcut: sort a search by views and call the winners “trends.” That ignores publication time, ambiguous formatted counts, channel baselines, and the questions viewers actually ask. This tutorial builds a defensible workflow from the public endpoints in the YouTube API catalog: search, page, normalize, enrich a shortlist, and preserve the evidence behind every conclusion.
The live calls were made on October 25, 2026 (UTC) with “AI agents.” The nested fields are observed response data. Endpoint schemas and availability can change. For channel-first research, see the YouTube channel research API guide.
Key takeaway
- Search results can carry formatted view counts such as
5.8K views; parse them before sorting.- The tested continuation request needed both the original keyword and
continuation_token.- One tested combination,
this_monthplusview_count, returned no Shorts; an empty page is data, not a reason to invent results.- Enrich only a shortlist with video info, comments, and channel Shorts.
What the endpoint actually returned
The unfiltered search returned a shorts array, the original keyword, and a continuation token. The first item exposed video_id, title, URL, thumbnail, and a formatted view-count string. Author and channel fields were present but empty on that item, so the pipeline cannot assume search always identifies a channel.
The search response is useful for discovery, but its formatted counts need normalization.
The first attempt to request page two with only the token returned HTTP 400. Sending the keyword with the same token succeeded and produced another Shorts page plus a new continuation token. This observed requirement belongs in saved job state and regression tests.
For this endpoint and test, page two required the original keyword as well as the token.
The comment endpoint returned a comment list and its own continuation token. It is most useful after ranking: repeated questions can reveal follow-up demand that raw views cannot show.
Comments add qualitative demand signals after deterministic filtering.
Install and authenticate
python -m pip install requests
export SANDBASE_API_KEY="your_key_here"
import os, time, requests
BASE = "https://api.sandbase.ai/v1/api"
HEADERS = {"Authorization": f"Bearer {os.environ['SANDBASE_API_KEY']}",
"Content-Type": "application/json"}
def call(model, payload, retries=2):
for attempt in range(retries + 1):
r = requests.post(f"{BASE}/{model}", headers=HEADERS,
json=payload, timeout=90)
if r.status_code < 500 and r.status_code != 429:
r.raise_for_status(); break
if attempt == retries: r.raise_for_status()
time.sleep(2 ** attempt)
body = r.json()
if body.get("status") != "completed":
raise RuntimeError(body.get("error") or body.get("status"))
try: return body["outputs"][0]["data"]
except (KeyError, IndexError, TypeError) as exc:
raise RuntimeError("Missing outputs[0].data") from exc
Search and page without losing context
def search_shorts(keyword, pages=2, sort_by=None, upload_date=None):
token, rows, seen = None, [], set()
for _ in range(pages):
payload = {"keyword": keyword}
if sort_by: payload["sort_by"] = sort_by
if upload_date: payload["upload_date"] = upload_date
if token: payload["continuation_token"] = token
page = call("youtube/web-v2/shorts-search-v2", payload)
for item in page.get("shorts") or []:
video_id = item.get("video_id")
if not video_id or video_id in seen: continue
seen.add(video_id)
rows.append(item)
token = page.get("continuation_token")
if not token: break
return rows
Do not automatically loosen filters when a page is empty. Record the empty result, then run a separately labelled broader query. In my test, the restrictive query returned an empty shorts array while still returning a continuation token. That is a useful edge case for monitoring.
Normalize formatted counts
import re
def parse_count(value):
if isinstance(value, int): return value
if not value: return None
text = str(value).upper().replace(",", "")
match = re.search(r"([0-9]+(?:\.[0-9]+)?)\s*([KMB]?)", text)
if not match: return None
number = float(match.group(1))
scale = {"": 1, "K": 1_000, "M": 1_000_000, "B": 1_000_000_000}
return int(number * scale[match.group(2)])
for row in results:
row["views_numeric"] = parse_count(row.get("view_count"))
Keep the original string too. If YouTube changes localization or formatting, the raw value makes the parser’s decision auditable.
Enrich the shortlist
def enrich(video_id, channel_id=None):
info = call("youtube/web-v2/video-info-v2", {"video_id": video_id})
comments = call("youtube/web-v2/video-comments", {
"video_id": video_id, "sort_by": "top",
"need_format": True, "language_code": "en",
})
channel = None
if channel_id:
channel = call("youtube/web-v2/channel-shorts", {
"channel_id": channel_id, "need_format": True
})
return {"info": info, "comments": comments.get("comments") or [],
"channel_shorts": channel}
Channel enrichment is conditional because the first observed search item had an empty channel ID, and the tested video-info response exposed only publish_date, rating, thumbnail_url, and video_id. A robust pipeline skips unavailable enrichment instead of constructing a channel identifier.
A separate check used OpenAI’s public channel ID and confirmed that channel-shorts completed; it does not connect the search result above to OpenAI. When a channel baseline is available, compare the Short with the median of a fixed recent sample. Add freshness only when publish_date parses successfully. Keep comment themes separate from quantitative scores: an LLM may cluster comments, but it should receive a bounded sample and return citations to source comment positions.
Configuration reference
| Endpoint | Required | Useful optional fields | Pagination |
|---|---|---|---|
shorts-search-v2 | keyword on first request | sort_by, upload_date, continuation_token | returned continuation token |
channel-shorts | channel_id or channel_url | need_format, token | returned continuation token |
video-info-v2 | video_id or video_url | need_format | none |
video-comments | video_id | sort_by, language/country, need_format, token | returned continuation token |
Production safeguards
Persist query, filters, token, capture time, and parser version together. Cap pages and comment samples. Retry 429/5xx and transient network failures with backoff; send 400 responses to review because changing the query silently changes the research question. Treat empty strings as unavailable, not valid IDs. Never log the API key or temporary media URLs.
The report should distinguish “top results for this query at this time” from platform-wide trends. Search is query-dependent and personalized systems can change. Use several related queries, compare recurring themes, and keep the sampled video IDs available to the reviewer.
Troubleshooting and FAQ
- Page two returns 400: include the original keyword with the continuation token.
- A filtered search is empty: preserve the result and run a separately named broader query.
- View count sorts incorrectly: parse suffixes before sorting and keep the original string.
- Channel ID is blank: skip channel enrichment; do not derive it from a title.
- Comments are missing: the video may have no returned comments; keep the sample size at zero.
Does this identify every YouTube-wide trend?
No. It ranks evidence returned for explicit queries. Platform-wide claims need broader sampling and a documented methodology.
Can I use comments as training data?
This workflow is for public research. Review rights, terms, retention, and privacy requirements before any reuse beyond aggregate analysis.
Where can I check current options?
Use the live YouTube API catalog for current schemas and availability.