ScriptHaul API
Log inGet API key

Transcripts and languages

GET /v1/transcript is cache-first. Supply video_id, format, optional language, optional fallback, and optional raw=1.

curl --get https://api.scripthaul.com/v1/transcript \
  -H "Authorization: Bearer $SCRIPTHAUL_API_KEY" \
  --data-urlencode "video_id=jNQXAC9IVRw" \
  --data-urlencode "language=es" \
  --data-urlencode "fallback=true" \
  --data-urlencode "format=json"

Language contract

Situationrequesteddeliveredfallbacktranslation
Native requested trackenenfalsenot_requested
Translation servedesesfalseserved
Translation refused, fallback allowedessource languagetruerefused
Translation refused, strict modeesnonenoneretryable translation_refused error, 0 credits
No translation exists, fallback allowedessource languagetrueunavailable
No translation exists, strict modeesnonenonelanguage_unavailable error with available_languages, 0 credits

kind is manual or asr. Set fallback=false for strict mode. A labelled fallback delivery costs 1 when cold because a transcript was delivered; a strict refusal costs 0. refused is the throttle case and can succeed on a later attempt; unavailable is a fact about the video. Bulk job rows report null before delivery and never unavailable.

{
  "language": {
    "requested": "es",
    "delivered": "en",
    "kind": "manual",
    "fallback": true,
    "translation": "refused"
  }
}

Seven formats

Samples use a synthetic timing fixture based on the public-domain U.S. Constitution, never a customer transcript or cached YouTube transcript.

formatresponse field or raw typesample
jsoncues / JSON[{"start":0,"duration":2.4,"text":"We the People…"}]
cleantext / plain textWe the People of the United States…
timestampedtext / plain text[00:00] We the People…
bothtext / plain textclean text plus timestamped section
srttext / SubRip1 then 00:00:00,000 --> 00:00:02,400
vtttext / WebVTTWEBVTT then 00:00.000 --> 00:02.400
mdtext / Markdownheading, metadata, and timestamped cues

Use raw=1 to receive the rendered file with an attachment filename instead of the JSON wrapper.

Polling long cold calls

A cold request waits for up to 25 seconds. If work continues, it returns 202 with a request_id and relative poll_url. Repeat that URL with the same Bearer key. Settlement references make retries and polling idempotent.

Language request in four languages

Each sample requests Spanish, allows a labelled fallback, and asks for JSON. Maintained runnable versions use only standard libraries and live 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 "language=es" \
  --data-urlencode "fallback=true" --data-urlencode "format=json"
import os, urllib.request
url = "https://api.scripthaul.com/v1/transcript?video_id=jNQXAC9IVRw&language=es&fallback=true&format=json"
request = urllib.request.Request(url, headers={"Authorization": f"Bearer {os.environ['SCRIPTHAUL_API_KEY']}"})
print(urllib.request.urlopen(request).read().decode())
const url = new URL("https://api.scripthaul.com/v1/transcript");
url.search = new URLSearchParams({ video_id: "jNQXAC9IVRw", language: "es", fallback: "true", format: "json" });
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&language=es&fallback=true&format=json"
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)