Tone docs

Limits and quotas

Two budgets that fail in different ways, and need different responses.

curl "$TONE_API/v1/limits" -H "Authorization: Bearer $TONE_KEY"
{
  "data": {
    "environment": "live",
    "requests": { "limitPerMinute": 600, "remaining": 594 },
    "concurrency": {
      "agentCalls": { "inUse": 2, "limit": 10 },
      "byoCalls":   { "inUse": 0, "limit": 10 }
    }
  }
}

Live and test have separate budgets, and this answers for whichever key you asked with.

Requests per minute

A per-organization token bucket. Every keyed response carries the headers, on successes too β€” a client that only reads them on a 429 has already been throttled:

RateLimit-Policy: "org";q=600;w=60
RateLimit: "org";r=594;t=41
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 594
X-RateLimit-Reset: 1756029667

Exceeding it is 429 rate_limited with a Retry-After. Slow down and retry.

Dashboard traffic does not spend this budget, so a busy browser tab cannot throttle your integration.

Concurrent calls

Two pools that do not borrow from each other:

Pool
agentCallsCalls running a Tone agent
byoCallsCalls bridged to your own media stack

Exceeding one is 429 concurrent_call_limit_reached β€” a different failure that slowing down does not fix. You have to wait for calls to end.

πŸ”΄ A campaign's maxConcurrent does not queue above your quota. Recipients dialled over it are refused and settle as failed, which looks like bad phone numbers rather than a configuration mistake. Read GET /v1/limits and keep maxConcurrent at or below what it reports.

⚠️ Inbound and outbound currently share the agent pool. A campaign at your quota can lock out your published line.

Handling both

if (res.status === 429) {
  const { error } = await res.json();
  if (error.code === 'rate_limited') {
    await sleep(Number(res.headers.get('retry-after')) * 1000);
    return retry();                 // submitting too fast
  }
  if (error.code === 'concurrent_call_limit_reached') {
    return queueForLater();         // too many calls live β€” waiting won't help soon
  }
}

Both are 429, and treating them the same means either hammering a full concurrency pool or needlessly stalling on a rate limit. Branch on error.code.

Need more? Both are raised per organization β€” ask.

Was this page helpful?

On this page