Skip to content

Rate limits

Requests are metered with distributed token buckets. Several apply at once, and the strictest one wins.

BucketApplies toQuota
clientEvery data-plane request, per credential120/min, adjustable per credential
companyEvery data-plane request, across all credentials of the company600/min
transcriptTranscript reads only, on top of the two above60/min
oauth-clientPOST /oauth/token, per client id10/min
oauth-ipPOST /oauth/token, per source IP30/min

A company may hold up to 20 active credentials, and together they never exceed the company quota. Call GET /v1/context to read the credential quota actually in effect for yours.

Every response carries the current state. RateLimit-Policy lists every bucket the request consumed; RateLimit reports the single one closest to exhaustion:

RateLimit-Policy: "client";q=120;w=60, "company";q=600;w=60
RateLimit: "client";r=118;t=1

q is the quota, w the window in seconds, r what is left and t the seconds until another token. RateLimit names whichever bucket is holding the fewest tokens: normally your credential’s, the transcript one on transcript reads, and the company one once sibling credentials are spending it. Pace against the ceilings in RateLimit-Policy — the bucket named in one reading is not the only one binding you.

r counts tokens in the bucket, whose capacity is the burst rather than the quota, so it does not start at q and count down. Treat it as room available right now.

A rejected request returns 429 RATE_LIMIT_EXCEEDED with Retry-After in seconds. Wait that long — do not retry immediately, and add jitter so a fleet of workers does not resynchronise into the next window.

Read paths fail open: if the limiter is briefly unreachable, business reads keep serving rather than going dark.

Token issuance, transcript reads and internal control-plane routes fail closed with 503 SERVICE_UNAVAILABLE. These are the paths where serving unmetered traffic is worse than serving none.

A coarse per-IP limit runs at the WAF, before traffic reaches the service, and it is not part of the quotas above: roughly 2000 requests per five minutes per source IP, and 100 per five minutes on /oauth/token.

It fails differently, which matters when you meet it:

  • it blocks with 403, not 429, so it reads as an authorization failure;
  • there is no error code, no Retry-After and no rate limit headers, because the request never reaches the service;
  • it counts by source IP, so every integration behind the same egress address shares it, whatever credentials they use.

A 403 with none of our error body, especially on /oauth/token, is this limit rather than a missing scope.

  • Page with limit=100 instead of many small pages — one request returns up to 100 records at the same cost as one returning 10.
  • Filter by period rather than walking the whole history repeatedly.
  • Cache the access token; every /oauth/token call consumes its own bucket.
  • Fetch transcripts only for records you will actually process. has_transcript lets you filter the list first.