Arabic is one of the hardest languages for speech-to-text. A single country can have several spoken dialects, people switch into English mid-sentence, and names, numbers and email addresses have to survive the trip into text. This guide covers how to transcribe Arabic with the Hakim API, which of the two paths to pick, and the settings that matter most for accuracy.
One model, two ways in
Every transcription on Hakim runs on Hakim Arab v2 (hakim-arab-v2). It covers Modern Standard Arabic and 15 regional dialects, and treats English words inside an Arabic sentence as normal input rather than noise. You reach it in two ways:
- Batch,
POST /v1/audio/transcriptions, for recordings that already exist: calls, interviews, voicemail, podcasts. - Realtime, a WebSocket at
wss://api.tryhakim.ai/v1/audio/transcriptions/stream, for audio captured right now: voice agents, live captions, call analytics during the call.
Batch transcription
curl https://api.tryhakim.ai/v1/audio/transcriptions \
-H "Authorization: Bearer $HAKIM_API_KEY" \
-F "file=@call.mp3" \
-F "model=hakim-arab-v2" \
-F "language=ar" \
-F "response_format=verbose_json" \
-F "timestamps=word" \
-F "diarize=true"
response_formatcan bejson,text,srt,vttorverbose_json. Picksrtorvttfor subtitles.timestampscan beword,segmentornone. Word timings are what you want for search, highlighting and editing.diarizelabels who spoke when, which is essential for call centres.- Instead of uploading, you can send JSON with a public
url(for example a presigned storage link) and we fetch it.
Files over 25 MiB or ten minutes return 202 with a job. Poll GET /v1/jobs/{id} or, better, subscribe to the job.succeeded webhook.
Realtime transcription
The realtime socket uses a familiar event contract. You configure the session, stream audio as base64 inside JSON frames, and read partial and final text as it arrives:
import { WebSocket } from 'ws';
const ws = new WebSocket('wss://api.tryhakim.ai/v1/audio/transcriptions/stream', {
headers: { Authorization: `Bearer ${process.env.HAKIM_API_KEY}` },
});
ws.on('open', () => {
ws.send(
JSON.stringify({
type: 'session.update',
session: {
model: 'hakim-arab-v2',
language: 'ar',
input_audio_format: 'pcm16',
input_sample_rate: 16000,
trim_leading_silence: true,
},
}),
);
});
ws.on('message', (raw) => {
const event = JSON.parse(raw.toString());
if (event.type === 'transcription.delta') process.stdout.write(event.text);
if (event.type === 'transcription.done') console.log('\nFINAL:', event.text);
});
// For each 50-250 ms chunk of audio from the microphone or call:
// ws.send(JSON.stringify({ type: 'input_audio_buffer.append', audio: chunk.toString('base64') }));
// When the speaker pauses:
// ws.send(JSON.stringify({ type: 'input_audio_buffer.commit' }));
The first partial usually arrives within about 150 ms of the matching audio. transcription.done carries the final text for everything up to the last commit.
Settings that decide accuracy
- Set the language. Send
language: "ar". For calls that mix Arabic and English, sendlanguage_hints: ["ar", "en"]instead. Recognition is noticeably more reliable when it knows what to expect. - Teach it your words. A
vocabularyobject withterms(names, brands, acronyms) and a one-sentencenotesdescribing the call fixes the words a general model can't know. Our vocabulary guide covers the patterns and pitfalls. - Keep silence out of the start. Calls often open with seconds of silence.
trim_leading_silence: trueholds audio until speech starts. - Send the right format.
pcm16at 16 kHz is ideal for microphones. Phone audio can go in directly asmulawat 8 kHz, andopusis accepted too. - Commit on pauses. Sending
input_audio_buffer.commitwhen the speaker stops gives you a clean final transcript for each turn.
Which path should you use?
If the audio exists before you need the text, use batch: it is simpler, gives you subtitles and diarization in one call, and handles long files as jobs. If someone is waiting for the text while they speak, use realtime. Many products use both: realtime during the call for the agent, batch afterwards for the archive.
The streaming guide has the full realtime protocol, and the free tier includes 10,000 credits a month to try it on your own recordings.