Skip to content

Instantly share code, notes, and snippets.

@jdegger
Created August 26, 2026 09:48
Show Gist options
  • Select an option

  • Save jdegger/9e703ef075565d3945a6e893f787f1ab to your computer and use it in GitHub Desktop.

Select an option

Save jdegger/9e703ef075565d3945a6e893f787f1ab to your computer and use it in GitHub Desktop.
OpenAI gpt-transcribe example: Dutch WAV + BHV-Knop terms (no API keys)
/**
* Example: Dutch speech-to-text with OpenAI `gpt-transcribe`.
*
* export OPENAI_API_KEY=...
* npx tsx transcribe-bhv.ts ./clip.wav
*
* Do not hardcode API keys. Restricted project keys need audio transcription
* scope; `whisper-1` may be blocked even when `gpt-transcribe` works.
*/
import { readFile } from "node:fs/promises";
import { basename } from "node:path";
const TRANSCRIBE_URL = "https://api.openai.com/v1/audio/transcriptions";
const MODEL = "gpt-transcribe";
/** Prompt should be in the same language as the audio. */
const PROMPT =
"Nederlandse opname over BHV-Knop.nl, een compleet BHV-oproepsysteem. " +
"Vaak genoemde termen: BHV, BHV-er, BHV-ers, BHV-Knop, BHV-app, " +
"BHV-organisatie, BHV-oproepsysteem, BHV-groepsgesprek, BHV-plan, " +
"basisstation, aanwezigheidskiosk, kiosk, alarmafhandeling, " +
"aanwezigheidsdetectie, bedrijfshulpverlening, bedrijfsongeval, " +
"portofoon, aanmelden, afmelden, centrale post, akoestisch signaal, " +
"groepsgesprek.";
/** Literal terms that STT otherwise mangles (BHV, knop, kiosk, …). */
const KEYWORDS = [
"BHV",
"BHV-Knop",
"BHV-Knop.nl",
"BHV-app",
"BHV-oproepsysteem",
"BHV-organisatie",
"BHV-groepsgesprek",
"BHV-plan",
"bedrijfshulpverlening",
"bedrijfsongeval",
"basisstation",
"aanwezigheidskiosk",
"aanwezigheidsdetectie",
"alarmafhandeling",
"portofoon",
"kiosk",
"aanmelden",
"afmelden",
"centrale post",
"akoestisch signaal",
"groepsgesprek",
];
type TranscriptionResponse = {
text: string;
languages?: { code: string }[];
usage?: { type: string; seconds?: number };
error?: { message: string; code?: string };
};
async function transcribe(audioPath: string, apiKey: string): Promise<TranscriptionResponse> {
const bytes = await readFile(audioPath);
const form = new FormData();
form.set("model", MODEL);
form.set("prompt", PROMPT);
form.set(
"file",
new Blob([bytes], { type: "audio/wav" }),
basename(audioPath),
);
// `gpt-transcribe` uses `languages` (list), not `language`.
form.append("languages[]", "nl");
for (const keyword of KEYWORDS) {
form.append("keywords[]", keyword);
}
const response = await fetch(TRANSCRIBE_URL, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
body: form,
});
const body = (await response.json()) as TranscriptionResponse;
if (!response.ok) {
throw new Error(body.error?.message ?? `HTTP ${response.status}`);
}
return body;
}
const apiKey = process.env.OPENAI_API_KEY;
const audioPath = process.argv[2];
if (!apiKey) {
throw new Error("Set OPENAI_API_KEY in the environment.");
}
if (!audioPath) {
throw new Error("Usage: npx tsx transcribe-bhv.ts ./clip.wav");
}
const result = await transcribe(audioPath, apiKey);
console.log(result.text);
if (result.languages?.length) {
console.error("languages:", result.languages.map((item) => item.code).join(", "));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment