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

bash
dotnet add package SpeechRevolutions

Or reference the project directly:

xml
<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.

Program.cs
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

csharp
// 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).

OptionTypeDefaultNotes
OutputTypeOutputTypeJsonTxt | Json | Srt | Vtt | Docx | Pdf
WordTimestampsbooltruePer-word start/end times
SpeakerLabelsbooltrueLabel who spoke each segment
Diarizebool?nullAlias for SpeakerLabels
NltkbooltrueRestore punctuation & capitalization
TierProcessingTierStandardStandard — the only tier currently available
CustomVocabularyIReadOnlyList<string>?nullDomain terms to bias toward
ProgressboolfalseRender live console bars
OnUploadProgressAction<ProgressEvent>?nullUpload-progress callback
csharp
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).

csharp
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

MemberDescription
result.TextFull transcript (AssemblyAI / ElevenLabs style)
result.TranscriptDeepgram-style alias
result.WordsWord + Start / End / Speaker
result.UtterancesAssemblyAI-style speaker turns
result.ContentRaw 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.

csharp
var outPath = await result.SaveAsync("output"); // -> output.json

Webhooks

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.

csharp
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.

csharp
// 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

bash
export SPEECHREVOLUTIONS_API_KEY=stt_...

Or pass it explicitly:

csharp
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).

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.