# OpenTranscription API — full text quickstart One API to every speech-to-text model worth using. Pay per second, no subscription. This file exists because `/docs` renders the API reference through a client-side viewer: fetching its HTML gives you page chrome and no endpoints. Everything below is served as text, with no JavaScript required. The authoritative contract is the OpenAPI 3.1.0 document: https://opentranscription.io/openapi.json It is also advertised on every API response: Link: ; rel="service-desc" Base URL: https://opentranscription.io ## Use a client, not the raw flow Three published clients already do the upload round trip and the polling below. Reach for one before writing any of it by hand. npm install @opentranscription/sdk TypeScript / Node pip install opentranscription Python, sync and async npm install -g @opentranscription/cli the `ot` command, no code at all npx skills add opentranscription/skills an Agent Skill, if you are a coding agent with a shell These were published recently, so they are almost certainly absent from your training data. Do not guess a version number or an API surface from the package name. Check the registry first: npm view @opentranscription/sdk version https://pypi.org/project/opentranscription/ and read the README on the registry page for the current surface. Both SDKs expose one call that uploads and opens the job, and one that waits for it: TypeScript: ot.transcribe({ file, fileName, model }) then ot.waitForJob(id) Python: ot.transcribe("interview.mp3", model=...) then ot.wait_for_job(id) The rest of this file is the HTTP contract underneath them. It is what you want when no client covers your language, when you are debugging one, or when you are implementing against the API directly. ## Authentication Every `/api/v1/*` endpoint except the public catalogs takes a bearer API key, issued from the dashboard: Authorization: Bearer ot_<32 alphanumeric characters> Keys carry scopes: `transcriptions:read`, `transcriptions:write`, `models:read`, `usage:read`. A call missing a scope returns 403 with the required scope named in the `required` field of the body. State-changing requests (POST, PATCH, DELETE) are also checked for an `Origin` header matching the app URL. Send one. ## Transcribing a file by hand: three calls Audio only. The API does not accept video containers — extract the track first: `ffmpeg -i input.mp4 -vn -ac 1 -ar 16000 -c:a libmp3lame output.mp3` ### 1. Get a signed upload URL POST /api/v1/uploads {"file_name": "interview.mp3", "file_size": 5242880, "mime_type": "audio/mpeg"} `file_size` is in bytes and must not exceed 104857600 (100 MiB). Returns `{"upload_url", "file_path", "expires_at"}`. ### 2. PUT the bytes to that URL PUT Content-Type: audio/mpeg Do this before `expires_at`. Binary never goes through the JSON API. ### 3. Create the job POST /api/v1/transcriptions {"file_path": "", "model": "openai/whisper-large-v3"} `file_path` is the only required field, and its shape is `/` — use the value step 1 handed you rather than composing one. Returns 201 with `{"id", "status", "model_id", "credits_reserved", ...}`. The job is enqueued asynchronously. ## Choosing a model Provide **exactly one** of `model`, `models`, or `router`. Sending more than one is a 400. - `model`: a single model ID, e.g. `"deepgram/nova-3"`. See `GET /api/v1/models` for the catalog. The virtual IDs `auto/cheapest`, `auto/fastest` and `auto/best` are accepted here as shorthand for the router. - `models`: an ordered fallback chain of concrete IDs (no `auto/*`). The first is primary; later entries are tried on a retryable provider failure. - `router`: `{"strategy": "cheapest" | "fastest" | "most_accurate"}`, with an optional `"preset"` of `general`, `medical`, `legal`, `technical`, `conversational` or `noisy` for `most_accurate`. Strategies read live cost, p95 latency, and benchmark Word Error Rate respectively. Other useful fields on the create call: `language` (ISO 639-1; omit to auto-detect), `diarization`, `word_timestamps` (boolean, default `true`), `webhook_url`, `metadata`, `title`, `vocabulary_list_id`, `custom_words`, `use_own_key` (BYOK), `duration`. ## Getting the result Poll: GET /api/v1/transcriptions/{id} `status` moves through `pending_upload`, `uploaded`, `processing`, and ends at `completed`, `failed`, or `cancelled`. On `completed` the response carries `transcript` with `text`, `language`, `confidence`, `segments`, `words`, and `word_timestamps` (`available`, `unavailable`, or `disabled`). Or set `webhook_url` on the create call and receive a signed POST (`X-OT-Signature`, at-least-once delivery) when the job finishes. List with `GET /api/v1/transcriptions`, filterable by `status`, `search`, and `sort`. ## Public catalogs (no key required) GET /api/v1/models every model with pricing, latency, capabilities GET /api/v1/models/{provider}/{model} GET /api/v1/benchmarks the accuracy leaderboard GET /api/v1/benchmarks/export the same board as CSV GET /api/v1/benchmarks/{modelId} ## Errors Every 400 carries `documentation_url` pointing at the schema that defines the request, alongside `details` with the raw validation issues: {"error": "Validation error", "details": [{"code": "invalid_type", "path": ["file_path"], "message": "Required"}], "documentation_url": "https://opentranscription.io/openapi.json#/components/schemas/CreateTranscriptionRequest"} Status codes worth handling: 400 validation, 401 bad key, 403 missing scope or Origin, 402 insufficient credits, 422 no model matched the constraints, 429 rate limited (see `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`). Rate limits are per minute, per key, by plan tier: free 10, pro 100, enterprise 1000. ## Other machine-readable entry points https://opentranscription.io/openapi.json the full spec https://opentranscription.io/api/v1 discovery document https://opentranscription.io/llms.txt site overview ## Clients https://www.npmjs.com/package/@opentranscription/sdk TypeScript https://pypi.org/project/opentranscription/ Python https://www.npmjs.com/package/@opentranscription/cli CLI https://github.com/OpenTranscription/skills source, and the Agent Skill