Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Apply production-ready ElevenLabs SDK patterns for TypeScript and Python. Use when implementing ElevenLabs integrations, refactoring SDK usage, or establishing team coding standards for audio AI applications. Trigger with "elevenlabs SDK patterns", "elevenlabs best practices", "elevenlabs code patterns", "idiomatic elevenlabs", "elevenlabs typescript".
.claude/skills/jeremylongshore-elevenlabs-sdk-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 46% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 23% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 38% | 0% |
| case-08 | ✗→✓ | ▲ Improved | -29% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 31% | 0% |
Production-ready patterns for the ElevenLabs TypeScript and Python SDKs. Covers singleton clients, type-safe TTS wrappers, error classification, retry with a concurrency queue, and multi-tenant client factories. Adopt them incrementally — the singleton client alone fixes the most common mistakes; add error classification and the queue as throughput grows.
The full, copy-ready code for all six patterns lives in references/implementation.md. This file gives the high-level workflow plus the essential skeleton so you can follow it end to end, then drill into the reference for depth.
@elevenlabs/elevenlabs-js installed (TypeScript) or elevenlabs (Python)ELEVENLABS_API_KEY exported in the environment (never hardcode the key)Apply the patterns in order — each builds on the previous one:
ElevenLabsClient guarded by anELEVENLABS_API_KEY check so misconfiguration fails fast at startup. Expose a resetClient() for tests. This is the skeleton every other pattern imports:
typescript 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, timeoutInSeconds: 60, }); } return instance; }
textToSpeech.convert behind a typed TTSOptions interfaceand named VoicePreset records (narration / conversational / dramatic / neutral) so voice settings are compile-time checked and consistent across the codebase.
ElevenLabsServiceError carrying a stablecode (auth_failed, quota_exceeded, rate_limited, concurrent_limit, voice_not_found, invalid_request, server_error, network_error) and a retryable flag driven by HTTP status.
p-queue sized to your plan'sconcurrent-request limit, retrying only retryable errors with exponential backoff + jitter.
Map so eachcustomer's API key stays isolated.
AsyncElevenLabsClientfor non-blocking Python backends.
See references/implementation.md for the complete code for every step above.
Applying these patterns produces a small set of focused SDK modules in the target project:
src/elevenlabs/client.ts — singleton client with config + resetClient()src/elevenlabs/tts-service.ts — typed generateSpeech() / generateToFile() with voice presetssrc/elevenlabs/errors.ts — ElevenLabsServiceError + classifyError()src/elevenlabs/queue.ts — queuedRequest() with backoff and plan-aware concurrencysrc/elevenlabs/multi-tenant.ts — per-tenant client factory (SaaS only)elevenlabs_service.py — async singleton + streaming generator (Python backends)TTS calls return an audio stream you pipe to a file or HTTP response; mp3_44100_128 is the default output format.
| Pattern | Error Type | Benefit | |---------|-----------|---------| | classifyError() | All API errors | Maps HTTP status to actionable codes | | queuedRequest() | 429, 5xx | Auto-retry with exponential backoff + jitter | | Singleton guard | Missing env var | Fails fast at startup, not at first call |
Only retryable codes (rate_limited, concurrent_limit, server_error, network_error) are retried; auth_failed, quota_exceeded, voice_not_found, and invalid_request throw immediately so callers surface a real problem instead of looping.
Generate speech to a file (TypeScript):
typescriptimport { generateToFile } from "./elevenlabs/tts-service"; await generateToFile( { voiceId: "21m00Tcm4TlvDq8ikWAM", text: "Welcome aboard.", preset: "narration" }, "welcome.mp3" );
Wrap a call in the retry queue:
typescriptimport { queuedRequest } from "./elevenlabs/queue"; import { generateSpeech } from "./elevenlabs/tts-service"; const audio = await queuedRequest(() => generateSpeech({ voiceId: "21m00Tcm4TlvDq8ikWAM", text: "High-throughput job." }) );
Full runnable examples — including the Python async path and multi-tenant usage — are in references/implementation.md.
Apply these patterns in elevenlabs-core-workflow-a for TTS generation, or see elevenlabs-rate-limits for advanced throttling and plan-aware concurrency tuning.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 36,419 | 33,458 | -8% | 1 | 1 | 0% | 6,194 | 9,047 | +46% | 0 | 0 | — |
case-02 | fail→fail | 35,468 | 28,028 | -21% | 1 | 1 | 0% | 6,046 | 6,125 | +1% | 0 | 0 | — |
case-03 | fail→fail | 21,100 | 24,283 | +15% | 1 | 1 | 0% | 3,358 | 6,456 | +92% | 0 | 0 | — |
case-04 | fail→pass | 13,454 | 18,267 | +36% | 1 | 1 | 0% | 2,627 | 3,229 | +23% | 0 | 0 | — |
case-05 | pass→pass | 26,698 | 18,147 | -32% | 1 | 1 | 0% | 4,258 | 5,309 | +25% | 0 | 0 | — |
case-06 | pass→pass | 19,701 | 20,233 | +3% | 1 | 1 | 0% | 4,077 | 5,989 | +47% | 0 | 0 | — |
case-07 | fail→pass | 20,567 | 15,832 | -23% | 1 | 1 | 0% | 3,471 | 4,773 | +38% | 0 | 0 | — |
case-08 | fail→pass | 31,084 | 14,268 | -54% | 1 | 1 | 0% | 5,206 | 3,693 | -29% | 0 | 0 | — |
case-09 | pass→pass | 12,999 | 3,449 | -73% | 1 | 1 | 0% | 1,910 | 2,001 | +5% | 0 | 0 | — |
case-10 | fail→pass | 20,044 | 20,856 | +4% | 1 | 1 | 0% | 3,662 | 4,790 | +31% | 0 | 0 | — |
case-11 | fail→fail | 25,158 | 15,163 | -40% | 1 | 1 | 0% | 5,219 | 4,441 | -15% | 0 | 0 | — |
case-12 | pass→pass | 15,039 | 10,835 | -28% | 1 | 1 | 0% | 2,510 | 3,027 | +21% | 0 | 0 | — |
case-13 | fail→fail | 21,837 | 24,319 | +11% | 1 | 1 | 0% | 3,386 | 6,891 | +104% | 0 | 0 | — |
case-14 | fail→pass | 17,024 | 8,120 | -52% | 1 | 1 | 0% | 2,742 | 2,748 | +0% | 0 | 0 | — |
case-15 | pass→pass | 15,383 | 6,713 | -56% | 1 | 1 | 0% | 2,381 | 2,352 | -1% | 0 | 0 | — |
case-16 | pass→pass | 17,436 | 8,650 | -50% | 1 | 1 | 0% | 2,652 | 2,866 | +8% | 0 | 0 | — |
case-17 | pass→pass | 20,106 | 18,992 | -6% | 1 | 1 | 0% | 2,878 | 4,492 | +56% | 0 | 0 | — |
case-18 | pass→pass | 14,309 | 15,144 | +6% | 1 | 1 | 0% | 2,418 | 3,897 | +61% | 0 | 0 | — |
case-19 | fail→pass | 11,595 | 8,244 | -29% | 1 | 1 | 0% | 2,098 | 2,596 | +24% | 0 | 0 | — |
case-20 | fail→pass | 15,543 | 19,250 | +24% | 1 | 1 | 0% | 3,339 | 5,234 | +57% | 0 | 0 | — |
case-21 | pass→pass | 19,367 | 20,092 | +4% | 1 | 1 | 0% | 3,018 | 5,558 | +84% | 0 | 0 | — |
case-22 | pass→pass | 12,755 | 18,806 | +47% | 1 | 1 | 0% | 2,637 | 5,332 | +102% | 0 | 0 | — |
case-23 | pass→pass | 17,107 | 15,516 | -9% | 1 | 1 | 0% | 2,746 | 4,564 | +66% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +35 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.