Last reviewed: 2026-07-30
Direct answer
A CometAPI text-to-speech request from Node.js needs a speech model, input text, and a voice. Send those fields through the OpenAI-compatible client, treat the successful response as binary audio, and write its bytes to a file with an extension that matches the requested format. For an MP3, set response_format to mp3 and do not try to parse the response as JSON.
Install the Node.js SDK used by the documented example:
npm install openai
Inject COMETAPI_KEY and COMETAPI_BASE_URL through your runtime configuration. The base URL must be the /v1 value documented by CometAPI. Do not paste either value into source control. The related guides on keeping CometAPI keys out of repositories
and verifying the client base URL
cover those two boundaries in more detail.
This complete example writes to a temporary file first, moves a nonempty result into place, emits sanitized operational fields, and sets a nonzero process exit code after a handled failure:
import OpenAI from "openai";
import { randomUUID } from "node:crypto";
import { mkdir, rename, rm, stat, writeFile } from "node:fs/promises";
import path from "node:path";
import process from "node:process";
const cometKey = process.env.COMETAPI_KEY;
const cometBase = process.env.COMETAPI_BASE_URL;
if (!cometKey || !cometBase) {
throw new Error("Required CometAPI environment configuration is missing.");
}
const client = new OpenAI({
apiKey: cometKey,
baseURL: cometBase,
});
const runId = randomUUID();
const startedAt = Date.now();
const input = "Welcome. Your report is ready to review.";
const outputDir = path.resolve("generated-audio");
const finalPath = path.join(outputDir, `${runId}.mp3`);
const temporaryPath = `${finalPath}.part`;
try {
await mkdir(outputDir, { recursive: true });
const response = await client.audio.speech.create({
model: "tts-1",
voice: "alloy",
input,
response_format: "mp3",
speed: 1,
});
const audio = Buffer.from(await response.arrayBuffer());
if (audio.length === 0) {
throw new Error("The speech response was empty.");
}
await writeFile(temporaryPath, audio, { flag: "w" });
await rename(temporaryPath, finalPath);
const file = await stat(finalPath);
console.info(JSON.stringify({
event: "speech_generation_completed",
run_id: runId,
model: "tts-1",
voice: "alloy",
output_format: "mp3",
input_chars: input.length,
bytes_written: file.size,
elapsed_ms: Date.now() - startedAt,
}));
} catch (error) {
await rm(temporaryPath, { force: true }).catch(() => {});
const status =
typeof error === "object" &&
error !== null &&
"status" in error &&
Number.isInteger(error.status)
? error.status
: null;
console.error(JSON.stringify({
event: "speech_generation_failed",
run_id: runId,
model: "tts-1",
voice: "alloy",
output_format: "mp3",
input_chars: input.length,
http_status: status,
error_type: error instanceof Error ? error.name : "UnknownError",
elapsed_ms: Date.now() - startedAt,
}));
process.exitCode = 1;
}
The code deliberately does not log the input text, configuration values, raw response bytes, or a provider error body. Its catch block also does not print or rethrow the original SDK error. Setting process.exitCode still reports command failure to a shell or supervisor, while preventing Node.js from rendering the original error object after the sanitized event.
Who this is for
This tutorial is for Node.js developers who already have text and need a downloadable or playable speech file. Typical uses include short narration, accessibility features, read-aloud controls, and generated status messages. It focuses on a bounded server-side request, not a browser-only integration or a realtime voice conversation.
You should be comfortable installing an npm package, supplying environment configuration through your deployment system, and checking a generated file locally. Production deployment also requires a retention policy for audio files and an end-user disclosure when an upstream model’s policy requires listeners to know that a voice is AI-generated.
Key takeaways
- The documented CometAPI speech route is
POST /v1/audio/speech. - The minimum request consists of
model,input, andvoice. - A successful response contains audio bytes rather than a JSON result object.
- The documented default output is MP3; the requested format and file extension should agree.
- Start with a short canary sentence before changing the voice, model, speed, or format.
- Log dimensions and outcomes, not source text, configuration values, binary output, or an unfiltered SDK error.
- Correct contract and configuration errors before retrying; reserve backoff for transient or capacity-related failures.
Sources checked
The CometAPI Create speech reference is the primary contract source. It documents the route, JSON body, supported voices and formats, input limit, speed range, binary response, and Node.js file-writing example.
The CometAPI Audio APIs overview distinguishes speech generation from transcription and translation. It also supplies the relevant status-code handling guidance and confirms that a successful speech response is binary audio.
The CometAPI tts-1-hd model page
describes tts-1-hd as a quality-focused text-to-speech option on the same speech endpoint. Treat its live model and pricing information as changeable catalog data and verify it before choosing the model for production.
The OpenAI text-to-speech guide provides upstream context for the compatible SDK workflow and states that users must be clearly told when the voice they hear is AI-generated. Its model-specific options are not automatically part of the CometAPI contract, so CometAPI’s endpoint reference remains authoritative for gateway fields.
Contract details to verify
The refetched CometAPI contract accepts an application/json body on POST /v1/audio/speech. Its required fields are model, input, and voice. The documented input maximum is 4,096 characters. The endpoint lists mp3, opus, aac, flac, wav, and pcm as output choices, with MP3 as the default. It also documents a speed range from 0.25 through 4.0.
Do not infer support for a field merely because an upstream provider guide shows it. For example, an upstream model may accept an additional instruction field, while the refetched CometAPI speech contract lists only its documented gateway fields. Check the CometAPI contract and selected model page again before extending the request.
Model selection deserves a separate check. The endpoint example uses tts-1. The refetched model directory presents tts-1-hd as a higher-quality option where output quality matters more than minimizing latency. Start with the endpoint’s simple tts-1 example, establish a baseline, and compare another currently available model only after the basic path works. Verify current availability and pricing rather than copying old catalog assumptions into application code.
A concrete operator workflow has both a happy path and an error path:
- Preflight the runtime. Confirm that both required environment settings exist, the base URL ends at the documented version boundary, the input is no more than 4,096 characters, and the output directory is writable.
- Run a canary with one short, non-sensitive sentence,
tts-1,alloy, speed1, and MP3 output. Keep every optional change out of this first request. - Validate the happy path. Require a nonempty buffer, write it to a
.partfile, move it to its final.mp3name, check that the file size is greater than zero, and listen to the result before scaling traffic. - Inspect sanitized logs. A successful event should contain
run_id,model,voice,output_format,input_chars,bytes_written, andelapsed_ms. These fields let an operator correlate runs and identify empty or unusually large results without recording the spoken text. - Classify the error path by status. Record
http_statuswhen available and a broaderror_type, remove the partial file, and set a nonzero process exit code. Do not print, serialize, or rethrow the original SDK error at the top level. - Retry only the appropriate class. Correct 400, 401, and 404 conditions first. Apply backoff to 429, 500, or 503 responses, and reduce concurrency when rate limiting is the cause.
- Expand one dimension at a time. Change the voice, model, speed, or format separately so a regression has one likely cause.
Before exposing generated speech to end users, add a clear AI-voice disclosure where the chosen upstream model’s policy requires it. The disclosure belongs in the product experience, not only in technical documentation.
Failure modes
The client tries to parse JSON. A successful speech result is binary. Calling a JSON parser can fail immediately or produce unusable output. Read the response as an ArrayBuffer and convert it to a Node.js Buffer before writing it.
The output extension does not match the requested format. Saving WAV or PCM bytes with an .mp3 name confuses players and downstream processing. Keep response_format, the expected content type, and the file extension aligned.
The input exceeds the documented limit. The speech reference caps input at 4,096 characters. Split longer material into deliberate segments before submission and preserve ordering in your own application. Do not repeatedly resend the same oversized request.
A 400 response is retried unchanged. The audio overview says not to retry until the text, model ID, voice, or format has been corrected. Validate those fields and compare them with the current endpoint contract.
A 401 response is treated as transient. This points to missing or invalid authentication configuration. Stop retries, correct the deployment setting, and avoid printing its value while investigating.
A 404 response follows a configuration change. Check the base URL, route, and current model ID. The base URL verification guide provides a focused review sequence for this case.
A 429 response triggers more parallel retries. The documented response is to back off and reduce concurrency. A burst of immediate retries can prolong the problem.
A 500 or 503 response leaves a partial file. Treat these as potentially transient, remove the .part file, wait before retrying, and never publish the incomplete artifact. The example performs that cleanup in its error path.
Logs capture too much. Source text, runtime configuration, raw audio, full provider error bodies, and unfiltered SDK errors do not belong in routine events. Do not rethrow an original SDK error from a top-level command after emitting a sanitized event, because the runtime or supervisor may render that object to standard error. Use the sanitized fields from the operator workflow and inspect additional data only through an explicitly controlled diagnostic process.
FAQ
Does the speech endpoint return JSON?
No. The refetched CometAPI reference describes a successful response as an audio file. In Node.js, read the response as an ArrayBuffer, convert it to a Buffer, and write those bytes to disk or controlled object storage.
Which model should I use first?
Use tts-1 for the first request because that is the straightforward model in the CometAPI endpoint example. The current tts-1-hd page positions that model as a quality-focused alternative. Confirm catalog availability and cost before changing models.
Which output formats are documented?
The CometAPI speech contract lists MP3, Opus, AAC, FLAC, WAV, and PCM. MP3 is the default. Match the saved extension to the chosen format.
How much text can one request contain?
The refetched endpoint contract specifies a maximum of 4,096 characters. Validate length before making the request so an oversized input becomes a local error rather than a repeated remote failure.
Should every failure be retried?
No. Fix 400, 401, and 404 conditions before trying again. Use backoff for 429, 500, and 503 responses, and reduce concurrency for rate limiting.
What should I log?
Log a generated run identifier, model, voice, format, character count, bytes written, elapsed time, broad error type, and HTTP status when available. Do not log the spoken text, runtime configuration values, raw audio, an unfiltered error body, or the original SDK error object.
Do listeners need an AI-voice disclosure?
The refetched OpenAI guide says its usage policies require a clear disclosure that the TTS voice is AI-generated rather than human. Apply the relevant upstream policy for the model you select and make the disclosure visible in the listening experience.
Reader next step
Run the short canary exactly once, play the resulting MP3, and inspect the sanitized completion event. Then test one intentional error, such as an unsupported voice, to confirm that no partial file is published, the process exits unsuccessfully, and only the sanitized failure event reaches standard error. Once both paths behave correctly, choose a current speech model, add the required AI-voice disclosure, and set storage and retention rules for generated audio.
When you are ready to configure the gateway and make the first request, Start with CometAPI .