Measuring speech-to-text accuracy for Indian English accents in 2026

Hand-drawn illustration showing a user interrupting a real-time AI voice assistant, stopping its current response and triggering a new reply through live voice interaction.

Build a repeatable benchmark to compare seven speech-to-text APIs on Indian English audio. Slice results by accent, noise, and vocabulary before you decide.

No speech-to-text API is the most accurate for Indian English accents in every setting. The ranking changes with your audio (8 kHz phone calls or 16 kHz microphone), your speakers (Hindi-, Tamil-, Bengali- or Punjabi-influenced English), and your vocabulary (bank product names, city names, drug names). The reliable way to answer the question is to measure candidate APIs on your own labelled en-IN audio, tune each one fairly, and compare the results by slice.

This guide walks you through that process end to end.

What you'll build: a repeatable benchmark that ranks speech-to-text APIs on Indian English audio, with before-and-after customization results and a production monitoring plan.

Who it's for: engineers, ML practitioners, and technical contact center owners who are choosing or tuning an ASR vendor.

Prerequisites:

  • API credentials for two or more providers (this guide uses Google Cloud Speech-to-Text V2, Azure Speech, AWS Transcribe, Deepgram, AssemblyAI, and Smallest AI Pulse)

  • Python 3.9+ with jiwer and pandas installed

  • ffmpeg and sox for audio conversion

  • A way to get 3 to 5 hours of representative audio transcribed by humans

Difficulty and time: intermediate. Allow one to two days for data collection and labelling, and a few hours for scripting and runs.

Why Indian English breaks generic speech models

Before you build the test set, know which behaviours you need it to cover. "Indian English" isn't one accent. It's a family of varieties shaped by speakers' first languages, and ASR errors cluster around these traits:

  • Phoneme substitutions: dental and alveolar stops are often retroflexed (for example, "three" and "tree" can sound alike), and /v/ and /w/ may merge.

  • Consonant cluster handling: "school" may be produced as "iskool", which shifts word boundaries for the decoder.

  • Vowel and stress patterns: syllable-timed rhythm and different stress placement change how acoustic models segment words.

  • Code-mixing: speakers move between English and Hindi, Tamil, Telugu, Bengali or Marathi inside one sentence ("Mera account number bata dijiye, please").

  • Local proper nouns: names of people, towns, and products are rare in training data and are the most commonly mistranscribed tokens.

Telephony adds its own damage. Narrowband phone audio, codec compression, background noise, and cross-talk degrade any model, and they degrade accent-sensitive models more. The contact center speech-to-text article on this site covers those audio conditions in more depth.

Aggregate word error rate (WER) hides these failures. A system with a 9% overall WER can sit at 20% on one regional accent group, and you won't see it unless you report by slice. This guide enforces slice reporting for that reason.

Step 1: Build a labelled test set that mirrors your audio

Define your target first. A call center bot, a video dictation tool, and a mobile voice-note feature each need different audio, so don't benchmark on someone else's distribution.

  1. Split the data into two sets. Collect a calibration set of 30 to 60 minutes, which you'll use to tune vocabularies and settings. Collect a separate test set of several hours, which you'll touch only for final scoring. Never tune on the test set.

  2. Tag every utterance with metadata. At minimum, record these fields:

    • accent_group (for example, self-reported first language or region)

    • channel (telephony_8k or wideband_16k)

    • noise_tier (low, medium, high)

    • code_mix (none, light, heavy)

    • speaking_rate (slow, normal, fast)

  3. Balance the slices. As a working rule of thumb, aim for at least 30 minutes of audio, or a few hundred utterances, per accent group. Smaller slices produce noisy WER numbers.

  4. Mix sources. Start with open Indian English corpora where licences permit, then add your own recordings. Open data rarely contains your product names or your telephony noise profile.

  5. Get consent. Confirm that every speaker consented to recording and transcription, and strip or mask personal data before sharing audio with annotators.

Write labelling rules before annotators start

Metrics are only comparable if the references follow one convention. Decide and document:

  • Casing: lowercase everything for scoring.

  • Punctuation: excluded from scoring.

  • Numbers: spoken-form or digit-form, one choice (for example, "two thousand five hundred" vs "2500").

  • Code-mixed segments: transcribe Hindi words in Roman script or Devanagari, one choice, and apply it everywhere.

  • Disfluencies: keep or drop "um", "uh", and false starts.

Checkpoint: you have a calibration folder, a test folder, and a CSV with one row per utterance containing the file path, reference transcript, and the five metadata tags.

Step 2: Standardise the audio and the output format

Differences in preprocessing can swamp differences between vendors, so fix them first.

  1. Convert all audio to one format. For wideband audio:


ffmpeg -i input.wav -ac 1 -ar 16000 -sample_fmt s16 -af loudnorm output_16k.wav
ffmpeg -i input.wav -ac 1 -ar 16000 -sample_fmt s16 -af loudnorm output_16k.wav
ffmpeg -i input.wav -ac 1 -ar 16000 -sample_fmt s16 -af loudnorm output_16k.wav
  1. Keep telephony audio at its native 8 kHz for a telephony slice. Don't upsample it and call it wideband. Run it through the same API as a separate channel tag.

  2. Trim long silences with voice activity detection (VAD) so streaming and batch runs see comparable segments. Keep utterances under roughly 30 seconds.

  3. Decide where punctuation and inverse text normalization (ITN) happen. ITN converts "twenty five lakh" into "25 lakh" or "2500000" depending on the engine. Either disable formatting on every provider and normalise yourself, or enable it everywhere and normalise both sides at scoring time.

Warning: turning on diarization, smart formatting, or profanity filtering on one provider and not another changes the token stream. Hold every such flag constant across providers, or your WER delta measures configuration rather than accuracy.

Checkpoint: every file in the test set has the same sample rate and channel layout within its channel group, and you have a written list of the formatting flags used per provider.

Step 3: Run a baseline with the correct locale and model

Your first pass uses no customization. Its job is to show out-of-the-box performance on en-IN, so confirm each request names the Indian English locale and the newest transcription model available to you.

Provider baselines

Google Cloud Speech-to-Text V2 supports en-IN through the Chirp 3 model (chirp_3). Chirp 3 is regional, so check which locations offer it before you create a recognizer.


curl -X POST \
  "https://${LOCATION}-speech.googleapis.com/v2/projects/${PROJECT_ID}/locations/${LOCATION}/recognizers/_:recognize" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{
    "config": {
      "autoDecodingConfig": {},
      "languageCodes": ["en-IN"],
      "model": "chirp_3"
    },
    "content": "'"$(base64 -w0 sample_16k.wav)"'"
  }'
curl -X POST \
  "https://${LOCATION}-speech.googleapis.com/v2/projects/${PROJECT_ID}/locations/${LOCATION}/recognizers/_:recognize" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{
    "config": {
      "autoDecodingConfig": {},
      "languageCodes": ["en-IN"],
      "model": "chirp_3"
    },
    "content": "'"$(base64 -w0 sample_16k.wav)"'"
  }'
curl -X POST \
  "https://${LOCATION}-speech.googleapis.com/v2/projects/${PROJECT_ID}/locations/${LOCATION}/recognizers/_:recognize" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{
    "config": {
      "autoDecodingConfig": {},
      "languageCodes": ["en-IN"],
      "model": "chirp_3"
    },
    "content": "'"$(base64 -w0 sample_16k.wav)"'"
  }'

Azure Speech supports the en-IN locale. In the Speech SDK or REST API, set the recognition language to en-IN.

AWS Transcribe accepts en-IN as a language code:


aws transcribe start-transcription-job \
  --transcription-job-name baseline-001 \
  --language-code en-IN \
  --media MediaFileUri=s3://your-bucket/sample_16k.wav \
  --output-bucket-name your-output-bucket
aws transcribe start-transcription-job \
  --transcription-job-name baseline-001 \
  --language-code en-IN \
  --media MediaFileUri=s3://your-bucket/sample_16k.wav \
  --output-bucket-name your-output-bucket
aws transcribe start-transcription-job \
  --transcription-job-name baseline-001 \
  --language-code en-IN \
  --media MediaFileUri=s3://your-bucket/sample_16k.wav \
  --output-bucket-name your-output-bucket

Deepgram takes the language as a query parameter. Confirm the exact model and en-IN availability in its language documentation:


curl -X POST \
  "https://api.deepgram.com/v1/listen?model=nova-3&language=en-IN&punctuate=false" \
  -H "Authorization: Token $DEEPGRAM_API_KEY" \
  -H "Content-Type: audio/wav" \
  --data-binary @sample_16k.wav
curl -X POST \
  "https://api.deepgram.com/v1/listen?model=nova-3&language=en-IN&punctuate=false" \
  -H "Authorization: Token $DEEPGRAM_API_KEY" \
  -H "Content-Type: audio/wav" \
  --data-binary @sample_16k.wav
curl -X POST \
  "https://api.deepgram.com/v1/listen?model=nova-3&language=en-IN&punctuate=false" \
  -H "Authorization: Token $DEEPGRAM_API_KEY" \
  -H "Content-Type: audio/wav" \
  --data-binary @sample_16k.wav

AssemblyAI uses an asynchronous job. Check its current docs for the English language-code options and which speech models are available:


curl -X POST https://api.assemblyai.com/v2/transcript \
  -H "Authorization: $ASSEMBLYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "audio_url": "https://your-bucket.example/sample_16k.wav",
    "language_code": "en",
    "punctuate": false,
    "format_text": false
  }'
curl -X POST https://api.assemblyai.com/v2/transcript \
  -H "Authorization: $ASSEMBLYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "audio_url": "https://your-bucket.example/sample_16k.wav",
    "language_code": "en",
    "punctuate": false,
    "format_text": false
  }'
curl -X POST https://api.assemblyai.com/v2/transcript \
  -H "Authorization: $ASSEMBLYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "audio_url": "https://your-bucket.example/sample_16k.wav",
    "language_code": "en",
    "punctuate": false,
    "format_text": false
  }'

Smallest AI exposes speech-to-text through unified routes: POST /waves/v1/stt/ for batch and WS /waves/v1/stt/live for streaming, with the model chosen by the ?model= query parameter. Pulse is the general-purpose speech-to-text model, and Pulse Pro targets English-only batch accuracy. Take the exact model identifiers and request parameters from the Smallest AI Waves documentation.


curl -X POST "https://api.smallest.ai/waves/v1/stt/?model=${PULSE_MODEL_ID}" \
  -H "Authorization: Bearer $SMALLEST_API_KEY" \
  -H "Content-Type: audio/wav" \
  --data-binary @sample_16k.wav
curl -X POST "https://api.smallest.ai/waves/v1/stt/?model=${PULSE_MODEL_ID}" \
  -H "Authorization: Bearer $SMALLEST_API_KEY" \
  -H "Content-Type: audio/wav" \
  --data-binary @sample_16k.wav
curl -X POST "https://api.smallest.ai/waves/v1/stt/?model=${PULSE_MODEL_ID}" \
  -H "Authorization: Bearer $SMALLEST_API_KEY" \
  -H "Content-Type: audio/wav" \
  --data-binary @sample_16k.wav

If you run Speechmatics, set its English language pack in transcription_config and apply the same procedure.

Run the full calibration and test sets through each provider, and save raw transcripts as provider/run_id/utt_id.txt.

Checkpoint: you have one transcript per utterance per provider, and a log recording the model, locale, and flags used for each run.

Step 4: Score with WER, then slice the results

Overall WER comes first, but the decision comes from slices.

Normalise and compute


import re
import jiwer
import pandas as pd

def norm(text: str) -> str:
    text = text.lower()
    text = re.sub(r"[^\w\s']", " ", text)   # drop punctuation
    return re.sub(r"\s+", " ", text).strip()

df = pd.read_csv("results.csv")  # columns: utt_id, provider, ref, hyp, accent_group, channel, noise_tier, code_mix

def corpus_wer(group):
    return jiwer.wer(
        [norm(r) for r in group["ref"]],
        [norm(h) for h in group["hyp"]],
    )

overall = df.groupby("provider").apply(corpus_wer)
by_accent = df.groupby(["provider", "accent_group"]).apply(corpus_wer).unstack()
by_noise = df.groupby(["provider", "noise_tier"]).apply(corpus_wer).unstack()

worst_slice = by_accent.max(axis=1)
print(overall, worst_slice)
import re
import jiwer
import pandas as pd

def norm(text: str) -> str:
    text = text.lower()
    text = re.sub(r"[^\w\s']", " ", text)   # drop punctuation
    return re.sub(r"\s+", " ", text).strip()

df = pd.read_csv("results.csv")  # columns: utt_id, provider, ref, hyp, accent_group, channel, noise_tier, code_mix

def corpus_wer(group):
    return jiwer.wer(
        [norm(r) for r in group["ref"]],
        [norm(h) for h in group["hyp"]],
    )

overall = df.groupby("provider").apply(corpus_wer)
by_accent = df.groupby(["provider", "accent_group"]).apply(corpus_wer).unstack()
by_noise = df.groupby(["provider", "noise_tier"]).apply(corpus_wer).unstack()

worst_slice = by_accent.max(axis=1)
print(overall, worst_slice)
import re
import jiwer
import pandas as pd

def norm(text: str) -> str:
    text = text.lower()
    text = re.sub(r"[^\w\s']", " ", text)   # drop punctuation
    return re.sub(r"\s+", " ", text).strip()

df = pd.read_csv("results.csv")  # columns: utt_id, provider, ref, hyp, accent_group, channel, noise_tier, code_mix

def corpus_wer(group):
    return jiwer.wer(
        [norm(r) for r in group["ref"]],
        [norm(h) for h in group["hyp"]],
    )

overall = df.groupby("provider").apply(corpus_wer)
by_accent = df.groupby(["provider", "accent_group"]).apply(corpus_wer).unstack()
by_noise = df.groupby(["provider", "noise_tier"]).apply(corpus_wer).unstack()

worst_slice = by_accent.max(axis=1)
print(overall, worst_slice)

Compute WER over the pooled words in each group rather than averaging per-utterance WERs. Short utterances would otherwise dominate the average.

Track error slices beyond WER

Add targeted counts so you can see why a provider fails:

  • Proper nouns: the share of reference names that were mistranscribed.

  • Numbers: account numbers, amounts, dates, and phone numbers, scored after normalising spoken and digit forms.

  • Code-mixed spans: WER restricted to utterances tagged code_mix = heavy.

  • High-noise segments: WER where noise_tier = high.

If you plan to train a custom model, note that Azure's Custom Speech evaluation reports WER and token error rate (TER), broken down into insertions, deletions, and substitutions. That breakdown is useful for any provider, since insertions often point to noise and substitutions to accent confusion.

Build the summary table

Fill in this template from your own runs:


Provider

Overall WER

Worst accent-slice WER

Telephony WER

Number error rate

Proper noun error rate

Provider A

from run

from run

from run

from run

from run

Provider B

from run

from run

from run

from run

from run

Rank providers by worst-slice WER and by the slice that matches your business, not by overall WER alone. Vendor-published numbers, including Smallest AI's reported 5.42% average English WER for Pulse Pro, come from the vendor's own test sets. They're a reason to include a model in your shortlist, and they don't replace your measurement on en-IN audio. For context on other providers, see the best speech-to-text APIs in 2026 test on this site.

Checkpoint: you can state, for each provider, its overall WER, its worst accent-slice WER, and its number and proper noun error rates on the baseline configuration.

Step 5: Apply each provider's customization controls

Customization is where Indian English accuracy usually improves the most, because it targets the rare words that generic models miss. Build the term list from the calibration set only: product names, branch and city names, agent scripts, and common Hindi loanwords in the transcripts. Never add terms from the test set.

Google Cloud Speech-to-Text

Use inline phrase sets for adaptation. Whether a given model and region accepts adaptation varies, so check the feature table for your model first and re-test if you switch models.


{
  "config": {
    "autoDecodingConfig": {},
    "languageCodes": ["en-IN"],
    "model": "chirp_3",
    "adaptation": {
      "phraseSets": [
        {
          "inlinePhraseSet": {
            "phrases": [
              { "value": "Kotak Mahindra", "boost": 10 },
              { "value": "RuPay", "boost": 10 }
            ]
          }
        }
      ]
    }
  }
}
{
  "config": {
    "autoDecodingConfig": {},
    "languageCodes": ["en-IN"],
    "model": "chirp_3",
    "adaptation": {
      "phraseSets": [
        {
          "inlinePhraseSet": {
            "phrases": [
              { "value": "Kotak Mahindra", "boost": 10 },
              { "value": "RuPay", "boost": 10 }
            ]
          }
        }
      ]
    }
  }
}
{
  "config": {
    "autoDecodingConfig": {},
    "languageCodes": ["en-IN"],
    "model": "chirp_3",
    "adaptation": {
      "phraseSets": [
        {
          "inlinePhraseSet": {
            "phrases": [
              { "value": "Kotak Mahindra", "boost": 10 },
              { "value": "RuPay", "boost": 10 }
            ]
          }
        }
      ]
    }
  }
}

Azure Speech

Azure Custom Speech lets you train on text and audio, compare accuracy between models, and deploy the winner to a custom endpoint. Upload your calibration transcripts as training data, evaluate against a held-out set, and point your recogniser at the custom endpoint with en-IN as the locale.

AWS Transcribe

Create a custom vocabulary with a table file. The Phrase column is required, and the SoundsLike and IPA columns are deprecated and ignored. Use hyphens to join the words in a multi-word phrase. Save the file tab-delimited:


Phrase	SoundsLike	IPA	DisplayAs
Kotak-Mahindra			Kotak Mahindra
RuPay			RuPay
Phrase	SoundsLike	IPA	DisplayAs
Kotak-Mahindra			Kotak Mahindra
RuPay			RuPay
Phrase	SoundsLike	IPA	DisplayAs
Kotak-Mahindra			Kotak Mahindra
RuPay			RuPay


aws transcribe create-vocabulary \
  --vocabulary-name in-banking-terms \
  --language-code en-IN \
  --vocabulary-file-uri s3://your-bucket/vocab.tsv

aws transcribe start-transcription-job \
  --transcription-job-name tuned-001 \
  --language-code en-IN \
  --media MediaFileUri=s3://your-bucket/sample_16k.wav \
  --settings VocabularyName=in-banking-terms
aws transcribe create-vocabulary \
  --vocabulary-name in-banking-terms \
  --language-code en-IN \
  --vocabulary-file-uri s3://your-bucket/vocab.tsv

aws transcribe start-transcription-job \
  --transcription-job-name tuned-001 \
  --language-code en-IN \
  --media MediaFileUri=s3://your-bucket/sample_16k.wav \
  --settings VocabularyName=in-banking-terms
aws transcribe create-vocabulary \
  --vocabulary-name in-banking-terms \
  --language-code en-IN \
  --vocabulary-file-uri s3://your-bucket/vocab.tsv

aws transcribe start-transcription-job \
  --transcription-job-name tuned-001 \
  --language-code en-IN \
  --media MediaFileUri=s3://your-bucket/sample_16k.wav \
  --settings VocabularyName=in-banking-terms

Deepgram

Pass keyterms as repeated query parameters (keyterm prompting). Confirm in its docs which models support the feature.


curl -X POST \
  "https://api.deepgram.com/v1/listen?model=nova-3&language=en-IN&keyterm=Kotak%20Mahindra&keyterm=RuPay" \
  -H "Authorization: Token $DEEPGRAM_API_KEY" \
  -H "Content-Type: audio/wav" \
  --data-binary @sample_16k.wav
curl -X POST \
  "https://api.deepgram.com/v1/listen?model=nova-3&language=en-IN&keyterm=Kotak%20Mahindra&keyterm=RuPay" \
  -H "Authorization: Token $DEEPGRAM_API_KEY" \
  -H "Content-Type: audio/wav" \
  --data-binary @sample_16k.wav
curl -X POST \
  "https://api.deepgram.com/v1/listen?model=nova-3&language=en-IN&keyterm=Kotak%20Mahindra&keyterm=RuPay" \
  -H "Authorization: Token $DEEPGRAM_API_KEY" \
  -H "Content-Type: audio/wav" \
  --data-binary @sample_16k.wav

AssemblyAI

Use keyterms_prompt for boosting and custom_spelling to force the orthography you want in the output. Availability of keyterms_prompt depends on the speech model, so check the current documentation.


{
  "audio_url": "https://your-bucket.example/sample_16k.wav",
  "language_code": "en",
  "keyterms_prompt": ["Kotak Mahindra", "RuPay", "UPI"],
  "custom_spelling": [
    { "from": ["ru pay", "rupay"], "to": "RuPay" }
  ]
}
{
  "audio_url": "https://your-bucket.example/sample_16k.wav",
  "language_code": "en",
  "keyterms_prompt": ["Kotak Mahindra", "RuPay", "UPI"],
  "custom_spelling": [
    { "from": ["ru pay", "rupay"], "to": "RuPay" }
  ]
}
{
  "audio_url": "https://your-bucket.example/sample_16k.wav",
  "language_code": "en",
  "keyterms_prompt": ["Kotak Mahindra", "RuPay", "UPI"],
  "custom_spelling": [
    { "from": ["ru pay", "rupay"], "to": "RuPay" }
  ]
}

Speechmatics

Speechmatics supports a custom dictionary through additional_vocab inside transcription_config, where each entry has a content field and optional sounds_like hints. Add your term list there and score it like every other provider.

Smallest AI Pulse

Pulse's production feature set includes keyword and vocabulary boosting, code-switching, ITN, diarization, word and sentence timestamps, and PII/PCI redaction. Pulse also covers 38 languages, which matters when your Indian English traffic includes code-mixed speech. Enable keyword boosting using the parameter names in the Waves documentation, and re-run the same calibration set.

Warning: a long boost list can reduce accuracy by pulling ordinary words toward your terms. Start with 20 to 50 high-value terms, measure, and expand only if the numbers improve.

Measure the delta

Re-run the test set with customization on, and compute the change per provider and per slice:


delta = baseline_by_accent - tuned_by_accent   # positive = improvement
delta = baseline_by_accent - tuned_by_accent   # positive = improvement
delta = baseline_by_accent - tuned_by_accent   # positive = improvement

Run the experiment in three stages: baseline, then preprocessing fixes, then customization. Record WER at each stage so you know which change paid off. If a customization improves the proper noun error rate but worsens WER on a slice, you've found a trade-off worth a decision, not a default.

Checkpoint: you have a before-and-after WER table per provider, with slice-level deltas and at least one note on each provider's biggest remaining failure type.

Step 6: Set quality gates before you ship

Turn the benchmark into rules the pipeline enforces:

  • A maximum acceptable worst-slice WER for each accent group you serve.

  • A minimum audio quality check (sample rate, clipping, signal-to-noise estimate) that routes bad audio to a fallback path.

  • A confidence threshold below which a transcript goes to human review. Calibrate it on the calibration set, because confidence scales differ between providers.

  • A fixed transcript formatting policy (ITN and punctuation), so downstream parsers don't break when you change vendors.

Step 7: Check latency, privacy, and deployment before cutover

Accuracy alone doesn't qualify a provider for production. Test these with the same audio:

  1. Latency: measure time to first token on the streaming endpoint (for Smallest AI, WS /waves/v1/stt/live) and total turnaround on batch. Report p50 and p95, not averages. Smallest AI reports speed and latency benchmarks for Pulse, including results on MuBench by Sierra, which you should verify on your own network path.

  2. Concurrency: run 50 to 100 simultaneous streams at your expected peak and watch for rate limits, dropped sockets, and WER changes under load.

  3. Region selection: pick an API region close to your traffic, and confirm the chosen model is available there. Chirp 3, for example, isn't offered in every location.

  4. Privacy and compliance: document where audio is processed, how long it's retained, whether it's used for training, and what redaction exists for PII and payment data. Confirm consent for recording and transcription in your workflow. Smallest AI lists ISO 27001, SOC 2 Type 2, GDPR, and HIPAA compliance, plus an on-prem option for enterprise environments, so compare those controls against the others on your shortlist.

Step 8: Monitor accuracy after launch

Models, callers, and audio paths all drift. Put these in place:

  • Live WER sampling: send a random 0.5 to 1% of calls to human reviewers each week, tagged with the same metadata as your test set.

  • Drift alerts: watch the share of low-confidence transcripts, average SNR, and the mix of telephony versus wideband audio.

  • Targeted error rates: alert on number and proper noun error rates, since these cause the most expensive downstream mistakes.

  • Rollback: keep the baseline configuration versioned, and put each customization behind a feature flag, so you can switch off a bad vocabulary update in minutes.

Refresh the term list quarterly from reviewed transcripts, and re-run the full benchmark whenever a vendor releases a new model.

Troubleshooting common problems

WER is high on every provider. Check your references first. Inconsistent number or code-mix conventions inflate error everywhere. Sample 50 utterances and compare the reference to the hypothesis by hand.

One accent group is far worse than the rest. Check that slice's audio. It may share a channel type or noise tier rather than an accent. Compare WER within the same channel and noise_tier before concluding it's an accent effect.

Customization made WER worse. Your term list is probably too long or contains common words. Cut it back to rare terms and retest on the calibration set.

Telephony results look poor even with a good model. Confirm you didn't upsample 8 kHz audio, and check whether the provider offers a telephony-tuned model or setting.

Streaming results don't match batch. Endpointing and partial-result handling differ. Score only final transcripts, and evaluate streaming and batch as separate configurations.

Numbers are scored as errors although they sound right. Your reference and hypothesis use different number forms. Normalise both sides, or enable ITN uniformly.

What you've accomplished and what to do next

You now have a labelled en-IN test set, a normalised scoring script, baseline and customized results per provider, slice-level reporting that exposes accent-specific failures, and a deployment and monitoring plan. The answer to "which speech-to-text API is most accurate for Indian English accents" is whichever one wins on your worst-slice WER at acceptable latency, and you can now defend that choice with data.

Next steps:

  • Add Pulse and Pulse Pro to your shortlist and run them through Steps 3 to 5, using Pulse Pro for English-only batch jobs where accuracy outweighs latency and Pulse for real-time voice workloads.

  • Grow the test set with the hardest utterances found in production review.

  • Read the primer on automatic speech recognition if your team needs shared terminology for the pipeline components.

  • Re-benchmark on a schedule, and keep the results file under version control so every provider change has a measurable before and after.

Frequently asked questions

Which speech-to-text API is best for Indian English?

Why shouldn't I trust a vendor's supported-languages page?

How many audio files do I need to evaluate an ASR API?

Should I monitor telephony and wideband audio separately?

Can I fix regional errors with simple vendor settings?

Listen to the article
2:00
Listen to the article
2:00

Summarize with AI

Benchmark ASR for Indian English Accents

Compare seven speech-to-text APIs on labeled en-IN audio to choose the most accurate model for your use case.

Summarize with AI

Benchmark ASR for Indian English Accents

Compare seven speech-to-text APIs on labeled en-IN audio to choose the most accurate model for your use case.

Related Blogposts