| name | tikhub-youtube-search |
| description | Lightweight TikHub YouTube search and video-detail workflow. Prioritizes single-request usage with curl or minimal Python, saves raw API JSON by default, and includes a small stdlib post-processor for CSV and simplified JSON. Use when the user wants YouTube comprehensive search results, continuation-token pagination, or structured video metadata from TikHub without a heavy wrapper. |
TikHub YouTube Search
What this skill gives you
This skill is optimized for the common case: one search or one video detail request.
It provides:
-
Minimal request patterns
curl for quickest validation
- tiny
httpx example for people who prefer Python
-
Raw JSON saving
- save the full TikHub response after each request
- useful for audit, replay, and later post-processing
-
One optional post-processor
postprocess_youtube_raw.py
- reads one raw file or a directory of raw files
- writes
youtube_search_summary.csv and youtube_search_summary.json
-
Optional batch guidance
- enough information for concurrent use later
- intentionally brief, not the main path
Does not import TikHub-Multi-Functional-Downloader or any other project package.
API key requirement
This skill intentionally does not contain any API key.
Use one of these:
- environment variable:
TIKHUB_API_KEY
- ask the user to provide an API key explicitly
If the key is missing, stop and ask for it instead of hardcoding one into scripts.
Install
pip install httpx
Post-processor: no extra packages.
API (for reference)
- Search:
GET https://api.tikhub.io/api/v1/youtube/web_v2/get_general_search_v2?keyword=...
- Video details:
GET https://api.tikhub.io/api/v1/youtube/web_v2/get_video_info?video_id=...&language_code=zh-CN&need_format=true
- Header:
Authorization: Bearer <API_KEY>
Notes from real requests
keyword should be passed through request params or URL-encoded.
need_format should be sent as true or false; a blank parameter may fail boolean parsing.
- Search responses usually contain
data.videos, data.shorts, data.channels, data.playlists, and sometimes continuation_token.
- Detail responses may have empty structured fields while display fields are populated;
view_count_text, like_count_text, and date_text are often more reliable than view_count, like_count, and upload_date.
Preferred path: single request
1. Quickest: curl
Search:
curl --location --request GET "https://api.tikhub.io/api/v1/youtube/web_v2/get_general_search_v2?keyword=Python%20tutorial" \
--header "Authorization: Bearer $TIKHUB_API_KEY"
Video detail:
curl --location --request GET "https://api.tikhub.io/api/v1/youtube/web_v2/get_video_info?video_id=_uQrJ0TkZlc&language_code=zh-CN&need_format=true" \
--header "Authorization: Bearer $TIKHUB_API_KEY"
With optional search filters:
curl --location --request GET "https://api.tikhub.io/api/v1/youtube/web_v2/get_general_search_v2?keyword=cute%20cats&type=video&sort_by=relevance" \
--header "Authorization: Bearer $TIKHUB_API_KEY"
2. Preferred Python pattern: tiny httpx
If the user wants Python, prefer a small request snippet, not a framework.
Search and save raw JSON:
import json
import os
import httpx
api_key = os.getenv("TIKHUB_API_KEY", "").strip()
if not api_key:
raise SystemExit("Missing TIKHUB_API_KEY")
url = "https://api.tikhub.io/api/v1/youtube/web_v2/get_general_search_v2"
params = {"keyword": "Python tutorial"}
headers = {"Authorization": f"Bearer {api_key}", "Accept": "*/*"}
with httpx.Client(timeout=30.0, follow_redirects=True) as client:
raw = client.get(url, params=params, headers=headers).json()
with open("youtube_search_raw.json", "w", encoding="utf-8") as f:
json.dump(raw, f, ensure_ascii=False, indent=2)
for item in raw.get("data", {}).get("videos", [])[:5]:
print(item.get("title", ""))
print(item.get("url", ""))
Detail and save raw JSON:
import json
import os
import httpx
api_key = os.getenv("TIKHUB_API_KEY", "").strip()
if not api_key:
raise SystemExit("Missing TIKHUB_API_KEY")
url = "https://api.tikhub.io/api/v1/youtube/web_v2/get_video_info"
params = {
"video_id": "_uQrJ0TkZlc",
"language_code": "zh-CN",
"need_format": "true",
}
headers = {"Authorization": f"Bearer {api_key}", "Accept": "*/*"}
with httpx.Client(timeout=30.0, follow_redirects=True) as client:
raw = client.get(url, params=params, headers=headers).json()
with open("youtube_detail_raw.json", "w", encoding="utf-8") as f:
json.dump(raw, f, ensure_ascii=False, indent=2)
data = raw.get("data", {})
print("title:", data.get("title", ""))
print("author:", data.get("author", ""))
print("views:", data.get("view_count_text", "") or data.get("view_count", ""))
print(, data.get(, ))
Save raw JSON by default
For this workflow, the recommended default is:
- request the API
- save the full raw JSON immediately
- print only a few useful fields for quick inspection
- optionally run the post-processor later
Suggested file naming:
- search raw:
search_<keyword>_<request_id>.json
- detail raw:
detail_<video_id>_<request_id>.json
If request_id is unavailable, hash the keyword or video ID.
Post-process raw JSON
Save as postprocess_youtube_raw.py (stdlib only).
Input:
- one raw search/detail JSON file
- or a directory containing multiple raw JSON files
Output:
youtube_search_summary.csv
youtube_search_summary.json
from __future__ import annotations
import argparse
import csv
import json
import os
import sys
from glob import glob
from typing import Any, Dict, List
def collect_inputs(path: str) -> List[str]:
if os.path.isfile(path):
return [path]
if os.path.isdir(path):
return sorted(glob(os.path.join(path, "*.json")))
raise FileNotFoundError(path)
def as_list(value: Any) -> List[dict]:
return value if isinstance(value, list) else []
def flatten_for_csv(row: Dict[str, Any]) -> Dict[str, Any]:
out: Dict[str, Any] = {}
for k, v in row.items():
if v :
out[k] =
(v, (, )):
out[k] = json.dumps(v, ensure_ascii=)
:
out[k] = v
out
() -> [, ]:
data = raw.get() {}
videos = as_list(data.get())
first_video = videos[] videos {}
{
: os.path.basename(source_file),
: ,
: raw.get(),
: raw.get(),
: raw.get(),
: data.get() (raw.get() {}).get(, ),
: (videos),
: (as_list(data.get())),
: (as_list(data.get())),
: (as_list(data.get())),
: data.get(, ),
: first_video.get(, ),
: first_video.get(, ),
: first_video.get(, ),
: first_video.get(, ),
: first_video.get(, ),
}
() -> [, ]:
data = raw.get() {}
{
: os.path.basename(source_file),
: ,
: raw.get(),
: raw.get(),
: raw.get(),
: data.get(, ),
: data.get(, ),
: data.get(, ),
: data.get(, ),
: data.get(, ),
: data.get(, ),
: data.get(),
: data.get(, ),
: data.get(, ),
: data.get(, ),
: data.get(, ),
: data.get(, ),
: data.get(, ),
: data.get(, ),
: data.get(, ),
: data.get(, ),
: data.get(, ),
: (as_list(data.get())),
}
() -> [, ]:
router = raw.get()
router:
simplify_search(raw, source_file)
router:
simplify_detail(raw, source_file)
{
: os.path.basename(source_file),
: ,
: raw.get(),
: raw.get(),
: router,
: ,
}
() -> :
ap = argparse.ArgumentParser(description=)
ap.add_argument(, , required=, =)
ap.add_argument(, , default=, =)
args = ap.parse_args()
:
files = collect_inputs(args.)
FileNotFoundError e:
(, e, file=sys.stderr)
files:
(, file=sys.stderr)
out_dir = os.path.abspath(args.out_dir)
os.makedirs(out_dir, exist_ok=)
csv_path = os.path.join(out_dir, )
json_path = os.path.join(out_dir, )
rows: [[, ]] = []
fp files:
:
(fp, , encoding=) f:
raw = json.load(f)
Exception ex:
rows.append({: os.path.basename(fp), : , : })
rows.append(simplify_raw(raw, fp))
(json_path, , encoding=) f:
json.dump(
{
: os.path.abspath(args.),
: (rows),
: rows,
},
f,
ensure_ascii=,
indent=,
)
flat = [flatten_for_csv(r) r rows]
fieldnames = ({k row flat k row.keys()})
(csv_path, , encoding=, newline=) f:
writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction=)
writer.writeheader()
row flat:
writer.writerow({k: row.get(k, ) k fieldnames})
(, csv_path)
(, json_path)
__name__ == :
SystemExit(main())
Commands:
python postprocess_youtube_raw.py --input ./youtube_api_raw
python postprocess_youtube_raw.py --input ./youtube_detail_raw.json --out-dir .
Optional: concurrent or multi-request usage
Only use this when the user clearly needs many keywords, many video IDs, or pagination at scale.
Keep the batching layer thin:
- accept a text file of keywords or video IDs
- call the same two endpoints in a loop or thread pool
- save one raw JSON per request
- reuse
postprocess_youtube_raw.py afterward
Recommended limits:
- start with
max_workers=3 to 5
- reduce concurrency if you hit
429
- keep filenames stable and collision-safe
Do not lead with a big wrapper if the task is only one search or one detail lookup.
End-to-end workflow
- Provide
TIKHUB_API_KEY.
- Make a single search or detail request with
curl or a tiny httpx snippet.
- Save the full raw response JSON.
- Inspect a few important fields directly.
- If needed, run
postprocess_youtube_raw.py on one file or a directory of raw files.
Troubleshooting
401/403: invalid API key or missing YouTube scopes.
429: rate limit; retry later or reduce concurrency.
need_format error: pass true or false, not a blank query parameter.
- Empty structured fields in detail: prefer
view_count_text, like_count_text, and date_text.
- No search results: keyword too narrow, region-dependent results, or temporary upstream changes.
What this skill does not cover
- downloading YouTube media streams
- comment crawling
- transcript extraction
- channel-wide crawling beyond what the search response already includes