The models are open source and you can run them yourself. If you'd rather not own GPUs, this is the hosted path: submit a file or a URL, poll the job, pull transcript JSON with speaker labels and word timings, hand the segments to your agent.
async batch · faster-whisper + WhisperX · diarization · txt/json/srt/vtt/docx
Whisper, faster-whisper, WhisperX and whisper.cpp are open source. Nothing here is locked behind a proprietary model — we run the same open weights on our own GPUs. What you're buying is the operations: the queue, the retries, the diarization pass, the storage, and the pager at 3am.
audio can't leave your network; you already run GPUs and someone owns them; you need to patch the decoding path or fine-tune; or volume is steady enough that a reserved GPU beats per-minute credit.
volume is bursty, you want a transcript today rather than a CUDA afternoon, or transcription is one node in a bigger pipeline and the week is better spent on the agent than the GPU box.
This is asynchronous batch. No streaming socket, no on-device mode, no bot that joins a live call. If you need partial hypotheses while someone is still talking, use a streaming stack instead.
We keep a neutral comparison of the open-source Whisper family — what each implementation is actually best at, with no fabricated benchmarks.
Four calls. Every path, field and header below is in the API reference — nothing on this page is aspirational.
POST /api/v1/transcribe/url takes JSON; POST /api/v1/transcribe takes multipart/form-data with a file field. Both accept language, diarize, word_timestamps and source. Send an Idempotency-Key so a retried submit returns the original job instead of a duplicate.
curl https://whipscribe.com/api/v1/transcribe/url \
-H "X-API-Key: $WHIPSCRIBE_KEY" \
-H "Idempotency-Key: ep-412-v1" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/ep-412.mp3","diarize":true,"word_timestamps":true,"source":"api"}'
# 202 Accepted
{ "job_id": "35f4be54-…", "status": "queued", "tier": 2 }
GET /api/v1/jobs/{job_id} every ~3 seconds while status is queued or processing. It also returns progress, audio_duration_seconds, language, source, speech_detected, speech_ratio and error.
# 200 OK
{ "job_id": "35f4be54-…", "status": "processing", "progress": 0.42,
"audio_duration_seconds": 967, "speech_detected": true,
"speech_ratio": 0.78, "error": null }
Branch on speech_detected === false: a VAD pre-flight catches music and near-silence and finishes the job with an empty transcript rather than letting Whisper hallucinate over it.
GET /api/v1/jobs/{job_id}/result?format=json is the pipeline format — text, language, and a segments array of start / end / speaker / text, each with a words array. format also takes txt, srt, vtt and docx.
{ "text": "Welcome back to the show…", "language": "en",
"segments": [
{ "start": 0.0, "end": 16.3, "speaker": "SPEAKER_00",
"text": "Welcome back to the show…",
"words": [ { "start": 0.0, "end": 0.4, "text": "Welcome" } ] } ] }
Group segments by speaker, or window them by start, and pass the windows as tool results. Every segment carries a timestamp, so whatever the agent asserts traces back to a moment in the audio — and GET /api/v1/jobs/{job_id}/audio/url returns a short-lived playback url (with storage and expires_in) so a human can hear it.
import httpx, time
h = {"X-API-Key": KEY}
job = httpx.post("https://whipscribe.com/api/v1/transcribe/url",
headers=h, json={"url": SRC, "diarize": True}).json()
while True:
s = httpx.get(f"https://whipscribe.com/api/v1/jobs/{job['job_id']}", headers=h).json()
if s["status"] in ("done", "failed"): break
time.sleep(3)
r = httpx.get(f"https://whipscribe.com/api/v1/jobs/{job['job_id']}/result",
params={"format": "json"}, headers=h).json()
turns = [{"t": seg["start"], "who": seg.get("speaker"), "say": seg["text"]}
for seg in r["segments"]] # → your agent's context
402 NO_CREDITS carries a credits snapshot and an upgrade_url. 429 RATE_LIMITED wants backoff. 502 BACKEND_ERROR and 502 BACKEND_UNREACHABLE are safe to retry. 410 AUDIO_EXPIRED means the audio retention window elapsed — the transcript is still readable, only playback is gone.
Every error is {"error": "…", "code": "…"}. Branch on code, never on the sentence. Only /api/v1/* is public and versioned; anything outside that prefix is internal and will move under you.
The MCP server at https://whipscribe.com/mcp exposes the same capability as tools, so an MCP client — Claude, Claude Code, Cursor, or your own — submits and reads transcripts with no polling loop of yours. Setup is one connector →
| Tool | What it does |
|---|---|
transcribe_url | Transcribe a direct media link, podcast episode, or Creative-Commons YouTube URL. Polls until done, then returns a preview plus a link to the full transcript. |
transcribe_urls_batch | Up to 20 URLs concurrently; a per-URL failure doesn't abort the batch. This is the playlist / backlog path. |
get_job_status | Poll one job by job_id. |
get_transcript | Fetch a finished transcript as txt, json, srt, vtt or docx. Large transcripts are linked, not inlined. |
list_my_transcripts | The account's recent transcripts, including ones uploaded through the web app rather than by the agent. |
clips_search_transcript | Search one transcript's sentences; returns matches with start/end timestamps and speaker. |
request_upload_url | Mint a one-time presigned PUT URL, upload the bytes from the user's machine, then pass the returned public_url to transcribe_url. Audio never streams through the MCP transport. |
One honest gap: transcribe_file exists in the toolset but is not supported over the remote transport — a local path doesn't exist on the server. Use request_upload_url then transcribe_url, or upload in the app.
The Chrome extension records the current tab's audio or your microphone and submits it through the same API — useful when the source is a browser player.
Pass X-User-Email alongside your key for server-to-server flows, or Authorization: Bearer with a Firebase ID token from a signed-in browser, and the transcript lands in that account's files.
A submit with no user identity returns a claim_token. Hold it client-side, then POST /api/v1/jobs/claim once the user signs in to attach every pending job.
GET /api/v1/me returns email, retention_days and signed_in — read it rather than baking a window into your client.
Credit is metered in audio-hours: a 30-minute file spends 0.5h, and failed jobs don't consume credit. Credits don't expire.
| Plan | Price | Minutes | Per audio-hour |
|---|---|---|---|
| Starter | $4 | 300 | $0.80 |
| Pro | $12 | 2,000 | $0.36 |
| Team | $24 | 5,000 | $0.29 |
Packs are one-time and the credits do not expire — there is no monthly commitment and no subscription to cancel. Worked example: a nightly job over 40 episodes averaging 50 minutes is 40 × 50 = 2,000 minutes — exactly one $12 Pro pack, which is 33 hours of audio at $0.36 an hour. Team's 5,000 minutes is 83 hours. Full pricing →
Keys are self-serve: sign in, add credit, and create one at /apis/keys. The API has no free tier — a key needs a positive balance ($50 minimum, spendable on transcription). Then the whole pipeline above is four calls.
Create an API key →No. It's asynchronous batch: submit, poll, fetch. No streaming socket, no on-device mode, no bot that joins a live call. If you need partial hypotheses mid-sentence, this is the wrong tool — better to know that here than after integration.
When audio can't leave your network, when you already run GPUs, when you need to modify the decoding path or fine-tune, or when steady volume makes a reserved GPU cheaper than per-minute credit. The engines are open source — start here if you're picking one.
Up to 10 hours per file on the multipart endpoint, across mp3, m4a, wav, mp4, mov, ogg, webm and flac. Language is auto-detected when you omit language.
Send Idempotency-Key on submits. The same key from the same API key returns the original job with X-Idempotent-Replay: true instead of creating a duplicate. Keys are ≤255 chars, no whitespace.
On our own GPUs, running open-source Whisper — your audio isn't handed to a third-party transcription vendor, and it is never used to train a model. Audio retention depends on plan; the transcript stays until you delete it. Details on /security.