Skills Artificial Intelligence ElevenLabs SDK Production Patterns

ElevenLabs SDK Production Patterns

v20260423
elevenlabs-sdk-patterns
A comprehensive guide to production-ready patterns for ElevenLabs' TypeScript and Python SDKs. This resource covers crucial best practices including singleton client implementation, type-safe TTS service wrappers, robust error classification (rate limiting, quota), and concurrency management using request queues. Essential for building scalable and reliable voice AI applications.
Get Skill
240 downloads
Overview

ElevenLabs SDK Patterns

Overview

Production-ready patterns for the ElevenLabs TypeScript and Python SDKs. Covers singleton clients, type-safe wrappers, error handling, retry logic, and multi-tenant patterns.

Prerequisites

  • @elevenlabs/elevenlabs-js installed (TypeScript) or elevenlabs (Python)
  • Familiarity with async/await patterns
  • Understanding of error handling best practices

Instructions

Pattern 1: Singleton Client with Config

// src/elevenlabs/client.ts
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

let instance: ElevenLabsClient | null = null;

export function getClient(): ElevenLabsClient {
  if (!instance) {
    if (!process.env.ELEVENLABS_API_KEY) {
      throw new Error("ELEVENLABS_API_KEY environment variable is required");
    }
    instance = new ElevenLabsClient({
      apiKey: process.env.ELEVENLABS_API_KEY,
      maxRetries: 3,        // Auto-retry on 429/5xx (default: 2)
      timeoutInSeconds: 60, // Per-request timeout
    });
  }
  return instance;
}

// Reset for testing
export function resetClient(): void {
  instance = null;
}

Pattern 2: Type-Safe TTS Service

// src/elevenlabs/tts-service.ts
import { getClient } from "./client";
import { Readable } from "stream";
import { pipeline } from "stream/promises";
import { createWriteStream } from "fs";

export type VoicePreset = "narration" | "conversational" | "dramatic" | "neutral";

const VOICE_PRESETS: Record<VoicePreset, {
  stability: number;
  similarity_boost: number;
  style: number;
}> = {
  narration:      { stability: 0.6, similarity_boost: 0.75, style: 0.0 },
  conversational: { stability: 0.4, similarity_boost: 0.6,  style: 0.3 },
  dramatic:       { stability: 0.3, similarity_boost: 0.8,  style: 0.7 },
  neutral:        { stability: 0.8, similarity_boost: 0.5,  style: 0.0 },
};

export interface TTSOptions {
  voiceId: string;
  text: string;
  modelId?: "eleven_v3" | "eleven_multilingual_v2" | "eleven_flash_v2_5" | "eleven_turbo_v2_5";
  preset?: VoicePreset;
  outputFormat?: string;
  languageCode?: string;
}

export async function generateSpeech(options: TTSOptions): Promise<ReadableStream> {
  const client = getClient();
  const settings = VOICE_PRESETS[options.preset || "narration"];

  return client.textToSpeech.convert(options.voiceId, {
    text: options.text,
    model_id: options.modelId || "eleven_multilingual_v2",
    voice_settings: settings,
    output_format: options.outputFormat || "mp3_44100_128",
    language_code: options.languageCode,
  });
}

export async function generateToFile(options: TTSOptions, outputPath: string): Promise<void> {
  const audio = await generateSpeech(options);
  await pipeline(
    Readable.fromWeb(audio as any),
    createWriteStream(outputPath)
  );
}

Pattern 3: Error Classification

// src/elevenlabs/errors.ts
export type ElevenLabsErrorCode =
  | "auth_failed"
  | "quota_exceeded"
  | "rate_limited"
  | "concurrent_limit"
  | "voice_not_found"
  | "invalid_request"
  | "server_error"
  | "network_error";

export class ElevenLabsServiceError extends Error {
  constructor(
    message: string,
    public readonly code: ElevenLabsErrorCode,
    public readonly httpStatus: number | null,
    public readonly retryable: boolean,
    public readonly originalError?: Error
  ) {
    super(message);
    this.name = "ElevenLabsServiceError";
  }
}

export function classifyError(error: unknown): ElevenLabsServiceError {
  if (error instanceof Error) {
    const status = (error as any).statusCode || (error as any).status;

    if (status === 401) {
      const msg = (error as any).body?.detail?.message || error.message;
      if (msg?.includes("quota")) {
        return new ElevenLabsServiceError(msg, "quota_exceeded", 401, false, error);
      }
      return new ElevenLabsServiceError(msg, "auth_failed", 401, false, error);
    }

    if (status === 429) {
      const msg = (error as any).body?.detail?.message || error.message;
      const code = msg?.includes("concurrent") ? "concurrent_limit" : "rate_limited";
      return new ElevenLabsServiceError(msg, code, 429, true, error);
    }

    if (status === 404) {
      return new ElevenLabsServiceError(error.message, "voice_not_found", 404, false, error);
    }

    if (status === 400) {
      return new ElevenLabsServiceError(error.message, "invalid_request", 400, false, error);
    }

    if (status >= 500) {
      return new ElevenLabsServiceError(error.message, "server_error", status, true, error);
    }

    return new ElevenLabsServiceError(error.message, "network_error", null, true, error);
  }

  return new ElevenLabsServiceError(String(error), "network_error", null, true);
}

Pattern 4: Retry with Concurrency Queue

// src/elevenlabs/queue.ts
import PQueue from "p-queue";
import { classifyError, ElevenLabsServiceError } from "./errors";

// Concurrency limits by plan:
// Free: 2, Starter: 3, Creator: 5, Pro: 10, Scale: 15
const queue = new PQueue({
  concurrency: 5,    // Match your plan's concurrent request limit
  interval: 1000,    // Window in ms
  intervalCap: 10,   // Max requests per window
});

export async function queuedRequest<T>(
  operation: () => Promise<T>,
  maxRetries = 3
): Promise<T> {
  return queue.add(async () => {
    let lastError: ElevenLabsServiceError | undefined;

    for (let attempt = 0; attempt < maxRetries; attempt++) {
      try {
        return await operation();
      } catch (err) {
        lastError = classifyError(err);

        if (!lastError.retryable) throw lastError;

        const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
        const jitter = Math.random() * 500;
        await new Promise(r => setTimeout(r, delay + jitter));
      }
    }

    throw lastError;
  }) as Promise<T>;
}

Pattern 5: Multi-Tenant Client Factory

// src/elevenlabs/multi-tenant.ts
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const clients = new Map<string, ElevenLabsClient>();

export function getClientForTenant(tenantId: string, apiKey: string): ElevenLabsClient {
  if (!clients.has(tenantId)) {
    clients.set(tenantId, new ElevenLabsClient({
      apiKey,
      maxRetries: 3,
    }));
  }
  return clients.get(tenantId)!;
}

export function removeTenantClient(tenantId: string): void {
  clients.delete(tenantId);
}

Pattern 6: Python Async Pattern

# elevenlabs_service.py
import os
import asyncio
from elevenlabs.client import AsyncElevenLabsClient

_client: AsyncElevenLabsClient | None = None

def get_async_client() -> AsyncElevenLabsClient:
    global _client
    if _client is None:
        _client = AsyncElevenLabsClient(
            api_key=os.environ["ELEVENLABS_API_KEY"]
        )
    return _client

async def generate_speech(text: str, voice_id: str, output_path: str):
    client = get_async_client()
    audio = await client.text_to_speech.convert(
        voice_id=voice_id,
        text=text,
        model_id="eleven_multilingual_v2",
        voice_settings={
            "stability": 0.5,
            "similarity_boost": 0.75,
        },
    )

    with open(output_path, "wb") as f:
        async for chunk in audio:
            f.write(chunk)

# Usage
asyncio.run(generate_speech("Hello!", "21m00Tcm4TlvDq8ikWAM", "out.mp3"))

Pattern Summary

PatternUse CaseKey Benefit
Singleton clientSingle-tenant appsMemory efficiency, shared retries
Type-safe TTSAll TTS workVoice presets, compile-time checks
Error classificationProduction error handlingActionable error codes
Concurrency queueHigh-throughput appsRespects plan limits automatically
Multi-tenant factorySaaS platformsPer-customer isolation
Python asyncPython backendsNon-blocking I/O

Error Handling

PatternError TypeBenefit
classifyError()All API errorsMaps HTTP to actionable codes
queuedRequest()429, 5xxAuto-retry with exponential backoff
Singleton guardMissing env varFails fast at startup, not at first call

Resources

Next Steps

Apply patterns in elevenlabs-core-workflow-a for TTS, or see elevenlabs-rate-limits for advanced throttling.

Info
Name elevenlabs-sdk-patterns
Version v20260423
Size 8.94KB
Updated At 2026-04-28
Language