ScriptHaul API
Log inGet API key

Credits and limits

One credit means one successfully delivered transcript that was not already cached, or one delivered page of YouTube search results. With translate=true, on a transcript request or a bulk job, a transcript delivered in a language YouTube declined to translate costs 300 credits per started 10,000 characters of transcript (at most 1,500), on top of the delivery credit.

EventCredits
Cache hit0
Successful cold transcript, including a labelled fallback1
With translate=true, a transcript delivered in a language YouTube declined to translate (single transcripts, bulk jobs)300 per started 10,000 characters, at most 1,500, plus the delivery credit
Delivered page of YouTube search results, including an empty page1
Identical search page repeated with the same key within 60 seconds0
Fresh refetch of a cached video (fresh=true)1
No captions, unavailable video, strict refusal, 429, 5xx, or budget pause0
Channel resolution, latest uploads, listing, facts, account, usage, status, files, manifests, archives, or webhooks0
Library coverage, scoped transcript search, and monitor management or RSS checks0
Monitor automatic job: successful cold transcript delivery1

Free accounts receive 100 credits each UTC month. The allowance resets and does not accumulate. Purchased credits are a separate bucket, never expire, and are spent only after free credits.

Per-key limits

LimitFree keyPaid key
Requests/minute, approximate60300
Concurrent synchronous cold fetches210
Cold fetches/day10020,000
Cache reads/day2,000100,000
Cold enumerations/day2525
Search pages/day502,000
Admitted search attempts/day, including failed attempts1506,000
Channel resolutions/day, including warm results and searches scoped by a handle1001,000
Latest-upload calls/day, including warm results5005,000
Library coverage calls/day, including warm results2002,000
Library searches/day50020,000
Monitors/account, excluding cancelled125
Live keys25
Active jobs1 × 100 videos5 × 500 videos

Ordinary API keys each use these per-key limits. An account's OAuth connections instead share one tier allowance pool across all grants: request rate, cold-fetch and search concurrency, and every daily counter in the table, including usage from revoked grants. Reconnecting or adding another assistant does not create more allowance. Search's free 60-second replay still belongs to the same grant. Monitor admission continues to count aggregate account traffic, including ordinary keys and OAuth work; ordinary API-key requests retain their own caps. The account can keep ten connected OAuth grants, with the oldest revoked when a new completed connection exceeds that cap. See OAuth authentication and reconnect timing.

New accounts are limited to 25 cold fetches per UTC day for their first two days. Global guards begin at 50,000 API cold fetches/day, a separate 5,000/day free-tier pool, and 6,000 API Data API units/day.

Search pages and admitted attempts have separate daily counters from cold transcript fetches. An admitted attempt counts even when it fails, but a failure costs zero credits. Denied admission, an identical request still in progress, and a delivered page replayed with the same key within 60 seconds consume no new attempt or page allowance; a replay also costs zero credits. Attempt exhaustion is 429 QuotaExceeded with search_attempt_cap, retryable: false, and Retry-After until 00:00 UTC. An in-progress duplicate instead returns search_in_progress, retryable: true, and Retry-After: 1.

Channel resolution and latest uploads cost zero credits; RSS results are cached for ten minutes. Resolution, latest uploads, and library coverage each have their own daily allowance above. Valid admitted calls count even when the result is already cached. Limits are checked before upstream work, and an exhausted daily allowance returns 429 QuotaExceeded with Retry-After until 00:00 UTC. These routes also share the key's requests-per-minute limit.

Library search costs zero credits and has a separate daily search allowance. It requires one channel or video scope. Monitors check recent RSS entries without proxy or Data API spend; automatic jobs use the same transcript reservations, delivery charges, and pause rules as ordinary jobs. Monitor limits apply to the account, including calls from connected assistants.

The keyless MCP server allows 30 latest-upload calls per IP address per UTC day, including feed-cache hits. This allowance is separate from its 20 cold transcript fetches/day. Cloud-hosted clients can share an IP address. RSS misses use the same ten-minute cache; reaching the daily limit requires waiting until UTC midnight, not retrying immediately.

Every API response includes X-RateLimit-Limit where a key rate applies and X-Request-Id. Authenticated responses include X-Credits-Charged and X-Credits-Balance. A 429 includes Retry-After; exact rate remaining/reset headers are not fabricated.

curl -i --get https://api.scripthaul.com/v1/transcript \
  -H "Authorization: Bearer $SCRIPTHAUL_API_KEY" \
  --data-urlencode "video_id=jNQXAC9IVRw"

Automatic outage credit

After each UTC day, ScriptHaul checks its own ten-minute serving-strategy observations. A day qualifies only after at least four recorded hours with no healthy strategy; missed observations do not manufacture outage time. Each account then receives floor(that day's successful delivery debits × 10%) as purchased, non-expiring credits. Failures already cost zero and are not part of the debit total. The ledger reference is unique per account and UTC day, so retries cannot credit twice. Frozen accounts receive the credit but remain frozen; closed accounts are recorded as skipped.

Inspect usage and balance in four languages

GET /v1/usage is the zero-credit way to inspect daily counters over a UTC date range. Its JSON body contains scope, range, and rows; X-Credits-Balance carries the current total. Use GET /v1/account when you need the separate free, purchased, and reserved buckets. Maintained runnable variants are in the examples directory.

curl https://api.scripthaul.com/v1/usage \
  -H "Authorization: Bearer $SCRIPTHAUL_API_KEY"
import os, urllib.request
request = urllib.request.Request("https://api.scripthaul.com/v1/usage")
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/usage", {
  headers: { Authorization: `Bearer ${process.env.SCRIPTHAUL_API_KEY}` },
});
console.log(await response.json());
req, _ := http.NewRequest("GET", "https://api.scripthaul.com/v1/usage", 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)

Reading the ledger

GET /v1/usage?resource=ledger lists the account's credit ledger newest first, up to 200 rows a page (limit, default 100). Each page carries pagination.next_cursor; send it back as cursor to fetch the next page. Cursors walk the whole history with no ceiling and are unaffected by rows that arrive between pages. since and until (ISO 8601) restrict a page to a date range, and format=csv returns the same page as a download. offset paging still works but stops at 100,000 rows.