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
| Situation | requested | delivered | fallback | translation |
|---|---|---|---|---|
| Native requested track | en | en | false | not_requested |
| Translation served | es | es | false | served |
| Translation refused, fallback allowed | es | source language | true | refused |
| Translation refused, strict mode | es | none | none | retryable translation_refused error, 0 credits |
| No translation exists, fallback allowed | es | source language | true | unavailable |
| No translation exists, strict mode | es | none | none | language_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.
| format | response field or raw type | sample |
|---|---|---|
json | cues / JSON | [{"start":0,"duration":2.4,"text":"We the People…"}] |
clean | text / plain text | We the People of the United States… |
timestamped | text / plain text | [00:00] We the People… |
both | text / plain text | clean text plus timestamped section |
srt | text / SubRip | 1 then 00:00:00,000 --> 00:00:02,400 |
vtt | text / WebVTT | WEBVTT then 00:00.000 --> 00:02.400 |
md | text / Markdown | heading, 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)