Let me state the conclusion first. Whether to use speech synthesis (TTS) "via a commercial API like ElevenLabs or by self-hosting an open model" is not a matter of quality or speed but of 'per-character price × usage volume × data sovereignty.' If you want the highest quality at small volume, a commercial API like ElevenLabs has an overwhelming edge. On the other hand, if you generate at high volume, or can't send data outside due to confidential data or voice-cloning consent management — under those conditions, self-hosting an open model like Qwen3-TTS is justified. And whichever you choose, the standard play in this fast-changing area is to avoid lock-in with a provider abstraction and keep a structure you can switch later.
This article, based on my experience running both commercial TTS (ElevenLabs / Google Chirp3) and a voice-cloning-capable open TTS in production, organizes the TTS API vs self-hosting decision from the perspective of buyers and decision-makers. This is the voice installment of the cost of adopting generative AI series; for a feature comparison of TTS, see Qwen-TTS vs ElevenLabs and other major TTS, and for the self-hosting implementation, Qwen-TTS production hosting.
1. Cost structure: per-character billing vs fixed cost
TTS's cost structure, like LLM API vs self-hosting, is "metered billing vs fixed cost." But TTS is characteristically billed by character count (or generated seconds).
| Commercial API (ElevenLabs, etc.) | Self-hosting (open TTS × GPU) | |
|---|---|---|
| Billing | Metered per character | Monthly GPU fixed cost + operations |
| Quality | Top-tier (especially naturalness, emotion) | Depends on the model (can reach practical quality) |
| Launch | Same day (usable immediately) | Need GPU and model setup |
| High-volume generation | Per-character billing gets heavy | Favorable, using the fixed cost to the hilt |
| Data residency | Sent externally | Stays in your own environment |
| On-prem support | No (cloud-premised) | Yes |
The per-character price of a commercial API is negligible at small volume, but it bites at high-volume generation (e.g. all narration for videos, large amounts of IVR audio, e-learning materials). Once usage exceeds a certain scale, a break-even appears where self-hosting's fixed cost is cheaper. Conversely, for sporadic use of a few thousand to tens of thousands of characters a month, the fixed cost of holding your own GPU is pricier.
Not sure where to start digitizing Excel/fax workflows?
Free DX assessment (30 min) — from mapping the current state to a rough cost and order of attack
2. Data sovereignty and on-prem requirements: the area a commercial API can't satisfy
What can become more decisive than cost is data sovereignty. A commercial TTS API sends the text (the script to be read) externally. What becomes a problem here is
- Highly confidential scripts — unpublished information, notices containing personal data, confidential narration.
- Regulation/compliance — restrictions on cross-border data transfer, industry regulations requiring on-prem.
- Voice-cloning consent management — when learning/cloning a person's voice, the handling of data close to biometric information that is the voice.
If these requirements are "clear," self-hosting where data doesn't leave your own environment is justified. Many commercial TTS, ElevenLabs included, excel in quality but structurally can't meet the "complete within your own environment (on-prem)" requirement. If "data can't go outside" is a business/regulatory requirement, this is the selection divide.
In the platform I worked on for a broadcaster, I realized broadcast-quality narration with commercial TTS (ElevenLabs / Google Chirp3) while placing it behind a provider abstraction to be swappable, designing it so the path can switch per requirement (governance, cost). On the other hand, in the AI video-localization platform premised on high-volume processing, I run a voice-cloning-capable open TTS myself. The practice is to use them separately by requirement: commercial for quality focus, self-host for volume/sovereignty focus.
3. Avoid lock-in: a swappable provider abstraction
TTS evolves fast, with frequent price revisions, new models, and service shutdowns. That's exactly why a design that doesn't directly tie business logic to a specific provider matters. I place TTS behind the abstraction boundary of "interface → provider → factory," making it swappable with just an environment variable.
/** TTSの共通インターフェース。業務ロジックはこの型だけに依存する(ETC:差し替え可能)。 */
interface SynthesizeRequest {
readonly text: string;
readonly voiceId: string;
readonly format: "mp3" | "wav";
}
interface SynthesizedAudio {
readonly bytes: Uint8Array;
readonly contentType: string;
/** 課金・コストの可観測性のために、必ず文字数を返す(SRP:合成と計測を同じ境界で) */
readonly characterCount: number;
}
interface TtsProvider {
readonly name: string;
synthesize(req: SynthesizeRequest): Promise<SynthesizedAudio>;
}
/** 商用API実装(例:ElevenLabs)。鍵は環境から取得し、コードに埋め込まない。 */
class ElevenLabsProvider implements TtsProvider {
readonly name = "elevenlabs";
constructor(private readonly apiKey: string) {}
async synthesize(req: SynthesizeRequest): Promise<SynthesizedAudio> {
/* ElevenLabs API を呼び出し、音声バイト列を返す */
}
}
/** 自前ホスティング実装(例:Qwen3-TTS を自社GPUのHTTPで提供)。データは外に出ない。 */
class SelfHostedProvider implements TtsProvider {
readonly name = "self-hosted";
constructor(private readonly endpoint: URL) {}
async synthesize(req: SynthesizeRequest): Promise<SynthesizedAudio> {
/* 自前の推論サーバへ POST し、音声バイト列を返す */
}
}
/** テスト用ダミー実装。実APIを叩かずに合成系を検証でき、テストでコストが発生しない。 */
class DummyProvider implements TtsProvider {
readonly name = "dummy";
async synthesize(req: SynthesizeRequest): Promise<SynthesizedAudio> {
return {
bytes: new Uint8Array(),
contentType: "audio/mpeg",
characterCount: req.text.length,
};
}
}
type ProviderKind = "elevenlabs" | "self-hosted" | "dummy";
/** 環境変数で実装を選ぶファクトリ。業務コードはこのファクトリ経由でしかTTSに触れない。 */
export function createTtsProvider(env: {
kind: ProviderKind;
elevenLabsApiKey?: string;
selfHostEndpoint?: string;
}): TtsProvider {
switch (env.kind) {
case "elevenlabs":
if (!env.elevenLabsApiKey) throw new Error("ELEVENLABS_API_KEY is required");
return new ElevenLabsProvider(env.elevenLabsApiKey);
case "self-hosted":
if (!env.selfHostEndpoint) throw new Error("TTS_SELF_HOST_ENDPOINT is required");
return new SelfHostedProvider(new URL(env.selfHostEndpoint));
case "dummy":
return new DummyProvider();
default:
// never に到達したらコンパイルエラー(分岐の取りこぼしを型で防ぐ)
return ((k: never) => { throw new Error(`unknown provider: ${k}`); })(env.kind);
}
}
This design has three effects.
- A migration of "start commercial, move to self-host when volume grows" can be done without touching business logic.
- You can test with
DummyProviderwithout hitting the real API — no TTS billing occurs on every test (testability, cost efficiency). - By always returning
characterCount, you make usage observable and can monitor cost.
In the broadcaster implementation too, I tested the synthesis path in dummy mode without hitting the real API and recorded every usage log (voice ID, character count, status) to make cost observable.
4. Cut regeneration cost: content-hash idempotent caching
What works most in TTS cost optimization is "don't synthesize the same text twice." Re-synthesizing the whole text after fixing only part of the script, or synthesizing the same boilerplate every time, wastes both per-character billing and GPU time.
The countermeasure is an idempotent cache keyed by the content hash of the input (text + voice settings).
- For the same content, reuse the already-synthesized audio (don't regenerate).
- This is the same idea as idempotency in payments — "converge the same operation to once."
For a commercial API you cut per-character billing, and for self-host you cut GPU time, structurally. That "design that doesn't call" is the biggest cost lever is common with LLM cost optimization.
5. Voice cloning is "consent/governance" more than "technology"
If you handle TTS, especially voice cloning (cloning a specific person's voice), design consent, disclosure, and governance before tech selection. Since a voice is strongly tied to personal identity,
- Whose voice, under what consent, do you use (written consent, use limitation, means of revocation).
- Do you disclose that it's AI voice (preventing impersonation/misperception).
- Do you keep generation logs and can you trace misuse (an audit trail).
These are irreparable "later." One reason to choose self-hosting is exactly the requirement to place the data that is a voice under your own governance and complete consent management and auditing yourself. Voice-cloning consent/governance design is detailed in the dedicated article.
Summary: choose by volume and data sovereignty, and keep a switchable structure
To not lose out on speech-synthesis (TTS) selection, here's what to grasp.
- The essence is "per-character price × volume × data sovereignty" — don't decide on quality or speed alone.
- Small/high-quality is ElevenLabs, high-volume/sensitive data is self-hosting — use them separately by requirement.
- An on-prem requirement can't be met by a commercial API — if data can't go outside, self-hosting is justified.
- Avoid lock-in with a provider abstraction, and cut regeneration cost with content-hash idempotent caching.
- Voice cloning is consent/governance more than technology — whose voice, with what consent, how disclosed.
Adopting voice AI, selecting TTS (commercial vs self-host), building a self-hosting platform, and designing voice-cloning consent — I take on, one-stop at production-operations quality, the design to "make voice earn in production," accounting for cost, data sovereignty, and governance.