Migrating from transcriptapi.com
ScriptHaul uses Bearer authentication, explicit language results, free cache hits, and pay-as-you-go credit blocks.
| Existing concept | ScriptHaul |
|---|---|
| API key query parameter | Authorization: Bearer sh_live_… |
| YouTube URL parameter | Extract its 11-character YouTube ID and send it as video_id |
| Plain transcript | format=clean |
| Timestamped transcript | format=timestamped |
| JSON transcript | format=json |
| Translation language | language=xx; inspect requested/delivered/translation |
| Silent language fallback | fallback=true, always labelled |
| Strict language | fallback=false; refusal is retryable and costs 0 |
| One credit for every call | 1 for a successful cold transcript delivery or a delivered search page; transcript cache hits and failures cost 0 |
| Subscription/top-up balance | One-time blocks; purchased credits never expire |
| Client-side channel loop | One durable bulk job with manifests, per-file downloads, events, and a signed webhook |
Discovery endpoint map
| transcriptapi.com endpoint | ScriptHaul endpoint | Credits |
|---|---|---|
/youtube/search | GET /v1/search?q=…&type=video | 1 per delivered page |
/youtube/channel/search | GET /v1/search?q=…&channel=@handle | 1 per delivered page |
/youtube/channel/videos | GET /v1/videos?url=https://www.youtube.com/@handle | 0; cold enumeration limits apply |
/youtube/channel/latest | GET /v1/channels/latest?channel=UC… | 0 |
/youtube/channel/resolve | GET /v1/channels/resolve?input=@handle | 0 |
/youtube/playlist/videos | GET /v1/videos?url=https://www.youtube.com/playlist?list=PL… | 0; cold enumeration limits apply |
Encode nested URLs as query values. Search uses opaque continuation tokens; channel and playlist listing uses offsets in multiples of 500. Repeating an identical search page with the same key within 60 seconds costs zero. Latest uploads return the RSS feed's newest 15 entries with a ten-minute cache; use full listing for older videos.
curl --get https://api.scripthaul.com/v1/transcript \
-H "Authorization: Bearer $SCRIPTHAUL_API_KEY" \
--data-urlencode "video_id=jNQXAC9IVRw" \
--data-urlencode "format=json" \
--data-urlencode "language=en" \
--data-urlencode "fallback=false"
Do not mechanically copy a key from a query string into a new URL. Move it into the header first, then verify your retry policy against typed errorClass and retryable fields.
Equivalent request in four languages
Each version uses an Authorization header, strict English, and JSON output. Maintained runnable variants 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=json" \
--data-urlencode "language=en" --data-urlencode "fallback=false"
import os, urllib.request
url = "https://api.scripthaul.com/v1/transcript?video_id=jNQXAC9IVRw&format=json&language=en&fallback=false"
request = urllib.request.Request(url, headers={"Authorization": f"Bearer {os.environ['SCRIPTHAUL_API_KEY']}"})
print(urllib.request.urlopen(request).read().decode())
const url = "https://api.scripthaul.com/v1/transcript?video_id=jNQXAC9IVRw&format=json&language=en&fallback=false";
const response = await fetch(url, { headers: { Authorization: `Bearer ${process.env.SCRIPTHAUL_API_KEY}` } });
console.log(await response.json());
url := "https://api.scripthaul.com/v1/transcript?video_id=jNQXAC9IVRw&format=json&language=en&fallback=false"
req, _ := http.NewRequest("GET", url, 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)