Transcribing audio
Use the ot command. It uploads the file, waits for the job, writes the
transcript and subtitles to disk, and prints either the text or a short receipt.
Before the first run
Check the command exists:
ot --version
If that fails, install it:
npm install -g @opentranscription/cli
If npm is also missing, stop and tell the user Node.js 22+ is required.
Guessing at a package manager wastes a turn and usually installs nothing.
Then check for a signed-in account:
ot whoami
If it says "Not signed in", run ot login. It prints a code and a URL and waits
for the user to approve in a browser. This needs a human. Show them the code
and the URL from the output and wait. Do not try to complete it yourself.
Transcribing
ot transcribe path/to/audio.mp3
Useful flags:
| Flag | When |
|---|---|
--diarize |
more than one speaker, or the user asks who said what |
--no-word-timestamps |
the user only wants prose, and no word timing stored |
--model auto/best |
accuracy matters more than cost |
--model auto/cheapest |
long file, rough transcript is fine |
--model auto/fastest |
the user is waiting on the result |
--language es |
you know the language; skips detection |
--vocab <words> |
the audio contains names or jargon (see below) |
--out <dir> |
write artifacts somewhere other than beside the audio |
Run ot models to see what is available with prices and measured accuracy. A
row marked deprecated → <successor> still works when named, but auto/* will
not choose it; prefer the successor for new work.
Custom vocabulary
Speech models get the sentence right and the one word that mattered wrong. Proper nouns, product names, drug names, ticker symbols, and internal jargon are the words a general model has the weakest prior for, and they are usually the reason someone wanted the transcript.
Pass them ahead of time:
ot transcribe standup.mp3 --vocab "Kubernetes,Grafana,Sanjay Bhattacharya"
You usually already know these terms. They are in the file name, the surrounding code, the ticket, or what the user just told you. Supply them on the first run: a second run to fix a misspelling costs money and takes as long as the first.
Keep the list to terms a model would plausibly miss. Padding it with ordinary English words does not help and can bias the model toward them.
For a list the user maintains in the web app, pass its id instead:
--vocab-list <id>. Both can be given; they are merged.
Not every model supports this. A model that does not will ignore the words instead of failing, so passing them is always safe.
Reading the output
Artifacts are always written next to the audio (or to --out):
<name>.transcript.md: the text, with speaker labels when diarized<name>.json: the full job, including per-word timing<name>.srt/<name>.vtt: subtitles, when the model returned segments
Inside the JSON, transcript.word_timestamps says whether word timing is there:
available (transcript.words is an array, and so is each
transcript.segments[].words), unavailable (the model returned none), or
disabled (--no-word-timestamps was passed). transcript.words is null for
both of the last two, so check the status before iterating it. Each word is
{ text, start, end, confidence }, plus speaker when diarized. The key is
text, not word.
Short audio: the transcript is printed directly. Use it. The cutoff is about 2,000 tokens of transcript, so most recordings under ten minutes come back this way.
Long audio: a receipt is printed instead, carrying word count, duration, model, the artifact paths, and a section index of timestamps. This is deliberate. Read the sections to find what matters, then open just that part of the transcript file instead of loading the whole thing.
A Warning: first line: the platform's quality gate flagged the result as
empty, sparse, or in a language the model does not support. The line names the
reason and whether the job was charged (empty and sparse are not). Report it to
the user instead of treating the text as a good transcript. Both ot transcribe
and ot show print it.
When something goes wrong
The command exits non-zero and prints one sentence saying what to do. Common cases:
- not signed in →
ot login - no credential for that workspace →
ot login --org <id>; never retry with a different workspace, the command refuses on purpose - out of credits or free minutes → the user has to add credits on the web app
- file too large → the API caps uploads at 100 MiB; split the file or re-encode it smaller
- video file → the API takes audio only; extract first with
ffmpeg -i in.mp4 -vn -ac 1 -ar 16000 -c:a libmp3lame out.mp3 No such file: <path>→ the path is wrong; relative paths resolve from the current directory<path> is a folder. Pass an audio file.→ the command takes one file; pass the recording inside the folderOption '--out <value>' argument missingorUnknown option '--langauge'→ a flag was given without its value, or misspelled; nothing ran, fix the flag and re-run
More detail in references/troubleshooting.md.
Do not
- Do not print a full transcript back to the user unless they asked for it. The file path is usually the useful answer.
- Do not transcribe the same file twice to "check" a result; it costs money and returns the same thing.
- Do not pass
--modela name you have not seen inot models. - Do not treat a transcript as good when
ot transcribeorot showopened with aWarning:line. The platform flagged the result (empty, sparse, or the wrong language); tell the user what it said. - Do not re-run with
--vocabafter seeing a misspelling in the output when you could have supplied the term the first time. Read the context for names before the first run, not after.