Custom spelling
The problem. The model hears a word correctly but writes it its own way: “e mail” where your style guide says “e-mail”, “sequel” for “SQL”, “gladia” without its capital. Custom spelling rewrites those variants into your spelling, in every transcript, before you receive them.
Where it works. Every model.
Step by step
Section titled “Step by step”1. Map each spelling to its variants
Section titled “1. Map each spelling to its variants”{ "realtime_processing": { "custom_spelling": true, "custom_spelling_config": { "spelling_dictionary": { "SQL": ["sequel"], "e-mail": ["email", "e mail"] } } }}Each key is the spelling you want; its values are the variants to replace.
2. Start a session with it
Section titled “2. Start a session with it”Each program streams a 16-bit WAV file and prints what comes back. Put your API key in the GLADIA_API_KEY environment variable, then run python custom_spelling.py meeting.wav.
# pip install gladiaio-sdkimport osimport sysimport threadingimport wave
from gladiaio_sdk import GladiaClient, LiveV2InitRequest
API_URL = os.environ.get("GLADIA_API_URL", "https://eu1.api.gladia.io")API_KEY = os.environ["GLADIA_API_KEY"]
with wave.open(sys.argv[1]) as wav: # a 16-bit PCM WAV file rate, channels = wav.getframerate(), wav.getnchannels() audio = wav.readframes(wav.getnframes())
config = { "encoding": "wav/pcm", "sample_rate": rate, "bit_depth": 16, "channels": channels, "model": "solaria-3", "language_config": { "languages": [ "en" ] }, "realtime_processing": { "custom_spelling": True, "custom_spelling_config": { "spelling_dictionary": { "Gladia": [ "gladya", "glad ia" ], "SQL": [ "sequel" ], "e-mail": [ "email", "e mail" ] } } }}
client = GladiaClient(api_key=API_KEY, api_url=API_URL)session = client.live_v2().start_session(LiveV2InitRequest.from_dict(config))ended, errors = threading.Event(), []
@session.on("message")def on_message(message): if message.type == "transcript" and message.data.is_final: print(message.data.utterance.text)
session.on("error", errors.append)session.once("ended", lambda _: ended.set())
step = rate * channels * 2 // 10 # 100 ms of audio per chunkfor start in range(0, len(audio), step): session.send_audio(audio[start : start + step]) # live audio: send each chunk as it is capturedsession.stop_recording()
ended.wait(timeout=60)sys.exit(1 if errors or not ended.is_set() else 0)// npm install @gladiaio/sdkimport { readFileSync } from "node:fs";import { GladiaClient } from "@gladiaio/sdk";
const { sampleRate, channels, pcm } = readWav(process.argv[2]); // a 16-bit PCM WAV file
const config = { encoding: "wav/pcm", sample_rate: sampleRate, bit_depth: 16, channels, model: "solaria-3", language_config: { languages: [ "en" ] }, realtime_processing: { custom_spelling: true, custom_spelling_config: { spelling_dictionary: { Gladia: [ "gladya", "glad ia" ], SQL: [ "sequel" ], "e-mail": [ "email", "e mail" ] } } }};
const client = new GladiaClient({ apiKey: process.env.GLADIA_API_KEY, apiUrl: process.env.GLADIA_API_URL ?? "https://eu1.api.gladia.io",});const session = client.liveV2().startSession(config);
session.on("message", (message) => { if (message.type === "transcript" && message.data.is_final) console.log(message.data.utterance.text);});session.on("error", (error) => { console.error(error); process.exitCode = 1;});session.once("ended", () => process.exit());
const step = (sampleRate * channels * 2) / 10; // 100 ms of audio per chunkfor (let start = 0; start < pcm.length; start += step) { session.sendAudio(pcm.subarray(start, start + step)); // live audio: send each chunk as it is captured}session.stopRecording();
function readWav(path) { const file = readFileSync(path); let fmt, pcm; for (let pos = 12; pos < file.length; ) { const id = file.toString("ascii", pos, pos + 4); const size = file.readUInt32LE(pos + 4); if (id === "fmt ") fmt = { channels: file.readUInt16LE(pos + 10), sampleRate: file.readUInt32LE(pos + 12) }; if (id === "data") pcm = file.subarray(pos + 8, pos + 8 + size); pos += 8 + size + (size % 2); } return { ...fmt, pcm };}Create the session, then stream its audio to the returned url (Live WebSocket):
curl -X POST https://eu1.api.gladia.io/v2/live \ -H "x-gladia-key: $GLADIA_API_KEY" \ -H "content-type: application/json" \ -d '{ "encoding": "wav/pcm", "sample_rate": 16000, "bit_depth": 16, "channels": 1, "model": "solaria-3", "language_config": { "languages": [ "en" ] }, "realtime_processing": { "custom_spelling": true, "custom_spelling_config": { "spelling_dictionary": { "Gladia": [ "gladya", "glad ia" ], "SQL": [ "sequel" ], "e-mail": [ "email", "e mail" ] } } } }'3. Check the result
Section titled “3. Check the result”{ "type": "transcript", "data": { "is_final": true, "utterance": { "text": "Send the SQL query by e-mail to Gladia." } } }The rewrite applies to partial and final transcripts, and to the text of their words.
Limits and gotchas
Section titled “Limits and gotchas”- Whole words, any case. “sequel” is rewritten, “sequels” is not. Add each form you need as a variant.
- The longest variant wins. With “e mail” and “mail” both listed, “e mail” is rewritten first.
- It does not change what is recognised. If the model writes something else entirely, there is nothing to rewrite: help it hear the word with custom vocabulary, then fix the spelling here.
- Values must be lists.
{"SQL": "sequel"}is refused (400): write{"SQL": ["sequel"]}. - Translations are not rewritten. Translation works from the audio, not from the transcript.
- Size limit. The spelling dictionary shares the session’s URL budget with the vocabulary and
custom_metadata: a few hundred entries in all, else400“the session configuration is too large…”. - Entities keep their tags. With entities or PII redaction, the rewrite never touches the inline elements, only the words.
Troubleshooting
Section titled “Troubleshooting”| symptom | cause | fix |
|---|---|---|
| a variant is not rewritten | it is part of a longer word, or a different form | add that exact form as a variant |
400 on custom_spelling_config |
a value is a string, not a list | wrap it in a list |
| the spelling flag is on, nothing changes | custom_spelling_config missing, or the variant is never written by the model |
send the dictionary; check the raw transcript first |