TIL: Testing speech-to-text with curl
TIL Tuesday: testing a speech-to-text API takes one curl command, not a full integration. I spent months writing SDK wrappers before realising that curl tells me everything I need to know in about three seconds.
curl -s -X POST "https://api.deepgram.com/v1/listen?model=nova-3&language=en&punctuate=true" \
-H "Authorization: Token YOUR_DG_API_KEY" \
-H "Content-Type: audio/wav" \
--data-binary @test.wav \
| jq '.results.channels[0].alternatives[0].transcript'
The response comes back with the full transcript, confidence scores, word-level timings, and a bunch of metadata. Piping through jq grabs just the transcript. That is usually enough to know whether the format is right and the accuracy is acceptable.
What curl reveals about speech-to-text that SDKs hide
SDKs abstract away the HTTP layer. That is their job. But when something goes wrong, the abstraction becomes a wall you have to dig through. curl shows you exactly what the API returned, status code included.
A 400 response means the audio format is wrong. A 401 means the API key is bad. A 413 means the file is too big. The SDK might wrap these into a generic exception. curl gives you the raw response, which is faster to read than stack traces.
# Check the full response headers too
curl -s -D - -X POST "https://api.deepgram.com/v1/listen?model=nova-3" \
-H "Authorization: Token YOUR_DG_API_KEY" \
-H "Content-Type: audio/wav" \
--data-binary @test.wav \
-o /dev/null
The -D - flag prints response headers to stdout. The dg-request-id header is the quickest way to reference a request when you need support.
Parameters worth setting from day one
The defaults work but they are not what you want. These three parameters change the output more than anything else:
punctuate=trueadds capitalisation, commas, and full stops. Without it you get a wall of lowercase words.model=nova-3picks the model. The default is usually base, which is less accurate but faster. Nova-3 was the production model at Deepgram in 2026.language=enforces the language. Auto-detection works but adds latency and can guess wrong on short audio.
curl -s -X POST "https://api.deepgram.com/v1/listen?model=nova-3&language=en&punctuate=true&smart_format=true&utterances=true" \
-H "Authorization: Token YOUR_DG_API_KEY" \
-H "Content-Type: audio/wav" \
--data-binary @recording.wav \
| jq '.results.channels[0].alternatives[0].paragraphs'
smart_format converts numbers, dates, and currency into readable forms. utterances splits the transcript into speaker turns if diarization is enabled. I learned these one at a time as developers asked why their transcripts looked wrong.
When curl is not enough
Curl tests the pre-recorded endpoint. For streaming audio you need a WebSocket client. But for the common case of testing an audio file against an STT model, curl is the fastest path from file to transcript.
FAQ
What is the minimum curl command for STT?
A POST request to the STT endpoint with the audio file as the body and your API key in the Authorization header. Add --data-binary @file.wav and pipe the response through jq to extract the transcript.
Why use curl instead of an SDK for testing?
SDKs handle errors by throwing exceptions. curl shows you the raw HTTP response, which is faster to interpret when the issue is a wrong parameter or audio format mismatch. You also skip the setup step entirely.
Does this work with any STT API?
The URL and parameter names change between providers but the pattern is the same. POST the audio file, send an auth token, get JSON back. The curl workflow translates across providers with minimal changes.
Can curl handle streaming STT?
No. Streaming uses WebSockets, which require a persistent connection. For pre-recorded audio files, curl works. For live audio you need a WebSocket client like wscat or a language-specific library.