Get a transcript — plus metadata, frames, on-screen text, or a grounded answer — from any YouTube, TikTok, Instagram, Reddit, or Pinterest video URL, with a single requests.post. No yt-dlp to install and keep updated, no ffmpeg on your machine, no Whisper model to download and run. The heavy parts happen server-side; your code stays one HTTP call.
Getting a transcript "in Python" normally means a stack you have to own: yt-dlp pinned and updated every time a site changes its player, ffmpeg installed, a Whisper model downloaded and a GPU to run it on, plus proxy handling when a platform blocks your datacenter IP. FrameFetch runs all of that and hands you the result. Your dependency list is one line — an HTTP client — and it stays that way as the platforms underneath keep moving.
pip install requests
That's the only dependency. httpx or the standard-library urllib.request work identically — this is a plain JSON-over-HTTPS API.
POST the video's URL with fields: ["transcript"]. You get back the flat transcript text and timestamped segments — plus a source telling you whether it came from the video's own captions or from Whisper transcribing the audio.
import requests
resp = requests.post(
"https://framefetch.net/v1/extract",
headers={"Authorization": "Bearer <your-key>"},
json={
"url": "https://www.youtube.com/watch?v=Bj9BD2D3DzA",
"fields": ["metadata", "transcript"],
},
timeout=180,
)
resp.raise_for_status()
data = resp.json()
print(data["metadata"]["title"])
print(data["transcript"]["source"], len(data["transcript"]["segments"]))
for seg in data["transcript"]["segments"][:3]:
print(f"[{seg['start']:.0f}s] {seg['text']}")# output — a real, measured run against the live API
Tracing the thoughts of a large language model
captions 66
[0s] you often hear that AI is like a black box ...
[7s] you can put something in and get a response ...
[13s] but you don't really know why it gave that ...This exact call was run against production while writing this page: the transcript came from captions (66 segments) — no audio download, so it's fast and cheap. On a video with no caption track the same call returns source: "whisper" instead, transcribed from the audio server-side; your Python doesn't change. Read the timing as one observation, not a benchmark.
You don't pick the transcription path — FrameFetch does, cheapest-first. If the video carries a usable caption track it's fetched directly: exact, fast, and no per-minute audio charge. Only when there's no caption track does it fall back to Whisper on the audio. The transcript.source field reports which happened ("captions" or "whisper"), so in code you can treat them differently if you want — but you never have to run either one.
| Field | Shape | What it is |
|---|---|---|
transcript.text | string | The full transcript as one flat string |
transcript.segments | list | Cue objects: { start, end, text } with timestamps in seconds |
transcript.source | string | "captions" (fetched) or "whisper" (transcribed from audio) |
transcript.lang | string | null | Detected language of the track, when known |
When you don't have a URL yet, start from a keyword. POST /v1/search returns candidate YouTube videos; take a result's url and pass it to /v1/extract with a top-level ask — a short answer grounded in the transcript with timestamped quotes, instead of pulling the whole transcript into your own code and searching it.
import requests API, KEY = "https://framefetch.net", "<your-key>" h = {"Authorization": f"Bearer {KEY}"} # 1) find candidates from a topic hits = requests.post(f"{API}/v1/search", headers=h, json={"query": "what is an api", "limit": 3}, timeout=60).json() # 2) ask the top hit a direct question answer = requests.post(f"{API}/v1/extract", headers=h, json={"url": hits["results"][0]["url"], "ask": "What is an API in one sentence?"}, timeout=180).json() print(answer["ask"]["answer"]) print(answer["ask"]["confidence"], len(answer["ask"]["quotes"]), "quotes")
# output — a real run; the answer carries timestamped quotes and a confidence
An API, or Application Programming Interface, is a connection between apps that ...
high 3 quotesTwo calls: one to turn a topic into URLs, one to turn the best URL into a grounded answer. search is YouTube-only and a flat $0.002 per call; ask is a flat $0.0075 and is never cached, so a repeated question always gets a fresh answer.
The free tier is 100 calls a month. When credit runs out, the API returns 402 — with a machine-readable body so your code can react rather than crash. Catch it explicitly:
resp = requests.post(f"{API}/v1/extract", headers=h, json=payload, timeout=180) if resp.status_code == 402: body = resp.json() # { error, balance_micros, price_floor_micros, topup } raise RuntimeError(f"out of credit: {body['topup']['pricing']}") resp.raise_for_status() data = resp.json()
Every response also carries a cost.totalMicros so you can meter spend per call. Prices: metadata $0.00015, transcript $0.0015/min, frames $0.00012 each, on-screen text (OCR) $0.000225 each, ask $0.0075/call, with a $0.002 minimum per call. Full breakdown on pricing.
Grab a free key with one call — no card, no dashboard step:
import requests key = requests.post("https://framefetch.net/v1/keys", json={"email": "you@example.com"}).json()["key"] print(key) # ff_... — 100 free calls a month, use it as the Bearer token above