Home → Blog → Speech to text in C#: connecting from .NET in five minutes
Speech to text in C#: connecting from .NET in five minutes
In .NET shops the need usually starts with calls: telephony records conversations, the CRM stores deals, and text is the missing link between them. Connecting takes five minutes, either through the official client or with a bare HttpClient.
Using the official client
If the OpenAI package is already in the project, only the endpoint changes:
var options = new OpenAIClientOptions { Endpoint = new Uri("https://voicesscribe.com/v1") };
var client = new OpenAIClient(new ApiKeyCredential(Environment.GetEnvironmentVariable("VS_KEY")), options);
var audio = client.GetAudioClient("whisper-1");
var result = await audio.TranscribeAudioAsync("call.ogg");
Console.WriteLine(result.Value.Text);
Everything else stays the same: parameters, response parsing and exception types are unchanged.
Without third-party packages
When you would rather not add a dependency, HttpClient is enough:
using var http = new HttpClient { Timeout = TimeSpan.FromMinutes(3) };
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("VS_KEY"));
using var form = new MultipartFormDataContent();
using var stream = File.OpenRead("call.ogg");
var file = new StreamContent(stream);
file.Headers.ContentType = new MediaTypeHeaderValue("audio/ogg");
form.Add(file, "file", "call.ogg");
form.Add(new StringContent("whisper-1"), "model");
var response = await http.PostAsync("https://voicesscribe.com/v1/audio/transcriptions", form);
var json = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new InvalidOperationException($"error {(int)response.StatusCode}: {json}");
using var doc = JsonDocument.Parse(json);
Console.WriteLine(doc.RootElement.GetProperty("text").GetString());
The filename in form.Add is required — it is what identifies the audio format.
Handling a stream of calls
Telephony delivers recordings in batches, so the work belongs in a background service. A practical shape: a job queue, a concurrency cap through SemaphoreSlim, retries with growing delays for temporary failures, and the result written back to the deal record.
Create one HttpClient per application — through IHttpClientFactory or a static field. A new instance per request exhausts sockets under load, and that is the classic .NET mistake.
Details that bite
- Timeout. The default is one hundred seconds, not enough for a long recording. Raise it.
- Encoding. Responses are UTF-8; set Console.OutputEncoding on Windows or non-Latin text turns into question marks.
- Response format. Add response_format as another StringContent to get srt, vtt or timed segments.
- Secrets. Keep the key in configuration, not in source control.
Try it on your own recordings. Sign-up takes a minute, and the free minutes are enough to judge the quality.
Get a free API keyFrequently asked questions
Is the official client required?
No, HttpClient from the standard library is entirely sufficient. The client is convenient if the project already uses it.
Why does it time out on long files?
HttpClient waits one hundred seconds by default. For files at the size limit set two or three minutes.
How do I get timed segments?
Add a response_format field with the value verbose_json and the response includes each utterance with start and end times.
Is this suitable for a background service?
Yes, the usual pattern is a hosted service with a queue and a concurrency limit matched to your plan.
Related reading
- How to transcribe a call recording to text — A step-by-step guide to turning phone call recordings into text through an API in minutes: Python and C# samples, response formats and the errors you will meet.
- Transcription API errors and what they actually mean — A walkthrough of transcription API status codes: why 401, 400, 413, 429 and 502 happen, how to fix each one and which failures are worth retrying.
- Speech to text in Java: code on the standard HttpClient — How to send audio for transcription from Java: building a multipart request on the built-in HttpClient, parsing the response, timeouts and error handling.