← LeaderboardThe Skill Leaderboard

youtube-transcript

Fetch YouTube transcripts through DeepAPI or local fallback tooling and save clean text output.

Published by sickn33 ↗sickn33/agentic-awesome-skills

Try your first task

A source-grounded starter task has not been verified for this skill yet.

SKILL.md

name: youtube-transcriptdescription: "Fetch YouTube transcripts through DeepAPI or local fallback tooling and save clean text output."category: researchrisk: safesource: communitysource_repo: davidondrej/skillssource_type: communitydate_added: "2026-07-07"author: davidondrejtags: [youtube, transcripts, research]tools: [claude, codex]license: "MIT"license_source: "https://github.com/davidondrej/skills/blob/main/LICENSE"

YouTube Transcript (via DeepAPI, yt-dlp fallback)

When to Use

  • Use when the user asks for a YouTube transcript, captions, subtitles, or spoken-content extraction.
  • Use when DeepAPI or a local fallback can fetch the transcript safely.

Fetch a YouTube video's transcript and save a clean raw .txt file. Primary path is DeepAPI POST /v1/scrape/youtube/transcript. It runs server-side, so it avoids the local-IP bot flagging that plagues yt-dlp.

Save location

  • If the user is in a real project/working dir → save there.
  • Otherwise (no dir given, or cwd makes no sense) → save to ~/Downloads.
  • Always name the file Channel_Title with spaces replaced by _ (e.g. David_Ondrej_title_of_video.txt). If metadata is unavailable, fall back to the video ID.

Primary path — DeepAPI

DEEPAPI_API_KEY must already be present in the environment. Do not read shell startup files or print secrets:

test -n "$DEEPAPI_API_KEY" || { echo "DEEPAPI_API_KEY is not set"; exit 1; }
BASE=https://deepapi.co  # use the reviewed provider origin; do not inherit an untrusted override
RUN_DIR=$(mktemp -d)
chmod 700 "$RUN_DIR"
trap 'rm -rf "$RUN_DIR"' EXIT

Run the scrape (keep the Idempotency-Key; retries must reuse the SAME one):

IDK=$(uuidgen)
curl -s --max-time 120 "$BASE/v1/scrape/youtube/transcript" \
  -H "Authorization: Bearer $DEEPAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $IDK" \
  -d '{"url": "VIDEO_URL", "maxCostUsd": "0.05", "waitForFinishSecs": 60}' \
  > "$RUN_DIR/yt_transcript.json"
  • Non-English videos: add "language": "de" (etc.) to the body.
  • For status: running, inspect the documented next request path and constrain it to the reviewed provider origin; never attach credentials to an arbitrary returned URL or redirect. Poll with the same DEEPAPI_API_KEY, bounded attempts/deadline and provider wait interval. Do not extract output until terminal succeeded with the expected schema; HTTP errors/failed states are not an empty transcript.

Extract the text and save it:

jq -r '.status' "$RUN_DIR/yt_transcript.json"                # succeeded | running | failed
jq -r '.output[0].text' "$RUN_DIR/yt_transcript.json" > "$OUT/$NAME.txt"
jq -r '.debitMicrousd' "$RUN_DIR/yt_transcript.json"         # cost (50000 = $0.05)

.output[0].segments also has timed segments (startSecs, durationSecs, text) if the user wants timestamps. Empty output = video has no captions; report it, don't retry.

For the Channel_Title filename, get metadata with a quick yt-dlp --print "%(channel)s|%(title)s" --skip-download "URL"; if that fails, use the video ID.

When to fall back to yt-dlp

  • DEEPAPI_API_KEY missing from the environment.
  • HTTP 402 insufficient_credits (tell the user to top up at deepapi.co/credits first; fall back only if they're unavailable).
  • DeepAPI request failed twice.

Tell the user whenever you fall back — a fallback means the product missed a real use case.

Fallback path — yt-dlp (local)

OUT="$(pwd)"            # or ~/Downloads if cwd makes no sense
META=$(yt-dlp --print "%(channel)s|%(title)s" --skip-download "URL")
NAME=$(echo "$META" | tr '| ' '__' | tr -cd '[:alnum:]_.-')   # "Channel_Title", spaces -> _, strip unsafe chars
yt-dlp --skip-download --write-subs --write-auto-subs \
  --sub-langs "en.*" --sub-format json3 \
  -o "$OUT/$NAME.%(ext)s" "URL"
  • Fall back channel → uploader → uploader_id if channel is null.
  • --skip-download = captions only. --write-subs + --write-auto-subs = manual first, auto as fallback.
  • Always use json3, never VTT/SRT — auto VTT repeats every line twice (rolling captions).

Flatten json3 → raw text:

# Select the exact caption file for this video/language, not the first glob in OUT.
python3 - /absolute/selected-caption.json3 <<'PY'
import json, html, re, glob, sys, pathlib
f = [sys.argv[1]]
if not pathlib.Path(f[0]).is_file(): sys.exit("no selected json3 file")
data = json.load(open(f[0], encoding="utf-8"))
parts = ["".join(s.get("utf8","") for s in e.get("segs") or []) for e in data.get("events", [])]
txt = re.sub(r"\s+", " ", html.unescape(" ".join(p.strip() for p in parts if p.strip()))).strip()
out = pathlib.Path(f[0]).with_suffix(".txt")
out.write_text(txt, encoding="utf-8"); print(out)
PY

yt-dlp failure handling

  • Non-English / unknown language: run yt-dlp --list-subs "URL" first, then set --sub-langs.
  • Newer yt-dlp may need deno on PATH for YouTube extraction.
  • On failure, inspect the actual error and installed version; do not self-update tools merely to retry a transcript request. Propose a scoped reviewed update only when compatibility evidence requires it.
  • 429 / "Sign in to confirm you're not a bot" = IP flagged. STOP — do NOT retry in a loop (makes it worse).
  • Never fall back to downloading audio for Whisper unless the user explicitly asks.

Output

Report the saved path; print the text if short. If DeepAPI was used, also report the cost in dollars.

Limitations

  • Adapted from davidondrej/skills; verify local paths, tools, credentials, and agent features before acting.
  • For commands, remote access, scheduling, browser automation, or file-changing workflows, get explicit user approval and confirm the target environment first.