Skip to main content
Mira STT is our own speech recognition, the one our voice agents listen with. It transcribes Hindi, English, Gujarati and seven more Indian languages, including the Hinglish most calls are in. The HTTP endpoint speaks OpenAI’s POST /v1/audio/transcriptions protocol, so an OpenAI SDK can call it once you change the base URL and the model name. For live audio, the same key opens a WebSocket that sends text back while the person is still talking.
Which one do I use? One clip of up to two minutes, text back in one answer: the HTTP endpoint in step 2. Live audio, such as a call in progress: the socket in step 3. A recording of up to two hours, with speakers and timestamps: offline transcription.

1. Get a key

Open the console, go to Developers and press Create key. You do not need an access code. The first time, the console asks for your work email and sends you a confirmation link; open it, then press Create key again. The key is a sk_live_ secret, and the console shows it only once. We store only its hash, so we cannot recover a lost key; you rotate it instead. The same key works for speech to text, text to speech and the rest of the v2 API, and draws on the same wallet.

2. One request

Send the audio as a multipart form:
200 OK
The answer arrives once the whole clip is transcribed. HTTP sends no partial transcripts; for text while someone is still speaking, use the socket. We cut a long clip into turns ourselves, so a 120-second file still comes back as one text. We wait up to 30 seconds plus half the clip’s length for the transcript (90 seconds for a 120-second clip), so give your client a timeout of 120 seconds. OpenAI’s prompt, temperature and timestamp_granularities are accepted and ignored, so an OpenAI client that sends them works unchanged.
8 kHz μ-law straight off a phone line

Response formats

json, the default, is the text alone. text is the same text as a plain body (text/plain; charset=utf-8), with no JSON around it. verbose_json adds the language as its full code, such as gu-IN even when you sent gu, the audio’s length in seconds, and one segment for each utterance we found, with where it starts and ends in the clip. srt and vtt are not offered.
verbose_json

What comes back

Every 200 carries its own accounting in headers: Every error after authentication also carries X-Request-Id. Quote it when you ask us about a failed request.

3. Stream it

The socket takes audio as it is captured and sends back partial transcripts as the words are recognised, then one final transcript for each utterance:
Send your key in the Authorization: Bearer header, as on every other request. This script streams a WAV file as if it were live: it opens an utterance with speech_start, sends the audio in 20 ms audio_input messages, closes the utterance with speech_end, and ends the session with end.
stream.py
Output
A partial can rewrite words it sent earlier, so replace what you show with each new partial instead of appending to it. To make a test file, ffmpeg -i call.mp3 -ac 1 -ar 16000 -sample_fmt s16 hello.wav. With websockets older than 14, pass extra_headers= instead of additional_headers=. The script sends the file as one utterance. With manual endpointing, one utterance can run for at most 60 seconds: past that you get a fatal utterance_too_long, the socket closes with 1011, and that utterance gets no final. For longer audio, send speech_end at a pause at least every 25 seconds, or connect with endpointing=vad and we cut the utterances for you. The WebSocket reference has every message and event, both endpointing modes, phone audio and the close codes. A browser connects with a short-lived stream token instead of your key.

4. Pipecat

If you build on Pipecat, install our integration with uv add "pipecat-mirai>=0.6.0" and put MiraiSTTService where your STT service was:
It opens one socket per call, uses your pipeline’s VAD to mark the start and end of each utterance, sends 8 kHz phone audio as it is, and reconnects without losing what the caller said. See Pipecat.

For phone calls

  • Send the line’s audio as it is. Twilio and Plivo carry 8 kHz μ-law: connect with encoding=mulaw&sample_rate=8000 and forward each media payload. Exotel carries 8 kHz 16-bit PCM: encoding=linear16&sample_rate=8000. Don’t resample to 16 kHz first; that adds bytes and nothing the model can use.
  • Keep one socket per call. Open it when the call connects and send end when it hangs up.
  • Use your own VAD if you have one. With endpointing=manual, the final comes back straight after your speech_end, with no silence wait on our side. Send the half second of audio from before your VAD fired right after speech_start, so the first word is not clipped.

Billing

₹18 per hour of audio, on HTTP and on the socket. That is 0.5 paise a second. We charge on the seconds of audio we received, rounded up to the next second, with a minimum of 1 second, and round the price up to the next paisa. A 20-second clip costs 10 paise; a 3-minute call streamed in full costs ₹0.90.
  • HTTP: charged once the transcript is written. A request that fails or times out is not charged.
  • Socket: you pay for the audio you send, silence included, however the session ends: with end, with a closed socket, on a timeout or on an error. With manual endpointing you can send audio only around speech, but keep the half second before speech_start. A session longer than 5 minutes is charged in parts as it runs, and the parts add up to what the whole session would cost.
Before a request or a session starts, we check that your wallet is not empty; if it is, you get 402 insufficient_balance and nothing is transcribed. If the wallet runs out during a long session, the session ends with insufficient_balance after its last final. Each charge is one debit row on the same wallet your calls draw from:
GET /v2/wallet/transactions
Live and offline transcription cost the same: offline transcription of recordings is ₹18 per hour too, charged once when the job completes.

Limits

We refuse a request over the session limit instead of queueing it. Honour Retry-After and send it again; a refused request costs nothing. The socket has its own limits on message size, utterance length and idle time, listed in the WebSocket reference. The limit is raised on request, with no change to your code. If you run many calls at once, ask your Mirai contact for a little more than your peak number of simultaneous calls.

Your request log

We log every request and every session, and you can read the log yourself:
200 OK
Rows come newest first, and only from your own workspace: GET /v2/stt/requests/{id} returns a single row. A row holds no audio and no transcript, only what was used and what it cost.

When something fails

Errors on the HTTP endpoint use the same envelope as the rest of the API: The socket refuses a connection with the same codes before the upgrade, and an open session reports errors as error events. Both are listed in the WebSocket reference. If you are stuck on something this page does not cover, reply on your onboarding thread, where a person reads it.