Quickstart
Get a key with an email address, then make one authenticated request. No password is required.
1. Get a key
Sign up on api.scripthaul.com: enter your email, type the six-digit code from the message, accept the terms, and copy the sh_live_… key shown once. Sign-up runs in the browser behind a bot check, so there is no scripted step for it; more keys, rotation and revocation live on the dashboard and /v1/keys.
Store the key in a secret manager. Never put it in a URL, log, browser bundle, or query string.
export SCRIPTHAUL_API_KEY="sh_live_replace_me"
2. Fetch a transcript
curl --get https://api.scripthaul.com/v1/transcript \
-H "Authorization: Bearer $SCRIPTHAUL_API_KEY" \
--data-urlencode "video_id=jNQXAC9IVRw" \
--data-urlencode "format=clean"
Inspect X-Cache, X-Credits-Charged, X-Credits-Balance, and X-Request-Id. A cache hit costs 0. A successful cold delivery costs 1. Failures cost 0.
3. Download SRT
curl --get https://api.scripthaul.com/v1/transcript \
-H "Authorization: Bearer $SCRIPTHAUL_API_KEY" \
--data-urlencode "video_id=jNQXAC9IVRw" \
--data-urlencode "format=srt" \
--data-urlencode "raw=1" \
-o jNQXAC9IVRw.srt
Cold calls may return 202 after 25 seconds. Follow the returned poll_url with the same Bearer key; polling itself does not add another delivery charge.
Thin clients
The MIT-licensed JavaScript and Python clients are published to npm and PyPI once the live verification run passes; until then use curl or copy the examples. Every response carries meta (cache, credits charged, balance, rate limit, request id, and library coverage), and wait() stops on a paused job instead of polling through it. Both clients expose transcripts, discovery, channel resolution and latest uploads, library coverage and search, monitors, and durable jobs. They bound retries for 429/502/503 responses and 202 polling, raise typed errors that mirror errorClass, and keep request_id on exceptions.
import ScriptHaul from "scripthaul";
const client = new ScriptHaul({ apiKey: process.env.SCRIPTHAUL_API_KEY });
const job = await client.jobs.create({ input: "https://www.youtube.com/@example" });
await job.wait();
from scripthaul import ScriptHaul
client = ScriptHaul()
job = client.jobs.create(input="https://www.youtube.com/@example").wait()
Choose a workflow
| Goal | Start here | Cost |
|---|---|---|
| Find videos by topic or inside one channel | YouTube search | 1 credit per delivered page; identical per-key retry within 60 seconds is free |
| Resolve a creator or inspect recent uploads | Channels and latest uploads | 0 credits |
| Inspect a full source or a pasted selection | Listing, pasted lists | 0 credits; cold enumeration has its own allowance |
| Read one video in a chosen format or language | Transcripts, video facts | 1 credit per successful cold transcript; cache hits and failures are free |
| Export a channel, playlist, or selection | Bulk jobs, webhooks | Jobs reserve the cold count; files, manifests, and archives cost 0 credits |
| Answer what a channel said, with timestamp citations | Transcript library | Coverage and scoped searches cost 0 credits; fill missing captions through ordinary jobs |
| Keep new captions arriving automatically | Monitors | Monitoring costs 0 credits; optional automatic jobs use normal transcript credits |
| Connect an assistant or automation | MCP client setup, agent skill, examples and n8n | Connection costs 0 credits; tools use their documented operation costs |
Use the interactive API reference to inspect every request and response schema and make same-origin requests with your Bearer key. The page also keeps a readable reference when JavaScript is disabled. OpenAPI JSON, llms.txt, and the full agent reference describe the same API.
For a library question, check coverage first, explain the cold count, fetch missing captions when authorized, then search passages and cite their timestamp URLs. Library search always requires one channel or one video scope. Use credits and limits to budget the work and typed errors to choose the right retry or next step.
Quickstart in four languages
All examples read the key from SCRIPTHAUL_API_KEY. Maintained runnable versions are in the examples directory.
curl --get https://api.scripthaul.com/v1/transcript \
-H "Authorization: Bearer $SCRIPTHAUL_API_KEY" \
--data-urlencode "video_id=jNQXAC9IVRw" --data-urlencode "format=clean"
import os, urllib.request
request = urllib.request.Request("https://api.scripthaul.com/v1/transcript?video_id=jNQXAC9IVRw&format=clean")
request.add_header("Authorization", f"Bearer {os.environ['SCRIPTHAUL_API_KEY']}")
print(urllib.request.urlopen(request).read().decode())
const response = await fetch("https://api.scripthaul.com/v1/transcript?video_id=jNQXAC9IVRw&format=clean", {
headers: { Authorization: `Bearer ${process.env.SCRIPTHAUL_API_KEY}` },
});
console.log(await response.json());
req, _ := http.NewRequest("GET", "https://api.scripthaul.com/v1/transcript?video_id=jNQXAC9IVRw&format=clean", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("SCRIPTHAUL_API_KEY"))
response, err := http.DefaultClient.Do(req)
if err != nil { log.Fatal(err) }
defer response.Body.Close()
io.Copy(os.Stdout, response.Body)