Use this file to discover all available pages before exploring further.
The official TypeScript SDK for Camb.ai provides convenient access to text-to-speech, dubbing, translation, transcription, audio separation, voice cloning, and audio generation. Every method returns a promise, the package ships as ES modules, and saveStreamToFile handles audio output in one call.
Get your API key from Camb.ai Studio and read it from process.env.CAMB_API_KEY so it never appears in source control.Every API call on CambClient returns a promise. There is no synchronous client: wrap usage in async functions and use await throughout.
import { CambClient } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});
Use dotenv to load CAMB_API_KEY from a .env file during local development.
Streaming TTS returns binary data that saveStreamToFile writes to disk. await client.textToSpeech.tts(...) resolves to a stream object the helper understands.
import { CambClient, CambApi, saveStreamToFile } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});async function main() { const stream = await client.textToSpeech.tts({ text: "Hello from Camb.ai.", language: CambApi.CreateStreamTtsRequestPayload.Language.EnUs, voice_id: 147320, speech_model: CambApi.CreateStreamTtsRequestPayload.SpeechModel.MarsFlash, output_configuration: { format: "wav" }, }); await saveStreamToFile(stream, "output.wav"); console.log("Audio saved to output.wav");}void main();
Camb.ai offers MARS models tuned for different quality and latency requirements. Pass speech_model using CambApi.CreateStreamTtsRequestPayload.SpeechModel. If you omit it, the API applies its default model.
Model
Sample Rate
Best For
mars-8.1-flash-beta
48 kHz
Faster MARS 8.1 generation; same quality improvements as mars-8.1-pro-beta
mars-8.1-pro-beta
48 kHz
Improved pronunciation, expressiveness, and prosody over mars-pro
mars-flash
22.05 kHz
Low-latency real-time applications and conversational AI
mars-pro
48 kHz
High-fidelity audio production and long-form content
mars-instruct
22.05 kHz
Fine-grained tone and style control via text instructions
The tab snippets below assume the same client and CambApi imports as in Quick Start.
MARS Flash
MARS Pro
MARS Instruct
const stream = await client.textToSpeech.tts({ text: "Hey, I can respond much faster.", language: CambApi.CreateStreamTtsRequestPayload.Language.EnUs, voice_id: 147320, speech_model: CambApi.CreateStreamTtsRequestPayload.SpeechModel.MarsFlash, output_configuration: { format: "wav" },});
Best for: Voice agents, real-time applicationsSample rate: 22.05 kHz
listVoices() returns every voice available to your account, including library voices and custom clones. Use the returned id (or equivalent field from the typed response) as voice_id in TTS calls.
import { CambClient } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});async function main() { const voices = await client.voiceCloning.listVoices(); for (const voice of voices) { console.log(voice); }}void main();
Upload a short reference clip with a display name and gender. Set enhance_audio when the recording may contain noise so the API can preprocess the clip before cloning.
import { createReadStream } from "node:fs";import { CambClient, CambApi } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});async function main() { const result = await client.voiceCloning.createCustomVoice({ voice_name: "My Custom Voice", gender: 1 as CambApi.Gender, file: createReadStream("reference.wav"), description: "Warm and conversational.", enhance_audio: true, }); console.log(result);}void main();
TTS requests use string locale values through CambApi.CreateStreamTtsRequestPayload.Language (for example Language.EnUs maps to "en-us"). See Language Support for the full per-model locale list. Dubbing, translation, transcription, and several other endpoints use the numeric CambApi.Languages enum so the wire format matches the REST API exactly.
import { CambApi } from "@camb-ai/sdk";console.log(CambApi.CreateStreamTtsRequestPayload.Language.EnUs);console.log(CambApi.Languages.EN_US, CambApi.Languages.HI_IN, CambApi.Languages.FR_FR);
Fetch the canonical list of supported languages from the API when you need to populate UI or validate user input.
import { CambClient } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});async function main() { const source = await client.languages.getSourceLanguages(); const target = await client.languages.getTargetLanguages(); console.log(source, target);}void main();
Languages supported per model are listed at MARS Models.
The dubbing client translates the audio track of a remote video and resynthesizes speech with cloned voices. In TypeScript the entry point is endToEndDubbing, not the Python name create_dub. You submit a job, poll getEndToEndDubbingStatus until status is success, then read URLs from getDubbedRunInfo.
import { CambClient, CambApi } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));async function main() { const submitted = await client.dub.endToEndDubbing({ video_url: "https://example.com/video.mp4", source_language: CambApi.Languages.EN_US, target_language: CambApi.Languages.HI_IN, }); const taskId = submitted.task_id; if (!taskId) { throw new Error("No task_id returned from endToEndDubbing"); } let runId: number | null | undefined; while (true) { const status = await client.dub.getEndToEndDubbingStatus({ task_id: taskId }); if (status.status === CambApi.TaskStatus.Success) { runId = status.run_id ?? undefined; break; } await sleep(5000); } if (runId == null) { throw new Error("No run_id after success"); } const info = await client.dub.getDubbedRunInfo({ run_id: runId }); console.log(info);}void main();
To dub into several targets in one job, pass target_languages instead of target_language.
createTranslation accepts an array of strings and returns translations in the same order. The workflow matches dubbing: create a task, poll getTranslationTaskStatus, then call getTranslationResult with the run_id from the status payload.
import { CambClient, CambApi } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));async function main() { const submitted = await client.translation.createTranslation({ texts: ["Hello, how are you?", "Welcome to Camb.ai."], source_language: CambApi.Languages.EN_US, target_language: CambApi.Languages.FR_FR, }); const taskId = submitted.task_id; if (!taskId) { throw new Error("No task_id returned from createTranslation"); } let runId: number | null | undefined; while (true) { const status = await client.translation.getTranslationTaskStatus({ task_id: taskId }); if (status.status === CambApi.TaskStatus.Success) { runId = status.run_id ?? undefined; break; } await sleep(2000); } if (runId == null) { throw new Error("No run_id after success"); } const result = await client.translation.getTranslationResult({ run_id: runId }); for (const text of result.texts ?? []) { console.log(text); }}void main();
For streaming translation over a single string, see client.translation.translationStream in the generated client types.
Send either media_url or a local media_file. Poll until the task succeeds, then request structured text (optionally with word-level timestamps) from getTranscriptionResult.
import { createReadStream } from "node:fs";import { CambClient, CambApi } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));async function main() { const submitted = await client.transcription.createTranscription({ language: CambApi.Languages.EN_US, media_url: "https://example.com/audio.mp3", }); const taskId = submitted.task_id; if (!taskId) { throw new Error("No task_id returned from createTranscription"); } let runId: number | null | undefined; while (true) { const status = await client.transcription.getTranscriptionTaskStatus({ task_id: taskId }); if (status.status === CambApi.TaskStatus.Success) { runId = status.run_id ?? undefined; break; } await sleep(3000); } if (runId == null) { throw new Error("No run_id after success"); } const result = await client.transcription.getTranscriptionResult({ run_id: runId, word_level_timestamps: true, }); console.log(result);}void main();
To transcribe a local file, pass a stream or buffer-compatible upload as media_file instead of media_url.
Audio separation splits a mixed recording into stems. Upload media_file, poll getAudioSeparationStatus, then read stem metadata from getAudioSeparationRunInfo.
import { createReadStream } from "node:fs";import { CambClient, CambApi } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));async function main() { const submitted = await client.audioSeparation.createAudioSeparation({ media_file: createReadStream("track.mp3"), }); const taskId = submitted.task_id; if (!taskId) { throw new Error("No task_id returned from createAudioSeparation"); } let runId: number | null | undefined; while (true) { const status = await client.audioSeparation.getAudioSeparationStatus({ task_id: taskId }); if (status.status === CambApi.TaskStatus.Success) { runId = status.run_id ?? undefined; break; } await sleep(3000); } if (runId == null) { throw new Error("No run_id after success"); } const info = await client.audioSeparation.getAudioSeparationRunInfo({ run_id: runId }); console.log(info);}void main();
Text-to-voice synthesizes a new voice timbre from a written description and sample line. The pipeline is asynchronous: create the job, poll status, then inspect getTextToVoiceResult for preview URLs and identifiers you can feed back into TTS.
import { CambClient, CambApi } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));async function main() { const submitted = await client.textToVoice.createTextToVoice({ text: "A confident narrator introducing a documentary.", voice_description: "Deep, measured baritone with a slight gravel. Calm and authoritative.", }); const taskId = submitted.task_id; if (!taskId) { throw new Error("No task_id returned from createTextToVoice"); } let runId: number | null | undefined; while (true) { const status = await client.textToVoice.getTextToVoiceStatus({ task_id: taskId }); if (status.status === CambApi.TaskStatus.Success) { runId = status.run_id ?? undefined; break; } await sleep(3000); } if (runId == null) { throw new Error("No run_id after success"); } const result = await client.textToVoice.getTextToVoiceResult({ run_id: runId }); console.log(result);}void main();
Inspect the getTextToVoiceResult payload for preview URLs and the voice_id you should pass into textToSpeech.tts for production speech.
Text-to-audio generates sound effects or ambient beds from a prompt. The create call returns a task_id. After polling succeeds, getTextToAudioResult returns streaming audio that saveStreamToFile can persist.
import { CambClient, CambApi, saveStreamToFile } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));async function main() { const submitted = await client.textToAudio.createTextToAudio({ prompt: "Heavy rain on a tin roof at night with distant thunder.", duration: 15, audio_type: CambApi.TextToAudioType.Sound, }); const taskId = submitted.task_id; if (!taskId) { throw new Error("No task_id returned from createTextToAudio"); } let runId: number | null | undefined; while (true) { const status = await client.textToAudio.getTextToAudioStatus({ task_id: taskId }); if (status.status === CambApi.TaskStatus.Success) { runId = status.run_id ?? undefined; break; } await sleep(3000); } if (runId == null) { throw new Error("No run_id after success"); } const stream = await client.textToAudio.getTextToAudioResult({ run_id: runId }); await saveStreamToFile(stream, "soundscape.mp3"); console.log("Saved to soundscape.mp3");}void main();
The story endpoint ingests a text or Word document, builds narration, and returns run metadata when processing completes. Pass source_language as CambApi.Languages and stream the document as file.
import { createReadStream } from "node:fs";import { CambClient, CambApi } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));async function main() { const submitted = await client.story.createStory({ file: createReadStream("story.pdf"), source_language: CambApi.Languages.EN_US, title: "My Story", }); const taskId = submitted.task_id; if (!taskId) { throw new Error("No task_id returned from createStory"); } let runId: number | null | undefined; while (true) { const status = await client.story.getStoryStatus({ task_id: taskId }); if (status.status === CambApi.TaskStatus.Success) { runId = status.run_id ?? undefined; break; } await sleep(5000); } if (runId == null) { throw new Error("No run_id after success"); } const info = await client.story.getStoryRunInfo({ run_id: runId }); console.log(info);}void main();
Translated TTS translates text and renders speech in the target language in one pipeline. The TypeScript client exposes createTranslatedTts and getTranslatedTtsTaskStatus. When the status reports success, inspect the status object and the API reference for URLs or identifiers your integration should download.
import { CambClient, CambApi } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));async function main() { const submitted = await client.translatedTts.createTranslatedTts({ text: "Good morning, welcome to our service.", voice_id: 147320, source_language: CambApi.Languages.EN_US, target_language: CambApi.Languages.HI_IN, }); const taskId = submitted.task_id; if (!taskId) { throw new Error("No task_id returned from createTranslatedTts"); } while (true) { const status = await client.translatedTts.getTranslatedTtsTaskStatus({ task_id: taskId }); if (status.status === CambApi.TaskStatus.Success) { console.log(status); break; } await sleep(3000); }}void main();
Dictionaries store glossary rows that dubbing and translation jobs can consume automatically. Upload bulk CSV files or manage individual terms through the JSON endpoints.
import { CambClient } from "@camb-ai/sdk";const client = new CambClient({ apiKey: process.env.CAMB_API_KEY,});async function main() { const dictionaries = await client.dictionaries.getDictionaries(); for (const d of dictionaries) { console.log(d.id, d.name); }}void main();
List or create folders with client.folders.listFolders({}) and client.folders.createFolder({ folder_name: "..." }) when you need to organize runs in Studio.
Failed HTTP responses throw CambApiError. Validation failures surface as CambApi.UnprocessableEntityError, which extends CambApiError and carries the parsed body.
Self-hosted MARS on Baseten reuses textToSpeech.tts, but you must construct CambClient with ttsProvider: "baseten" and supply providerParams. Baseten also requires base64 reference_audio and a reference_language entry inside additional_body_parameters on each TTS request. See Custom Cloud Providers for deployment details.
Baseten validates reference_audio and reference_language. You can supply only ttsProvider and providerParams if you never call the default Camb.ai API, or include apiKey when you use both hosted and Baseten routes in one process.