API
API quickstart
Create a job, wait for it, and read the Context, in curl, TypeScript and Python. Then stream progress and upload a file.
On this page
You need an API key. Keys are made in the dashboard, after you sign in. See API authentication. To check that the API is up first, run curl https://scribiz.com/api/v1/status. It needs no key.
export SCRIBIZ_API_KEY=sbz_live_your_key_hereThe examples use a 19 second public YouTube video, so they cost almost nothing.
Create a job and read the Context
JOB=$(curl -s https://scribiz.com/api/v1/context \ -H "Authorization: Bearer $SCRIBIZ_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"url": "https://www.youtube.com/watch?v=jNQXAC9IVRw", "mode": "auto"}' | jq -r .job_id)while true; do STATE=$(curl -s "https://scribiz.com/api/v1/jobs/$JOB" -H "Authorization: Bearer $SCRIBIZ_API_KEY") STATUS=$(printf '%s' "$STATE" | jq -r .status) case "$STATUS" in succeeded) break ;; failed|canceled) printf '%s' "$STATE" | jq .error; exit 1 ;; esac sleep 3doneprintf '%s' "$STATE" | jq '.result.synthesis.summary.tldr'const API = 'https://scribiz.com/api'const headers = { Authorization: `Bearer ${process.env.SCRIBIZ_API_KEY}`, 'Content-Type': 'application/json',}async function getContext(url: string) { const create = await fetch(`${API}/v1/context`, { body: JSON.stringify({ mode: 'auto', url }), headers: { ...headers, 'Idempotency-Key': crypto.randomUUID() }, method: 'POST', }) if (!create.ok) { throw new Error(`${create.status}: ${await create.text()}`) } const { job_id } = await create.json() while (true) { const res = await fetch(`${API}/v1/jobs/${job_id}`, { headers }) const job = await res.json() if (job.status === 'succeeded') { return job.result } if (job.status === 'failed' || job.status === 'canceled') { throw new Error(job.error?.message ?? job.status) } await new Promise(resolve => setTimeout(resolve, 3000)) }}const context = await getContext('https://www.youtube.com/watch?v=jNQXAC9IVRw')console.log(context.synthesis?.summary.tldr)import osimport timeimport uuidimport requestsAPI = "https://scribiz.com/api"HEADERS = {"Authorization": f"Bearer {os.environ['SCRIBIZ_API_KEY']}"}def get_context(url: str) -> dict: create = requests.post( f"{API}/v1/context", headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())}, json={"url": url, "mode": "auto"}, timeout=30, ) create.raise_for_status() job_id = create.json()["job_id"] while True: job = requests.get(f"{API}/v1/jobs/{job_id}", headers=HEADERS, timeout=30).json() if job["status"] == "succeeded": return job["result"] if job["status"] in ("failed", "canceled"): raise RuntimeError(job.get("error") or job["status"]) time.sleep(3)context = get_context("https://www.youtube.com/watch?v=jNQXAC9IVRw")print(context["synthesis"]["summary"]["tldr"])The speaker stands in front of elephants and points out that they have very long trunks.What you get back is the Context. Its fields are in The Context object.
Three things the examples do on purpose:
- They send an
Idempotency-Key, so a retry after a network error does not start a second job or charge twice. - They poll every three seconds. A job that is still
queuedorrunningis not an error. - They stop on
failedandcanceled, and showerror, which has acodeand ahint. See Errors and limits.
result is null while the job runs, and synthesis can be null if the summary step did not run, so check before you read into it. The curl example uses printf '%s' rather than echo on purpose: echo in zsh turns the \n inside the JSON into real line breaks, and jq then refuses the text.
Watch progress
The job's event stream sends one event as the work happens. It is optional. GET /v1/jobs/:id is always enough.
curl -N "https://scribiz.com/api/v1/jobs/$JOB/events" \ -H "Authorization: Bearer $SCRIBIZ_API_KEY"retry: 3000id: 1event: queueddata: {"runId":"job_2DUafpGltJTqVKlAYlIREm","t":0,"type":"queued"}id: 2event: starteddata: {"attempt":1,"runId":"job_2DUafpGltJTqVKlAYlIREm","t":0,"type":"started"}id: 3event: startdata: {"input":"https://www.youtube.com/watch?v=jNQXAC9IVRw","mode":"auto","type":"start","runId":"d6c4e6fe","t":0}id: 11event: stagedata: {"detail":"manual en captions, 6 segments","ms":5472,"stage":"download","status":"done","type":"stage","runId":"d6c4e6fe","t":5476}...id: 17event: donedata: {"summary":{"durationSeconds":19,"ms":8197,"usd":0.0017175,"words":39},"type":"done","runId":"d6c4e6fe","t":8197}Each data: line is a JSON event, the same ones the CLI prints with --json, plus queued, started and requeued from the API. See The JSON protocol. The id: lets you resume: reconnect with a Last-Event-ID header, or ?last_event_id=, and the stream continues after that event. A line that starts with : is a keep-alive. The stream ends after the job is done or has failed.
Upload a file
Upload the file first, then create the job from the upload. The API takes audio and video files, up to 100 MB. A video upload sends the whole file to Scribiz. To send only the sound, extract the audio first:
ffmpeg -i talk.mp4 -vn -ac 1 -ar 16000 -b:a 48k talk.mp3UPLOAD=$(curl -s -X PUT https://scribiz.com/api/v1/uploads \ -H "Authorization: Bearer $SCRIBIZ_API_KEY" \ -H "Content-Type: audio/mpeg" \ -H "X-File-Name: talk.mp3" \ --data-binary @talk.mp3 | jq -r .upload_id)curl https://scribiz.com/api/v1/context \ -H "Authorization: Bearer $SCRIBIZ_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"upload_id\": \"$UPLOAD\", \"mode\": \"audio\"}"{ "bytes": 73264, "duration_seconds": 12.120726, "expires_at": 1791097083297, "file_name": "speech.mp3", "has_audio": true, "has_video": false, "mime": "audio/mpeg", "upload_id": "upl_4dEjVvJnlzplZvFcA2PULr"}An upload is kept for one hour, belongs to one job, and is removed when that job succeeds. A file with no audio track, such as a screen recording, works with "mode": "visual". For anything over 100 MB, cut the audio first with ffmpeg as above. Or use the CLI, which cuts the audio on your machine and reads it with your own Gemini key or your Scribiz account.
Next
- Every route: Endpoints.
- Handling failures and limits: Errors and limits.
Checked against the Scribiz build on 2026-10-05.