Skills Development AssemblyAI SDK Production Patterns

AssemblyAI SDK Production Patterns

v20260423
assemblyai-sdk-patterns
This guide provides production-ready architectural patterns for integrating the AssemblyAI SDK in TypeScript and Python. It details best practices including implementing Singleton clients, robust service wrappers, advanced error handling (like exponential backoff retries), and multi-tenant client management. Use this when building highly scalable and reliable speech-to-text applications.
Get Skill
368 downloads
Overview

AssemblyAI SDK Patterns

Overview

Production-ready patterns for the assemblyai npm package covering client initialization, type-safe wrappers, error handling, and multi-tenant architectures.

Prerequisites

  • assemblyai package installed (npm install assemblyai)
  • Familiarity with async/await and TypeScript generics

Instructions

Step 1: Type-Safe Singleton Client

// src/assemblyai/client.ts
import { AssemblyAI } from 'assemblyai';

let instance: AssemblyAI | null = null;

export function getAssemblyAI(): AssemblyAI {
  if (!instance) {
    const apiKey = process.env.ASSEMBLYAI_API_KEY;
    if (!apiKey) throw new Error('ASSEMBLYAI_API_KEY is required');
    instance = new AssemblyAI({ apiKey });
  }
  return instance;
}

Step 2: Transcription Service Wrapper

// src/assemblyai/transcription-service.ts
import { AssemblyAI, type Transcript, type TranscriptParams } from 'assemblyai';

export interface TranscriptionResult {
  id: string;
  text: string;
  duration: number;
  words: Array<{ text: string; start: number; end: number; confidence: number }>;
  speakers?: Array<{ speaker: string; text: string }>;
}

export class TranscriptionService {
  constructor(private client: AssemblyAI) {}

  async transcribe(
    audio: string,
    options: Partial<TranscriptParams> = {}
  ): Promise<TranscriptionResult> {
    const transcript = await this.client.transcripts.transcribe({
      audio,
      ...options,
    });

    if (transcript.status === 'error') {
      throw new Error(`Transcription failed: ${transcript.error}`);
    }

    return {
      id: transcript.id,
      text: transcript.text ?? '',
      duration: transcript.audio_duration ?? 0,
      words: (transcript.words ?? []).map(w => ({
        text: w.text,
        start: w.start,
        end: w.end,
        confidence: w.confidence,
      })),
      speakers: transcript.utterances?.map(u => ({
        speaker: u.speaker,
        text: u.text,
      })),
    };
  }

  async getTranscript(id: string): Promise<Transcript> {
    return this.client.transcripts.get(id);
  }

  async listTranscripts(params?: { limit?: number; status?: string }) {
    const page = await this.client.transcripts.list(params);
    return page.transcripts;
  }

  async deleteTranscript(id: string): Promise<void> {
    await this.client.transcripts.delete(id);
  }
}

Step 3: Error Handling Wrapper

// src/assemblyai/errors.ts

export class AssemblyAIServiceError extends Error {
  constructor(
    message: string,
    public readonly statusCode?: number,
    public readonly retryable: boolean = false,
    public readonly transcriptId?: string
  ) {
    super(message);
    this.name = 'AssemblyAIServiceError';
  }
}

export async function safeTranscribe<T>(
  operation: () => Promise<T>
): Promise<{ data: T | null; error: AssemblyAIServiceError | null }> {
  try {
    const data = await operation();
    return { data, error: null };
  } catch (err: any) {
    const statusCode = err.status ?? err.statusCode;
    const retryable = statusCode === 429 || (statusCode >= 500 && statusCode < 600);

    return {
      data: null,
      error: new AssemblyAIServiceError(
        err.message ?? 'Unknown AssemblyAI error',
        statusCode,
        retryable
      ),
    };
  }
}

// Usage
const { data, error } = await safeTranscribe(() =>
  client.transcripts.transcribe({ audio: audioUrl })
);
if (error?.retryable) {
  // Implement retry logic
}

Step 4: Retry with Exponential Backoff

export async function withRetry<T>(
  operation: () => Promise<T>,
  maxRetries = 3,
  baseDelayMs = 1000
): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await operation();
    } catch (err: any) {
      if (attempt === maxRetries) throw err;

      const status = err.status ?? err.statusCode;
      // Only retry on rate limits (429) and server errors (5xx)
      if (status && status !== 429 && (status < 500 || status >= 600)) throw err;

      const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * 500;
      console.warn(`Retry ${attempt + 1}/${maxRetries} in ${delay.toFixed(0)}ms`);
      await new Promise(r => setTimeout(r, delay));
    }
  }
  throw new Error('Unreachable');
}

// Usage
const transcript = await withRetry(() =>
  client.transcripts.transcribe({ audio: audioUrl })
);

Step 5: Multi-Tenant Client Factory

// For apps serving multiple customers with their own API keys
const clients = new Map<string, AssemblyAI>();

export function getClientForTenant(tenantId: string): AssemblyAI {
  if (!clients.has(tenantId)) {
    const apiKey = getTenantApiKey(tenantId); // from your secrets store
    clients.set(tenantId, new AssemblyAI({ apiKey }));
  }
  return clients.get(tenantId)!;
}

Python Patterns

import assemblyai as aai
from dataclasses import dataclass
from typing import Optional

aai.settings.api_key = os.environ["ASSEMBLYAI_API_KEY"]

@dataclass
class TranscriptionResult:
    id: str
    text: str
    duration: float
    status: str

def transcribe_audio(audio_url: str, speaker_labels: bool = False) -> TranscriptionResult:
    config = aai.TranscriptionConfig(speaker_labels=speaker_labels)
    transcriber = aai.Transcriber()
    transcript = transcriber.transcribe(audio_url, config=config)

    if transcript.status == aai.TranscriptStatus.error:
        raise RuntimeError(f"Transcription failed: {transcript.error}")

    return TranscriptionResult(
        id=transcript.id,
        text=transcript.text,
        duration=transcript.audio_duration,
        status=transcript.status.value,
    )

Output

  • Type-safe singleton client with environment validation
  • Transcription service wrapper with clean return types
  • Error handling wrapper that classifies retryable vs non-retryable errors
  • Exponential backoff with jitter for rate limits
  • Multi-tenant client factory pattern

Error Handling

Pattern Use Case Benefit
safeTranscribe All API calls Prevents uncaught exceptions, classifies errors
withRetry Rate-limited operations Auto-retry on 429 and 5xx
Service wrapper Domain logic Clean types, hides SDK internals
Multi-tenant factory SaaS apps Per-customer isolation

Resources

Next Steps

Apply patterns in assemblyai-core-workflow-a (async transcription) and assemblyai-core-workflow-b (real-time streaming).

Info
Category Development
Name assemblyai-sdk-patterns
Version v20260423
Size 7.24KB
Updated At 2026-04-28
Language