OpenAI Audio Free code examples in C#, Python, Java and JavaScript
Transcription with gpt-transcribe, text to speech, and the realtime speech-to-speech API.
WHAT THIS PAGE COVERS
Transcribing a long file by chunking it correctly at silence, not at byte offsets.
gpt-transcribe
openai
Model ids change. If a sample returns a 404 on the model, that is the first thing to check against the official docs.
The official .NET library. AudioClient is constructed per model, so transcription and speech are two clients rather than two methods on one.
dotnet add package OpenAI
setx OPENAI_API_KEY "sk-your-key" # Windows, then open a new shell
export OPENAI_API_KEY="sk-your-key" # macOS / Linux
THE MODEL IS A CONSTRUCTOR ARGUMENT, not a property on the options — which is why swapping models means building a new client, and why holding one as a singleton per model is the right shape.
using OpenAI.Audio;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
AudioClient transcriber = new(model: "gpt-transcribe", apiKey: apiKey);
AudioTranscription transcription =
await transcriber.TranscribeAudioAsync("meeting.wav");
Console.WriteLine(transcription.Text);
Verbose is the only response format that carries Words and Segments — ask for them and the model returns start and end times you can seek to. Note this is the case where whisper-1 is still the documented choice, so check the model before relying on it.
AudioTranscriptionOptions options = new()
{
ResponseFormat = AudioTranscriptionFormat.Verbose,
TimestampGranularities = AudioTimestampGranularities.Word,
};
AudioTranscription detailed =
await transcriber.TranscribeAudioAsync("meeting.wav", options);
foreach (TranscribedWord word in detailed.Words)
{
Console.WriteLine($"{word.StartTime:mm\\:ss\\.ff} {word.Word}");
}
A second client, because the model is fixed at construction. GenerateSpeechAsync hands back BinaryData — there is no stream-to-file helper, so write the bytes yourself.
AudioClient speaker = new(model: "gpt-4o-mini-tts", apiKey: apiKey);
BinaryData speech = await speaker.GenerateSpeechAsync(
"Today is a wonderful day to build something people love!",
GeneratedSpeechVoice.Coral);
await File.WriteAllBytesAsync("speech.mp3", speech.ToArray());
The same `openai` package as the chat API — audio is a namespace on the same client, not a separate SDK. pydub and ffmpeg are only needed for the chunking step further down.
pip install openai
pip install pydub # only for the chunking step
export OPENAI_API_KEY="sk-your-key" # macOS / Linux
setx OPENAI_API_KEY "sk-your-key" # Windows, then open a new shell
The file goes as a binary handle, not a path and not base64. OpenAI() with no arguments reads OPENAI_API_KEY from the environment, which is the habit worth keeping.
from openai import OpenAI
client = OpenAI()
with open("meeting.wav", "rb") as audio:
transcription = client.audio.transcriptions.create(
model="gpt-transcribe",
file=audio,
)
print(transcription.text)
The endpoint rejects anything over 25 MB, so long recordings must be split. SPLITTING AT A BYTE OFFSET CUTS MID-WORD, and the model then guesses at both halves — you lose a word at every boundary. Split at silence instead.
from pydub import AudioSegment
from pydub.silence import split_on_silence
audio = AudioSegment.from_file("long-call.mp3")
# min_silence_len and silence_thresh are the two knobs. 700ms of quiet
# at 16 dB below the clip's own average is a sentence break in most
# recorded speech; a noisy room needs a higher threshold, not a longer
# window.
pieces = split_on_silence(
audio,
min_silence_len=700,
silence_thresh=audio.dBFS - 16,
keep_silence=250,
)
# Recombine into chunks under the limit rather than transcribing each
# pause separately — more requests means more per-request overhead and
# worse context for the model.
chunks, current = [], AudioSegment.empty()
for piece in pieces:
if len(current) + len(piece) > 10 * 60 * 1000: # ~10 minutes
chunks.append(current)
current = AudioSegment.empty()
current += piece
chunks.append(current)
Join with a space, not an empty string: every chunk ends at a sentence boundary, so the words either side are genuinely separate. Keeping the order is on you — an async gather will return them shuffled.
transcripts = []
for index, chunk in enumerate(chunks):
path = f"chunk-{index}.mp3"
chunk.export(path, format="mp3")
with open(path, "rb") as handle:
result = client.audio.transcriptions.create(
model="gpt-transcribe",
file=handle,
)
transcripts.append(result.text)
print(" ".join(transcripts))
with_streaming_response starts writing before generation finishes. `instructions` is the parameter people miss — it steers delivery in plain English and is the difference between usable and robotic.
with client.audio.speech.with_streaming_response.create(
model="gpt-4o-mini-tts",
voice="coral",
input="Today is a wonderful day to build something people love!",
instructions="Speak in a cheerful and positive tone.",
) as response:
response.stream_to_file("speech.mp3")
The official Java SDK. The client is built once and reads OPENAI_API_KEY from the environment; audio lives under client.audio().
# Gradle
implementation("com.openai:openai-java:4.42.0")
export OPENAI_API_KEY="sk-your-key"
The params class is TranscriptionCreateParams — not AudioTranscriptionCreateParams, which is the name people reach for and the compiler will not find. The result is a union, hence asTranscription().
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.audio.transcriptions.TranscriptionCreateParams;
import java.nio.file.Path;
public class Transcribe {
public static void main(String[] args) {
// fromEnv() reads OPENAI_API_KEY.
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
TranscriptionCreateParams params = TranscriptionCreateParams.builder()
.file(Path.of("meeting.wav"))
.model("gpt-transcribe")
.build();
String text = client.audio()
.transcriptions()
.create(params)
.asTranscription()
.text();
System.out.println(text);
}
}
A prompt biases the transcription toward names and jargon it would otherwise mangle. For a meeting recording, the attendee list and the product names are worth more than any other tuning available here.
TranscriptionCreateParams params = TranscriptionCreateParams.builder()
.file(Path.of("meeting.wav"))
.model("gpt-transcribe")
.prompt("Attendees: Anjali, Rahul, Priya. Product names: Yantra, Signal.")
.build();
String text = client.audio()
.transcriptions()
.create(params)
.asTranscription()
.text();
SpeechCreateParams is the mirror image. The response body is an InputStream, so copy it rather than reading it into memory — a long passage is a large array otherwise.
import com.openai.core.http.HttpResponse;
import com.openai.models.audio.speech.SpeechCreateParams;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
SpeechCreateParams speech = SpeechCreateParams.builder()
.model("gpt-4o-mini-tts")
.voice("coral")
.input("Today is a wonderful day to build something people love!")
.instructions("Speak in a cheerful and positive tone.")
.build();
try (HttpResponse audio = client.audio().speech().create(speech)) {
Files.copy(audio.body(), Path.of("speech.mp3"),
StandardCopyOption.REPLACE_EXISTING);
}
Server-side only. A key in browser JavaScript is a public key, and OpenAI will revoke it once it is scraped.
npm install openai
export OPENAI_API_KEY="sk-your-key"
createReadStream, not readFileSync — the SDK wants a stream it can upload, and a Buffer here throws a type error that does not name the real problem.
import fs from "node:fs";
import OpenAI from "openai";
// Reads OPENAI_API_KEY from the environment.
const openai = new OpenAI();
const transcription = await openai.audio.transcriptions.create({
file: fs.createReadStream("meeting.wav"),
model: "gpt-transcribe",
});
console.log(transcription.text);
25 MB is the hard limit. ffmpeg's silencedetect filter finds the pauses; cutting at those timestamps keeps every word whole, which cutting at byte offsets does not.
import { execFile } from "node:child_process";
import { promisify } from "node:util";
const run = promisify(execFile);
async function silenceBreaks(input) {
// silencedetect writes its findings to stderr, not stdout.
const { stderr } = await run("ffmpeg", [
"-i", input,
"-af", "silencedetect=noise=-30dB:d=0.7",
"-f", "null", "-",
]).catch((error) => error);
return [...String(stderr).matchAll(/silence_end: ([\d.]+)/g)]
.map((match) => Number(match[1]));
}
async function cut(input, start, end, output) {
await run("ffmpeg", [
"-i", input, "-ss", String(start), "-to", String(end),
"-c", "copy", output,
]);
}
A for-await loop rather than Promise.all: the pieces must be joined in order, and parallel requests come back in whatever order they finish.
const breaks = await silenceBreaks("long-call.mp3");
const transcripts = [];
let start = 0;
for (const [index, end] of breaks.entries()) {
const piece = `chunk-${index}.mp3`;
await cut("long-call.mp3", start, end, piece);
start = end;
const result = await openai.audio.transcriptions.create({
file: fs.createReadStream(piece),
model: "gpt-transcribe",
});
transcripts.push(result.text);
}
console.log(transcripts.join(" "));
`instructions` steers the delivery in plain English — tone, pace, character. It is the parameter that makes the difference and the one most examples leave out.
import { writeFile } from "node:fs/promises";
const mp3 = await openai.audio.speech.create({
model: "gpt-4o-mini-tts",
voice: "coral",
input: "Today is a wonderful day to build something people love!",
instructions: "Speak in a cheerful and positive tone.",
});
await writeFile("speech.mp3", Buffer.from(await mp3.arrayBuffer()));
QUESTIONS
Which languages are the OpenAI Audio examples in?
C#, Python, Java and JavaScript. Every sample runs as pasted and reads its credentials from an environment variable — no key is ever written into the code.
Which package do I install for OpenAI Audio?
The samples use openai. The install step on each language tab gives the exact command.
Which OpenAI Audio model do these examples use?
gpt-transcribe, checked against the official documentation on 26 September 2026. Model ids change often; if a call returns a model-not-found error, check the current list in the official docs.
Where is the official OpenAI Audio documentation?
https://developers.openai.com/api/docs/guides/speech-to-text — it is linked at the bottom of this page, and it is the authority whenever it disagrees with a sample here.
ALSO IN SPEECH & AUDIO
The authority
Official OpenAI Audio documentation
Model names, pricing and rate limits change faster than any page here can. When this page and the official docs disagree, the official docs are right.
https://developers.openai.com/api/docs/guides/speech-to-text