# Transcribing Audio

> Transcribes audio files to text with speaker labels, timestamps, and SRT/VTT subtitles using OpenTranscription's catalog of speech-to-text models. Use when the user has an audio file (mp3, wav, m4a, flac, ogg, webm), voice memo, recorded meeting, interview, lecture, or podcast to transcribe, or asks to caption, subtitle, or diarize audio.

- Skill: `opentranscription/transcribing-audio` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add opentranscription/transcribing-audio`
- Raw SKILL.md: https://api.skillmd.com/api/skills/opentranscription/transcribing-audio/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: OpenTranscription (https://skillmd.com/u/opentranscription)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/opentranscription/transcribing-audio

---


# 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 folder
- **`Option '--out <value>' argument missing` or `Unknown 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](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 `--model` a name you have not seen in `ot models`.
- Do not treat a transcript as good when `ot transcribe` or `ot show` opened
  with a `Warning:` line. The platform flagged the result (empty, sparse, or the
  wrong language); tell the user what it said.
- Do not re-run with `--vocab` after 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.

