← Code Recipes
Speech & Audio

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.

C# Python Java JavaScript

WHAT THIS PAGE COVERS

Transcribing a long file by chunking it correctly at silence, not at byte offsets.

Model gpt-transcribe
Package openai
Checked

Model ids change. If a sample returns a 404 on the model, that is the first thing to check against the official docs.

Install

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
Transcribe a file

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);
Word timestamps

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}");
}
Text to speech

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());
Install

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
Transcribe a file

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 25 MB limit, and why byte offsets fail

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)
Transcribing the chunks in order

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))
Text to speech

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")
Install

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"
Transcribe a file

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);
    }
}
Giving the model context

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();
Text to speech

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);
}
Install

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"
Transcribe a file

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);
Splitting a long file at silence

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,
  ]);
}
Transcribing the pieces in order

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(" "));
Text to speech

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

Read the docs ↗

https://developers.openai.com/api/docs/guides/speech-to-text

New drops, every week

Free tools & Claude templates, landing regularly.

We keep shipping new free tools, code recipes and Claude Code templates. Follow @freeyantra on Instagram or join the newsletter so you never miss one.

Follow @freeyantra

or get them by email

Double opt-in. Unsubscribe any time. Privacy