Skip to content

Limite de rate ​

Ce e limitat, ce nu e și cum arată un 429.

API-ul ​

/api/v1 e limitat per cheie de API, nu per organizație și nu per IP.

Implicit300 de request-uri pe minut per cheie
Suprascris deapi_rate_limit_per_minute al planului tău, dacă planul îl declară
Neautentificat60 pe minut per IP — accesibil doar înainte să se rezolve o cheie
Cheia bucket-uluiapi_key:<id-ul cheii>

Limitarea pe cheie și nu pe IP e intenționată: mai mulți tenanți pot împărți un IP de ieșire, iar un client zgomotos nu are voie să îl limiteze pe altul. Consecința care merită știută: o organizație cu trei chei de API primește trei bucket-uri separate de 300/minut. Dacă împarți traficul între servicii, împarte și cheile.

Headere ​

Fiecare răspuns poartă:

X-RateLimit-Limit: 300
X-RateLimit-Remaining: 292

Un 429 adaugă:

Retry-After: 37
X-RateLimit-Reset: 1735689637

Retry-After e în secunde. Respectă-l, în loc să faci backoff după un calendar propriu — e exact timpul până la resetarea bucket-ului.

Body-ul de 429 ​

json
{
  "error": {
    "type": "rate_limit_error",
    "message": "Too many requests. Slow down and retry after the interval in the Retry-After header.",
    "retryable": true,
    "documentation_url": "https://docs.callagent.dev/api/rate-limits",
    "request_id": "01JQ…"
  }
}

Cum îl tratezi ​

ts
async function call(path: string, init: RequestInit, attempt = 0): Promise<Response> {
  const response = await fetch(`${base}${path}`, init);
  if (response.status !== 429 || attempt >= 5) return response;

  const retryAfter = Number(response.headers.get('Retry-After') ?? '1');
  await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
  return call(path, init, attempt + 1);
}

Două lucruri pe care le face bine, spre deosebire de o reîncercare naivă: folosește Retry-After în loc să ghicească, și limitează numărul de încercări, ca o cheie permanent limitată să producă o eroare, nu o blocare.

Urmărește X-RateLimit-Remaining și încetinește înainte să ajungi la zero. Un client care reacționează doar la 429 petrece limitat o parte din fiecare minut.

Celelalte limitatoare ​

LimitatorSe aplică laLimităCheie
apiTot ce e sub /api/v1, și POST /mcp300/min (sau ce spune planul)cheia de API
internalCanalul gateway → control planeniciuna—
webhooksCallback-uri semnate de furnizori, care sosesc (Twilio, Telnyx, Stripe)1000/minIP-ul sursă
authLogin și resetare de parolă5/mine-mail cu litere mici + IP
demo-showcaseDemo-ul public de vânzări600/minIP-ul sursă

Canalul intern e intenționat nelimitat: raportează sute de evenimente pe apel, iar limitarea lui ar arunca istoricul apelurilor, nu ar proteja ceva.

Limitatorul webhooks e pentru callback-uri care sosesc la CallAgent. Livrarea de webhook-uri către endpoint-ul tău nu e limitată — e guvernată de reîncercări și backoff.

Limite care nu sunt limite de request-uri ​

Astea opresc un apel, nu un request, și nu sunt 429-uri:

LimităUnde mușcăCe vezi
Concurențăincluded_concurrency + burst_concurrency, plus per campanie, per număr și per trunkended_reason: concurrency_limit
Plafon zilnic de cheltuialăÎnainte de apelended_reason: insufficient_credit
Plafoane de volum ale planuluiMinute și apeluri lunareended_reason: insufficient_credit
Plafoane de destinațieOrare, zilnice și de minute pe zi, per destinațieended_reason: destination_blocked
Capacitatea coziimax_waiting_calls pe o coadă, implicit 100destinația de overflow a cozii
Drepturile planuluiNumărul de asistenți și alteleHTTP 402 plan_limit_exceeded, cu limit și used

Observă 402-ul: un plafon de plan e o limită comercială, iar un 429 ar spune clientului să reîncerce ceva ce nu va reuși niciodată.

Există o tabelă de suprascrieri per tenant și nu face nimic

rate_limit_overrides are coloanele organization_id, bucket, per_minute, burst, reason și expires_at, și niciun cod nu o citește. Singura suprascriere care funcționează e api_rate_limit_per_minute al planului. Nu-ți face planuri pe limite personalizate per bucket.

Limitele furnizorilor sunt separate ​

Un 429 de la OpenAI, Deepgram sau ElevenLabs e al lor, nu al nostru. Iese la suprafață ca:

json
{ "error": { "type": "provider_error", "code": "provider_rate_limit",
             "provider": "deepgram", "retryable": true } }

cu HTTP 429 și un Retry-After: 5. Într-un apel viu se ocupă în schimb lanțul de fallback: etapa trece la următorul furnizor și emite provider.fallback cu reason: rate_limit. Configurează un fallback pentru orice furnizor pe care te bazezi.

Mai departe ​

Scrisă după cod. Ce nu este încă implementat este marcat ca atare, niciodată descris ca funcțional.