BrightshoalDocsOpen console
MenuRate limits

API basics

Rate limits

How many requests a key, an organization and a chat visitor may send, what a publishable key may spend, how to read what is left, and how to back off when you hit the limit.

Request limits count requests in fixed one-minute windows. A window starts on the minute and resets all at once when it ends; it does not roll. Public chat also has hourly and daily publishable key budgets.

WhoLimitCounted per
Developer API (/api)120 requests a minuteAPI key
Developer API (/api), all keys together600 requests a minuteOrganization
Public chat (/public/v1)30 requests a minuteVisitor IP address
Public chat, polling /updates60 requests a minuteConversation and IP address
Public chat, analytics /telemetry120 requests a minuteVisitor IP address

Both developer limits apply to every /api request, so minting more keys raises the per-key budget only up to the organization's 600. A request refused for a bad key or a read-only key is refused before it is counted.

On public chat, starting a conversation, sending a message, uploading a file and ending all share the visitor's 30. Polling for updates has its own budget, so a chat that polls does not starve the visitor's sends. The widget's own routes (/public/chat) count separately, so your chat UI and the widget on the same network never spend each other's budget.

Publishable keys

A publishable key sits in your page for anyone to copy, so on top of the per-visitor limits each key has budgets that every visitor of your site shares. They bound what a copied key can spend; a real site stays far inside them.

WhatLimitCounted per
New conversations300 an hourPublishable key
New conversations30 an hourPublishable key and IP address
Assistant replies5,000 a dayPublishable key
Voice sessions started60 an hourPublishable key
Voice sessions started10 an hourPublishable key and IP address
Voice sessions running at once5Publishable key
Voice minutes300 a day (UTC)Publishable key

Going over answers 429 with Retry-After and the code key_quota_exceeded (voice_busy when all five voice sessions are running). The X-RateLimit-* headers on such a response describe the per-minute request limit, not what is left of the key's budget. A voice session holds its full max_seconds from the day's minutes while it runs and gives back what it did not use when it ends. If these counters cannot be reached, chat carries on, but a voice session is refused with 503 voice_unavailable.

Read what is left

Every counted response carries the state of its window:

HeaderMeaning
X-RateLimit-LimitThe per-key (or per-visitor) limit for this window.
X-RateLimit-RemainingRequests left in this window.
X-RateLimit-ResetWhen the window resets, as Unix seconds.
X-RateLimit-Org-Limit, -Remaining, -ResetThe same three for your organization's shared budget, on /api.

If the counter itself cannot be reached, the request is let through without these headers, so treat them as optional when you parse them.

When you go over

The request is refused with status 429 and a Retry-After header holding the whole seconds until the window resets:

Response 429
HTTP/1.1 429 Too Many Requests
Retry-After: 17
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1793180460

{ "error": "rate_limited", "message": "Rate limit exceeded. Retry after 17s." }

Wait that long and send it again. Retrying sooner is refused again and still counts.

async function apiFetch(url, init = {}, tries = 3) {
  for (let i = 0; ; i++) {
    const res = await fetch(url, {
      ...init,
      headers: { Authorization: `Bearer ${process.env.MLF_API_KEY}`, ...init.headers },
    })
    if (res.status !== 429 || i === tries - 1) return res
    const wait = Number(res.headers.get('retry-after') ?? '1')
    await new Promise((r) => setTimeout(r, wait * 1000))
  }
}

Staying under

  • Spread batch jobs out: 120 a minute is two a second per key.
  • Use webhooks for anything you would otherwise poll for.
  • Page with the largest limit the endpoint allows (see pagination), so a backfill takes fewer requests.
  • Run independent integrations on separate keys so one cannot spend another's budget, keeping the organization's 600 in mind.

Stuck on rate limits?