Text to Speech in TypeScript

Add text to speech to a TypeScript project by calling the Narakeet REST API. The example on this page uses the built-in fetch with full type annotations — no third-party package or build step required. Node.js 22.6+ runs .ts files natively, so the code works without tsc, Webpack, or any bundler.

See the Text to Speech API reference for authentication, polling, subtitle conversion, and other features common to all languages.

TypeScript Text to Speech Example

The following script sends text to the Narakeet API and saves the result as an MP3 file. The textToSpeech function is fully typed and uses AbortSignal.timeout for a 30-second request deadline.

import { writeFile } from 'node:fs/promises';

async function textToSpeech(
	apiKey: string,
	voice: string,
	text: string,
	outputPath: string
): Promise<void> {
	const response: Response = await fetch(
		`https://api.narakeet.com/text-to-speech/mp3?voice=${voice}`,
		{
			method: 'POST',
			headers: {
				'Accept': 'application/octet-stream',
				'Content-Type': 'text/plain',
				'x-api-key': apiKey,
			},
			body: text,
			signal: AbortSignal.timeout(30_000),
		},
	);

	if (!response.ok) {
		const errorBody: string = await response.text();
		throw new Error(`API error ${response.status}: ${errorBody}`);
	}

	const buffer: Buffer = Buffer.from(await response.arrayBuffer());
	await writeFile(outputPath, buffer);
}

const apiKey: string | undefined = process.env.NARAKEET_API_KEY;
if (!apiKey) {
	console.log('Please set NARAKEET_API_KEY environment variable');
	process.exit(1);
}

await textToSpeech(apiKey, 'lewis', 'Hi there from TypeScript', 'output.mp3');
console.log('File saved at: output.mp3');

Save this as tts.ts and run with node --experimental-strip-types tts.ts (Node 22.6+). No compilation step, no tsconfig.json, and no package.json required — Node strips the type annotations at runtime.

For the complete project with Docker support, see the TypeScript streaming API example on GitHub.

Running TypeScript Without a Build Step

Node.js 22.6 introduced --experimental-strip-types, which removes TypeScript annotations before execution. This means you can write .ts files with full type safety and run them directly — no tsc, ts-node, tsx, or any bundler in the chain. The flag becomes stable and unflagged in Node 23.6+.

This is ideal for quick scripts, CLI tools, and automation pipelines where adding a TypeScript build step creates unnecessary friction. The types help catch mistakes during development (your editor still type-checks), but the runtime does not need them.

TypeScript TTS with Deno and Bun

The same code runs on other TypeScript-native runtimes with minimal changes:

  • Deno — supports .ts natively. Replace node:fs/promises with Deno.writeFile and remove the Buffer.from wrapper, since Deno’s fetch returns a ReadableStream you can pipe directly.
  • Bun — runs .ts out of the box. The Node.js fetch and writeFile APIs are fully compatible, so the example above works without changes.

The API itself is runtime-agnostic — it accepts a POST with text and returns audio bytes over HTTPS.

Controlling Voice and Audio Settings

The endpoint URL and query parameters control the output:

Parameter How to set
Audio format Change the URL path: /text-to-speech/mp3, /text-to-speech/m4a, or /text-to-speech/wav (polling API required for WAV)
Voice Set ?voice=lewis — pick from 900 voices across 100 languages at Text to Speech Voices
Speed Add &voice-speed=1.2 for 20% faster, or &voice-speed=0.85 to slow down
Volume Add &voice-volume=soft or &voice-volume=loud

For pitch adjustments, sentence pauses, and multi-voice scripts, use the script header format in the request body. Full details at Configuring Audio Tasks.

TypeScript TTS Package

A common question is whether a dedicated TypeScript text to speech npm package exists. With Narakeet, the built-in fetch API handles the entire integration — POST text, receive audio bytes, write to disk. There is no package to install, no type definitions to add, and no SDK to configure. The textToSpeech function in the example above drops into any TypeScript project as a single self-contained function.

For input longer than 1 KB or uncompressed WAV output, switch to the Long Content (Polling) API.