Gå till innehåll

AI-ratebegränsning för röst: Samtidighet, köer och 429:or

Publicerad

LyssnaLyssna på den här artikeln

De flesta team hanterar AI-ratebegränsning för röst på samma sätt som andra API:er: begränsa antal förfrågningar per minut, försök igen om servern säger ifrån, och gå vidare. För arbetslaster på ElevenLabs fungerar inte det här när trafiken ökar, eftersom det är samtidighetsgränsen – inte antalet förfrågningar – du faktiskt slår i.

Den här guiden förklarar varför samtidighet är den verkliga begränsningen och går igenom klientmönster som hjälper dig hålla dig inom gränsen. Vi visar praktiska system du kan använda, från begränsade samtidighetspooler och smidig hantering av 429:or till rättvisa mellan flera användare och token- och leaky buckets. Varje mönster har ett fungerande TypeScript-exempel du kan anpassa.

Om du bygger röstagenter, berättarflöden eller andra produktionssystem ovanpå våra modeller och vill skala upp, är den här guiden för dig.

Sammanfattning

  • AI-ratebegränsning för röst handlar om samtidighetskontroll, inte antal förfrågningar per minut.
  • När du slår i ratebegränsningen avvisas inte trafiken direkt. Istället hamnar förfrågningarna i en prioritetskö som lägger till cirka 50 ms.
  • Om du överskrider kapaciteten även efter köning får du ett HTTP 429-fel.
  • WebSockets ökar kapaciteten rejält, eftersom bara aktiv generering räknas mot din gräns.
  • System med flera användare behöver ett extra lager för rättvisa: buckets per användare, viktad kö, reserverad marginal och uppdelning på nycklar för isolering.
  • Två svarshuvuden, current-concurrent-requests och maximum-concurrent-requests, visar hur du ligger till med AI-ratebegränsningen.

Varför gränsen är samtidighet, inte förfrågningar per minut

Samtidighet är antalet förfrågningar som pågår samtidigt. Förfrågningar per minut är genomströmning över tid. Det är viktigt att förstå skillnaden, eftersom det avgör vilken spak du ska dra i för att hålla dig inom gränsen.

När du använder någon av ElevenLabs modellerna, ökar serverns arbetsbelastning med antalet samtidiga användare. Ljudgenerering håller en plats så länge genereringen pågår, och den tiden varierar beroende på inmatning, modell och belastning.

En gräns per minut säger inget om hur många platser som är upptagna just nu – och det är det enda servern mäter.

Gränser per abonnemang och modellfamilj

Din samtidighetsbudget är inte ett enda tal. Gränserna skiljer sig mellan abonnemang och modellfamilj. Till exempel har Speech to Text en högre gräns än Text to Speech, eftersom transkriberingsförfrågningar oftast är kortare och systemet kan hantera fler samtidigt.

Multilingual v2
Free
2
Starter
3
Creator
5
Pro
10
Scale
15
Business
15
Enterprise
Elevated
Flash
Free
4
Starter
6
Creator
10
Pro
20
Scale
30
Business
30
Enterprise
Elevated
STT
Free
8
Starter
12
Creator
20
Pro
40
Scale
60
Business
60
Enterprise
Elevated
Realtime STT
Free
6
Starter
9
Creator
15
Pro
30
Scale
45
Business
45
Enterprise
Elevated
Priority
Free
3
Starter
4
Creator
5
Pro
5
Scale
5
Business
5
Enterprise
6

Gränsen gäller per modellfamilj. Om du kör Flash för agenter och Multilingual v2 för berättande, använder du två separata budgetar samtidigt. Aktuella siffror och mer om samtidighet finns på modellsidan.

Vad händer när du slår i samtidighetsgränsen?

Att slå i samtidighetsgränsen avvisar inte trafiken direkt. Systemet trappar ner via en prioritetskö och avvisar helt först när du fortfarande överskrider den totala kapaciteten.

Så länge du är under gränsen körs förfrågningar direkt. När du når gränsen hamnar nya förfrågningar i en kö sorterad efter din abonnemangsprioritet. Kötiden lägger oftast till cirka 50 ms, så en kort överbelastning märks knappt för användaren.

Om systemet fortfarande är överbelastat efter köning får du ett HTTP 429. Det är signalen att sakta ner istället för att försöka igen direkt. Prioritetsnivån i tabellen avgör hur dina köade förfrågningar hanteras jämfört med annan trafik – högre abonnemang tömmer kön snabbare.

HTTP vs. WebSocket: Hur de påverkar din gräns

Transportsättet du väljer påverkar ratebegränsningen och din budget direkt. Samma samtal kan dra olika mycket samtidighetsbudget beroende på om det körs över HTTP eller WebSocket.

Med HTTP räknas varje förfrågan mot samtidighetsgränsen under hela dess varaktighet. Med WebSocket räknas bara tiden då modellen faktiskt genererar ljud. En öppen men inaktiv WebSocket räknas oftast inte.

För en röstagent har ett samtal ofta långa pauser där ingen pratar och modellen inte genererar något. Med HTTP håller du en plats hela tiden, men med WebSocket används platsen bara under de millisekunder ljud genereras – så en plats kan delas mellan många samtal.

Se guiden för realtids-TTS via WebSocket för protokolldetaljer. För interaktiv trafik är WebSockets oftast rätt val.

Varför ~5 samtidigheter kan hantera ~100 sändningar

Matematiken bakom samtidighet är inte självklar förrän du räknar med uppspelningstiden. Generering går mycket snabbare än uppspelning, och en plats är bara upptagen när ljud genereras. Det är just det som gör att en liten budget kan räcka till många lyssnare.

En förfrågan som tar en bråkdel av en sekund att generera ger flera sekunders ljud som lyssnaren spelar upp – och under uppspelningen är platsen fri för andra.

En tumregel är att en samtidighetsgräns på 5 kan hantera ungefär 100 samtidiga ljudsändningar. Exakt antal beror på röst, taltempo och hur mycket tystnad som finns mellan yttranden.

Huvuden som visar hur du ligger till

Du behöver inte gissa hur nära gränsen du är. Varje svar innehåller två siffror du kan använda för att mäta marginalen istället för att bara uppskatta.

Håll koll på dessa två huvuden:

  • aktuella samtidiga förfrågningar: hur många förfrågningar pågår just nu?
  • maximalt antal samtidiga förfrågningar: din gräns för den modellfamiljen.

Tillsammans ger dessa huvuden en realtids- översikt över din aktuella användning och tillgänglig kapacitet. Du ska inte behöva gissa innan du stöter på AI-ratebegränsningar.

Klientstrategier för AI-ratebegränsning

Det finns fyra grundläggande verktyg som täcker nästan alla AI-ratebegränsningsfall:

  • En token bucket: Om det finns tokens tillgängliga släpps förfrågningar igenom. Kapaciteten fylls på över tid, så den klarar korta toppar utan att slå i gränsen.
  • En leaky bucket: Försöker jämna ut inkommande trafik till en fast utmatningshastighet, vilket förhindrar att plötsliga toppar överbelastar dina system.
  • En begränsad samtidighetspool: Begränsar hur många förfrågningar som kan vara aktiva samtidigt, så du aldrig överskrider samtidighetsgränsen.
  • Exponentiell backoff med full jitter: Lägger till allt längre väntetid mellan misslyckade förfrågningar för att undvika att alla klienter försöker igen samtidigt.

Nedan visar vi hur du bygger upp dem en i taget, med början på det som matchar samtidighetsgränsen bäst.

Alla kodexempel nedan utgår från en enda klient, initierad en gång:

import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const elevenlabs = new ElevenLabsClient({ apiKey: process.env.ELEVENLABS_API_KEY });

Begränsad samtidighet: det verktyg som matchar gränsen

Eftersom servern mäter samtidighet är det mest direkta klientkontrollen en begränsad arbetarpool som sätter tak på hur många förfrågningar du har igång samtidigt. Sätt taket lite under din abonnemangsgräns för att lämna utrymme för prioritetskön och för jitter.

async function pool<T, R>(
  items: T[],
  maxInFlight: number,
  worker: (item: T) => Promise<R>,
): Promise<R[]> {
  const results: R[] = new Array(items.length);
  let next = 0;

  async function run(): Promise<void> {
    while (next < items.length) {
      const i = next++;
      results[i] = await worker(items[i]); // never more than maxInFlight of these run at once
    }
  }

  await Promise.all(
    Array.from({ length: Math.min(maxInFlight, items.length) }, run),
  );
  return results;
}

async function synthesize(text: string): Promise<Buffer> {
  const stream = await elevenlabs.textToSpeech.stream("JBFqnCBsd6RMkjVDRZzb", {
    text,
    modelId: "eleven_flash_v2_5",
    outputFormat: "mp3_44100_128",
  });
  const chunks: Buffer[] = [];
  for await (const chunk of stream) chunks.push(Buffer.from(chunk));
  return Buffer.concat(chunks);
}

// Plan Flash limit is, say, 10. Stay under it.
const texts = Array.from({ length: 50 }, (_, i) => `Sentence number ${i}.`);
const audio = await pool(texts, 8, synthesize); // never more than 8 in flight

Token bucket: tillåt toppar, begränsa snittet

En token bucket rymmer upp till capacity tokens och fylls på med refillRate tokens per sekund. Varje förfrågan förbrukar en token, så bucketen tillåter korta toppar upp till sin storlek men håller nere snitthastigheten.

Det är rätt verktyg för att jämna ut när en kö av jobb plötsligt dyker upp, så du inte skickar allt på en gång och får en topp i samtidighet.

class TokenBucket {
  private tokens: number;
  private updated = performance.now();

  constructor(private capacity: number, private refillPerSec: number) {
    this.tokens = capacity;
  }

  private refill(): void {
    const now = performance.now();
    const elapsed = (now - this.updated) / 1000;
    this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillPerSec);
    this.updated = now;
  }

  tryAcquire(cost = 1): boolean {
    this.refill();
    if (this.tokens >= cost) {
      this.tokens -= cost;
      return true;
    }
    return false;
  }

  timeUntil(cost = 1): number {
    this.refill();
    return this.tokens >= cost ? 0 : ((cost - this.tokens) / this.refillPerSec) * 1000;
  }
}

Leaky bucket: håll en jämn takt

Ibland vill du inte tillåta toppar alls. En leaky bucket släpper igenom jobb i en fast, jämn takt oavsett hur ojämn inmatningen är. Det är bättre när systemet hellre vill ha jämn belastning än tillfälliga toppar.

Till exempel när du medvetet håller dig långt under en liten samtidighetsbudget som delas med andra tjänster.

class LeakyBucket {
  private next = performance.now();
  constructor(private intervalMs: number) {} // admit at most one item per intervalMs

  async acquire(): Promise<void> {
    const now = performance.now();
    const wait = Math.max(0, this.next - now);
    this.next = Math.max(now, this.next) + this.intervalMs;
    if (wait > 0) await new Promise((r) => setTimeout(r, wait));
  }
}

Exponentiell backoff med full jitter

När en förfrågan misslyckas med en återförsöksbar status gör det bara saken värre att försöka igen direkt. Backoff sprider ut försöken, och full jitter slumpmässigt fördelar väntetiden så att inte alla klienter försöker samtidigt och skapar samma topp igen.

Kodexemplet nedan använder RetryableError, en liten klass som innehåller misslyckad status och eventuell Retry-After. Den definieras i avsnittet om smidig hantering av 429:or nedan.

async function withBackoff<T>(
  call: () => Promise<T>,
  opts: { maxAttempts?: number; baseMs?: number; capMs?: number } = {},
): Promise<T> {
  const { maxAttempts = 5, baseMs = 500, capMs = 20_000 } = opts;
  let attempt = 0;
  for (;;) {
    try {
      return await call();
    } catch (e) {
      if (!(e instanceof RetryableError) || ++attempt >= maxAttempts) throw e;
      // honor Retry-After if present; otherwise capped exponential growth with full jitter
      const delay =
        e.retryAfterMs ?? Math.random() * Math.min(capMs, baseMs * 2 ** attempt);
      await new Promise((r) => setTimeout(r, delay));
    }
  }
}

Smidig hantering av 429: Vad gör du när du slår i taket?

En 429 betyder att du var över kapacitet även efter prioritetskön, så rätt svar är att sakta ner – inte försöka hårdare. Det finns fyra sätt att hantera det. Att göra det bra handlar om fyra strategier:

  • Upptäckt
  • Följa Retry-After
  • Visa tillbaka-tryck
  • Undvika retry-stormar med circuit breaker

Vi går igenom dessa mer i detalj.

Först: upptäckt. Behandla HTTP 429 (och tillfälliga 500, 502, 503 och 504) som återförsöksbara, och 400, 401, 403 och 422 som icke-återförsöksbara; att försöka igen med en felaktig eller obehörig förfrågan lyckas aldrig och slösar bara bort en plats.

För det andra: följ Retry-After. Om svaret har den headern, följ den exakt istället för att räkna ut en egen fördröjning. Servern vet bäst när det finns kapacitet igen. Använd bara jitter-backoff om headern saknas.

class RetryableError extends Error {
  constructor(public status: number, public retryAfterMs?: number) {
    super(`retryable ${status}`);
  }
}

function classify(resp: Response): void {
  if ([429, 500, 502, 503, 504].includes(resp.status)) {
    const ra = resp.headers.get("retry-after");
    throw new RetryableError(resp.status, ra ? Number(ra) * 1000 : undefined);
  }
  if (!resp.ok) throw new Error(`non-retryable ${resp.status}`);
}

För det tredje: visa tillbaka-tryck. Låt inte återförsök staplas osynligt. Om din kö eller marginal visar att du inte kan hantera en ny förfrågan snart, avvisa den direkt med tydlig signal istället för att ta emot jobb du inte kan göra.

För det fjärde: undvik retry-stormar med circuit breaker. Om misslyckanden passerar en gräns, öppna circuiten och avvisa snabbt under en paus istället för att skicka förfrågningar du vet kommer misslyckas. Efter pausen, skicka några testförfrågningar; om de lyckas, stäng circuiten.

class CircuitBreaker {
  private failures = 0;
  private openedAt: number | null = null;
  constructor(private threshold = 5, private cooldownMs = 10_000) {}

  allow(): boolean {
    if (this.openedAt === null) return true;
    if (performance.now() - this.openedAt >= this.cooldownMs) {
      this.openedAt = null; // half-open: allow a probe
      this.failures = 0;
      return true;
    }
    return false;
  }

  record(ok: boolean): void {
    if (ok) {
      this.failures = 0;
      this.openedAt = null;
    } else if (++this.failures >= this.threshold) {
      this.openedAt = performance.now();
    }
  }
}

Mönster för multi-tenant-kvoter vid AI-ratebegränsning

Allt hittills utgår från en enda applikation mot en enda budget. När du bygger en SaaS ovanpå ElevenLabs förändras problemet: din samtidighetsbudget delas mellan alla dina kunder, och en användare som kör ett batchjobb ska inte kunna blockera andras live-trafik. Du behöver ett rättviselager mellan dina användare och den gemensamma gränsen.

Grunden är token buckets per användare. Ge varje användare en egen bucket i storlek efter deras rättighet, och släpp bara igenom en förfrågan när både användarens bucket och en global limiter tillåter det.

class MultiTenantAdmission {
  private tenantBuckets = new Map<string, TokenBucket>();
  constructor(private globalMaxInFlight: number) {}

  private bucket(tenant: string): TokenBucket {
    let b = this.tenantBuckets.get(tenant);
    if (!b) {
      // Each tenant: burst of 5, sustained 2 starts/sec. Tune per tier.
      b = new TokenBucket(5, 2);
      this.tenantBuckets.set(tenant, b);
    }
    return b;
  }

  async run<R>(tenant: string, work: () => Promise<R>): Promise<R> {
    const b = this.bucket(tenant);
    if (!b.tryAcquire()) {
      throw new RetryableError(429, b.timeUntil());
    }
    // ... then admit through the global limiter (e.g. the bounded pool above)
    return work();
  }
}

Buckets håller varje användare ärlig, men avgör inte vem som får plats när flera tävlar om den globala gränsen. Där använder du viktad rättvis kö.

Undvik först-till-kvarn, som låter en användare ta alla platser vid en topp. Ha en kö per användare och skicka vidare i proportion till deras vikt, så en betalande användare får större andel än en gratisanvändare.

Utöver rättvisa, reservera marginal. Låt aldrig normal trafik använda 100 % av samtidighetsgränsen. Håll tillbaka t.ex. 15–20 % som buffert för interaktiva förfrågningar och prioritetskön.

När rättvisa inom en budget inte räcker, dela upp på arbetsytor eller nycklar. En enda samtidighetsbudget blir till slut en flaskhals oavsett hur rättvist du delar.

Då separerar du arbetslaster på olika arbetsytor eller API-nycklar med egna budgetar: till exempel en nyckel för realtidsagenter och en annan för bakgrundsberättande, så att en kö för berättande inte påverkar agentkapaciteten.

Arbetsytor låter dig också sätta begränsningar, kreditkvoter och kontroller per nyckel, beskrivs i dokumentationen om autentisering.

Så övervakar du din samtidighetsanvändning

Inget av detta går att justera utan mätning – du kan inte hantera marginal du inte mäter. Logga current-concurrent-requests och maximum-concurrent-requests på varje svar, taggat per modellfamilj, och visa nyttjandegraden som en mätare.

function recordHeadroom(resp: Response, metrics: Metrics): void {
  const cur = Number(resp.headers.get("current-concurrent-requests"));
  const max = Number(resp.headers.get("maximum-concurrent-requests"));
  if (Number.isFinite(cur) && Number.isFinite(max)) {
    metrics.gauge("el.concurrency.current", cur);
    metrics.gauge("el.concurrency.max", max);
    if (max > 0) metrics.gauge("el.concurrency.utilization", cur / max);
  }
}

Fyra signaler att följa:

  • Nyttjandegrad (current / maximum).
  • Andel 429 av totala förfrågningar.
  • Retry-djup, antal försök per logisk förfrågan.
  • Tid till första ljud, mätt från din applikation – inte från modellens inferens. Se förstå latens för vad TTFA inkluderar.

Ett friskt system håller nyttjandet klart under max och ser 429:or bara vid tillfälliga toppar. Att övervaka dessa signaler ger dig koll på ratebegränsning långt innan det blir ett problem.

När du behöver skala bortom klientbaserad ratebegränsning

Klientmönster kan göra mycket, men till slut växer den faktiska efterfrågan förbi dem. Då är det dags att göra förändringar som hjälper både kostnad och arbetsinsats.

Varje steg nedan ger dig extra kapacitet.

Börja med att byta från HTTP till WebSockets för interaktiv trafik. Om dina agenter eller live-fall körs över HTTP, ändra till WebSocket så att bara aktiv generering räknas. För samtal ger det ofta mycket högre kapacitet utan att byta abonnemang, eftersom inaktiv tid inte tar platser.

Om dina toppar är spikiga men snittet ryms inom budgeten, jämnar en token- eller leaky bucket plus en begränsad pool ut topparna till snittet.

Välj sedan rätt modell. Snabbare generering håller varje plats upptagen kortare tid, vilket ökar antalet sändningar en fast gräns klarar. Eleven Flash v2.5 är lägst latens för realtidsjobb; kombinera med en Omedelbar röstkloning eller standardröst för att slippa extra overhead från Professional Voice Clones.

Först därefter ska du uppgradera abonnemanget. När din faktiska efterfrågan överskrider budgeten trots att klienten är väloptimerad, höjer ett högre abonnemang både samtidighetsgränsen och din kö-prioritet. Jämför nivåer på API-prissidan.

Om du behöver högre gränser än de som finns publicerade erbjuder Enterprise-abonnemang högre och anpassade samtidighetsgränser samt högsta kö-prioritet. Fler kontroller finns för vissa användningsfall, som IP-whitelistning (i Enterprise-preview) och zero-retention-lägen. Kontakta din kundansvarige för att höja gränser.

Sammanfattning: tänk på detta vid AI-ratebegränsning

Det vanligaste misstaget är att se AI-ratebegränsning för röst som räkning av förfrågningar. Allt här handlar om samtidighetskontroll. Det som avgör om du lyckas är hur många förfrågningar som genererar ljud samtidigt och hur länge varje plats är upptagen.

Bygg klienten utifrån det.

Begränsa pågående förfrågningar med en pool, styr insläpp med token- eller leaky bucket, återförsök med begränsad exponentiell backoff och full jitter, följ Retry-After och bryt circuiten innan en retry-storm uppstår.

För multi-tenant-system: lägg till buckets per användare, viktad rättvisa, reserverad marginal och uppdelning för isolering. Håll koll på current-concurrent-requests och maximum-concurrent-requests och larma på trenden, inte på felen.

När du verkligen behöver mer kapacitet, gå igenom listan i ordning: WebSockets och bättre klientbeteende först, sedan rätt modell, sedan abonnemangsuppgradering och sist Enterprise-gränser.

Bygg röstapplikationer med ElevenAPI

AI-ratebegränsning i produktion börjar med rätt transport, rätt modell och svarshuvuden som visar exakt hur du ligger till.

ElevenAPI erbjuder modeller med låg latens som Eleven Flash v2.5, realtidsstreaming via WebSocket, Speech to Text och Text to Speech API:er, samt samtidighetshuvuden i varje svar så att du kan bygga röstagenter som skalar inom dina gränser.

Tillsammans med AI-ratebegränsningsstrategierna i den här artikeln levererar du snabba röstupplevelser med förutsägbar prestanda – även vid hög belastning.

Utforska ElevenAPI för att se hela modellutbudet i praktiken, eller skapa ett konto och börja bygga med ElevenLabs redan idag.

Vanliga frågor om AI-ratebegränsning

Liknande artiklar

Skapa med AI-ljud av högsta kvalitet