Sample Rate for Speech Recognition

Short answer. 16 kHz is the working standard for speech recognition, because it captures everything up to 8 kHz and human speech carries almost no useful energy above that. The Nyquist limit says a sample rate captures frequencies up to half the rate: 16 kHz reaches 8 kHz, 8 kHz reaches 4 kHz. Recording below the needed rate does not just lose high frequencies — it folds them back down as phantom tones that were never in the original. That is aliasing, and it is permanent.

How to do it

The rule is half the sample rate

A signal sampled at F samples per second can represent frequencies up to F/2 without ambiguity. That is the Nyquist limit. 16 kHz gives you 8 kHz of bandwidth; the 8 kHz telephone band gives you 4 kHz.

Speech energy is concentrated between 100 Hz and 4 kHz, which is why the telephone network was designed at 8 kHz and why it worked. What lives above 4 kHz is the fricatives — /s/, /f/, /sh/ — and their energy is real but low. Losing them costs intelligibility on a minority of words and costs a recognizer accuracy on exactly those words.

What aliasing actually does

Sampling below the Nyquist rate does not filter the high frequencies out. It reflects them. A 3000 Hz tone sampled at 4000 Hz appears at 1000 Hz — a component that does not exist in the original signal and cannot be told apart from a real 1000 Hz tone afterwards.

The run below shows it. The source contains 700 Hz and 3000 Hz. Taking every fourth sample produces a strong 1000 Hz peak at 0.700 of the level, and nothing in the output marks that peak as fake.

Decimating properly

To lower a sample rate you must low-pass filter first, then drop samples. scipy.signal.resample_poly does both and is the right default; librosa.resample wraps the same idea with a different filter design.

Taking every fourth sample without the filter — a slice like signal[::4] — is the mistake that produces the phantom tone above. The filter is not optional, and it is the whole difference between the last two blocks of output.

Choosing a rate for a data specification

16 kHz is the right default for speech data in 2026. 8 kHz is acceptable when the deployment target is telephony and you want the training data to match the deployment band. 44.1 or 48 kHz buys nothing for recognition and costs storage and preprocessing time.

The one case where a higher rate matters is when the same recording will also feed a music or acoustic model, or when you want headroom for dereverberation. Otherwise record at 16 kHz and state it in the specification, because a delivery at mixed rates is a preprocessing bug waiting to happen.

Code

Build a 16 kHz signal with a 700 Hz and a 3000 Hz component, then decimate it two ways and look for the tone that should not exist.

import numpy as np
from scipy.signal import resample_poly

SR = 16000
t = np.arange(SR) / SR
signal = np.sin(2 * np.pi * 700 * t) + 0.7 * np.sin(2 * np.pi * 3000 * t)


def top_peaks(x, sr, k=3):
    spec = np.abs(np.fft.rfft(x * np.hanning(len(x))))
    freqs = np.fft.rfftfreq(len(x), 1 / sr)
    out = []
    for i in np.argsort(spec)[::-1]:
        if all(abs(freqs[i] - f) > 150 for f, _ in out):
            out.append((freqs[i], spec[i] / spec.max()))
        if len(out) == k:
            break
    return out


runs = [
    ("source, 16 kHz", signal, 16000),
    ("every 4th sample, 4 kHz", signal[::4], 4000),
    ("resample_poly, 4 kHz", resample_poly(signal, 1, 4), 4000),
]
for name, x, sr in runs:
    print(f"{name}   (Nyquist {sr / 2:.0f} Hz)")
    for f, a in top_peaks(x, sr):
        print(f"    {f:>7.0f} Hz   relative level {a:.3f}")
    print()
  • pip install numpy scipy
  • The naive slice produces a 1000 Hz component at 0.700 — exactly the level of the real 3000 Hz tone, folded down because 4000 - 3000 = 1000.
  • resample_poly produces no such component. Its 3000 Hz energy is filtered out before the samples are dropped, which is the whole point of the filter.

Where this goes wrong

Resampling without an anti-aliasing filter

Slicing x[::4] to go from 16 kHz to 4 kHz adds tones that were never in the recording, and no later processing removes them. In the example on this page a 3000 Hz tone comes back as a 1000 Hz tone at the same level, which a model will learn as a real feature. Use resample_poly or librosa.resample.

Upsampling an 8 kHz source to 16 kHz and calling it 16 kHz

Upsampling is safe and sometimes needed to match a model input, but it does not recover the 4 to 8 kHz band. The data is still telephone-band, and describing it as 16 kHz quality misleads whoever trains on it.

Mixing sample rates in one delivery

A corpus that is 90% at 16 kHz and 10% at 8 kHz trains unevenly, because the model sees a different spectral band for a tenth of the data. Either resample everything to the lowest rate present or keep the two sets separate and labeled.

Trusting a filename extension or a manifest column

A .wav header carries its own sample rate, and files get relabeled during collection. Read the header for every file and verify, rather than trusting the spreadsheet.

Assuming a 16 kHz file has content up to 8 kHz

A file stored at 16 kHz that was upsampled from 8 kHz has no energy above 4 kHz. A model trained on a mix of real and upsampled 16 kHz files learns that the top octave is sometimes silent for no acoustic reason. Check the spectrum, not the header, when the provenance of the audio is unclear.

When to buy the data instead of building the pipeline

Buy when you need a specific rate and band your own recordings cannot provide — telephone-band audio for a call center deployment, or a corpus recorded consistently at one rate across hundreds of speakers, because consistency is the expensive part. Build it yourself when you control the recording app and the environment. The sample rate is a decision you make before recording, and the only way to fix it afterwards is to record again. State the rate in the delivery specification and verify it against the file headers.

How buying training data works →

More technical reference

All technical reference →

Need data for Sample Rate for Speech Recognition?

Tell us the language, the hours, and what the data needs to look like. You will get a real number and a real timeline — not a range. If we cannot source it well, we will tell you that instead.

  • Pilot batch before the full run, so problems surface early.
  • Consent documentation delivered with the data.
  • No medical or clinical data. No recorded telephone calls.

We reply within two business days. Your details are used only to answer this request. See our privacy policy.

Contact

Talk to a human

Send a specification and we will come back with a real number and timeline.

Submit a sourcing request

Or email hello@linguacorpus.com