C# / .NET SDK
Official C# client for the Speech Revolutions STT API. Async-first, targets net8.0, in the style of the Deepgram / ElevenLabs .NET clients.
Source on GitHub · report an issue · NuGet
Install
dotnet add package SpeechRevolutionsOr reference the project directly:
<ProjectReference Include="path/to/SpeechRevolutions/SpeechRevolutions.csproj" />Quickstart
TranscribeAsync accepts a local path, an http(s) URL, or a byte[] overload for in-memory audio. With the default OutputType.Json the SDK parses the response into a transcript-first TranscriptResult.
using SpeechRevolutions;
using var client = new SpeechRevolutionsClient(); // SPEECHREVOLUTIONS_API_KEY
var result = await client.TranscribeAsync("meeting.mp3", new TranscribeOptions
{
SpeakerLabels = true, // or Diarize = true
});
Console.WriteLine(result.Text);
foreach (var u in result.Utterances)
Console.WriteLine($"Speaker {u.Speaker}: {u.Text}");From a URL or file
// explicit alias
var result = await client.TranscribeUrlAsync("https://example.com/audio.mp3");
// or just pass the URL — http(s):// paths are downloaded:
var same = await client.TranscribeAsync("https://example.com/audio.mp3");Options
Pass a TranscribeOptions. Diarize is a Deepgram-compatible alias for SpeakerLabels (when set, it wins).
| Option | Type | Default | Notes |
|---|---|---|---|
OutputType | OutputType | Json | Txt | Json | Srt | Vtt | Docx | Pdf |
WordTimestamps | bool | true | Per-word start/end times |
SpeakerLabels | bool | true | Label who spoke each segment |
Diarize | bool? | null | Alias for SpeakerLabels |
Nltk | bool | true | Restore punctuation & capitalization |
Tier | ProcessingTier | Standard | Standard — the only tier currently available |
CustomVocabulary | IReadOnlyList<string>? | null | Domain terms to bias toward |
Progress | bool | false | Render live console bars |
OnUploadProgress | Action<ProgressEvent>? | null | Upload-progress callback |
var result = await client.TranscribeAsync("a.mp3", new TranscribeOptions
{
OutputType = OutputType.Srt,
CustomVocabulary = new[] { "Kubernetes", "Anthropic" },
});Live progress
Unlike AssemblyAI and Deepgram — which expose no percentage for pre-recorded audio — you get real-time progress for both the file upload and the transcription, as a console bar, a callback, or both. They compose: the bars render and your callbacks still fire for every event.
Set Progress = true for bars. An Uploading byte bar renders first, then a Transcribing bar — both single-line, updated in place, and written to stderr (so they never pollute piped stdout).
var result = await client.TranscribeAsync(
"meeting.mp3",
new TranscribeOptions
{
Progress = true, // console bars
// upload progress — Step == "upload"
OnUploadProgress = e => Console.WriteLine($"upload {e.Percent:0}%"),
},
// transcription progress
onProgress: e => Console.WriteLine($"{e.Percent:0}% {e.Step}"));Each ProgressEvent carries Completed, Total, Step, ElapsedSeconds, and a computed Percent (a clamped double? in 0–100, null until it can be computed).
Building a UI? Live progress for web apps shows how to fold both callbacks into a single 0–100 bar you can serve to your frontend.
Result shape
| Member | Description |
|---|---|
result.Text | Full transcript (AssemblyAI / ElevenLabs style) |
result.Transcript | Deepgram-style alias |
result.Words | Word + Start / End / Speaker |
result.Utterances | AssemblyAI-style speaker turns |
result.Content | Raw response bytes |
await result.SaveAsync(path) | Write to disk |
SaveAsync("output") writes output.<OutputType> (extension inferred when the path has none) and returns the final path. The client never writes files on its own.
var outPath = await result.SaveAsync("output"); // -> output.jsonWebhooks
Set CallbackUrl to be notified when a job finishes instead of holding the call open. On completion or permanent failure the platform POSTs a signed JSON body {job_id, status: "completed"|"failed", download_url?, step?, reason?} to your URL, signed with HMAC-SHA256 over the raw body in the X-SR-Signature: sha256=<hex> header (plus X-SR-Event with the status and a unique X-SR-Delivery id). Verify it against the raw request bytes with HMACSHA256 + CryptographicOperations.FixedTimeEquals.
var result = await client.TranscribeAsync("meeting.mp3", new TranscribeOptions
{
CallbackUrl = "https://you.example.com/hook",
});Retrieve results later
Fetch a job by id anytime — useful after a webhook, or when rebuilding state after a restart. The download URL is regenerated on demand.
// List the most-recent jobs (newest first), cursor-paginated.
var page = await client.ListJobsAsync(limit: 10);
Console.WriteLine($"{page.Jobs.Count} job(s); nextBefore={page.NextBefore}");
foreach (var job in page.Jobs)
Console.WriteLine($" {job.JobId} ({job.CreatedAt})");
// Poll a job by id, then fetch its transcript.
var status = await client.GetJobStatusAsync(jobId);
Console.WriteLine($"status: {status.Status}");
if (status.IsCompleted)
{
var result = await client.GetTranscriptAsync(jobId); // downloads + parses
Console.WriteLine(result.Text);
}
else if (status.IsFailed)
{
Console.WriteLine($"{status.FailedStage}: {status.Reason}");
}Auth
export SPEECHREVOLUTIONS_API_KEY=stt_...Or pass it explicitly:
using var client = new SpeechRevolutionsClient(apiKey: "stt_...");Errors
All errors derive from SpeechRevolutionsException: AuthenticationException, RateLimitException, JobNotFoundException, JobFailedException (Step / Reason), UploadException, JobTimeoutException, and ApiException (StatusCode / Body).
Under the hood
The SDK drives the /api/v1/upload flow and waits on the SSE job stream, converting the server's completed/total counts into Percent for you.