Skip to main content

Handle rate limits

Plan REST, MCP and webhook traffic, interpret Retry-After and retry accepted work without duplicating it.

6 min read

Tale limits API traffic by the key holder. All API keys belonging to the same person share that person’s budget; a key an Owner or Admin made for a member, a team, a project, or the organization has a budget of its own. Account for every integration and polling worker using that identity, rather than budgeting each key independently.

The limits below describe the current backend. An operator’s proxy or a downstream provider may impose additional limits.

The buckets

A token bucket refills continuously up to its burst capacity. A short batch can use that capacity, but sustained traffic must stay within the refill rate.

TrafficSustained rateBurstBudget owner
General /api/v1 traffic, including MCP and the model endpoints120/minute200Key holder
Run starts, model-message sends, task starts and MCP executions20/minute40Key holder
Settings changes over MCP (apply_settings)30/minute60Key holder
Project upload handoff and file binding240/minute300Key holder
Failed API-key authentication20/minute40Source IP
Webhook deliveries before token validation120/minute240Sender address
Deliveries to a verified webhook trigger20/minute40Trigger

Automation HTTP steps draw from a budget of their own, per organization: 120 calls a minute across the deployment, and 10 at once on one server. See Call an API from an automation.

Runs that are never stored draw from a budget of their own, per organization: trying an automation on simulated outputs and running its tests by hand, in the app, over MCP (run_automation, test_automation) or over REST (POST …/versions/{version}/tests). Each run, and each test of a suite, costs one; the budget refills at 120 a minute up to 240, and one server runs four of them at once.

REST execution and upload requests also consume the general budget. For example, a project file needs an upload-handoff request and a file-bind request; each counts against both the general and upload budgets. The larger upload bucket does not allow a user to bypass the general limit.

Execution includes project and non-project automation starts, putting an automation version live (POST /api/v1/automations/{name}/deployments), running a saved version's tests (POST /api/v1/automations/{name}/versions/{version}/tests), thread-message sends and explicit task starts. Task intake also consumes the execution budget when runWorkflowSlug is supplied. A starting-work request is charged once its body and headers have passed the endpoint's own checks — a 400 INVALID_BODY or INVALID_HEADER spends nothing — and before anything is looked up, so a 404 for a thread, task or automation you cannot see costs a token, as does a 409 the state answers. Some mutations, such as task comments and folder changes, have additional domain budgets shared with the app.

MCP batches have their own accounting: additional tool calls consume additional request budget. See MCP endpoint for the difference between an HTTP 429 and a refused message inside a batch. An MCP tool that executes an automation (run_automation, test_automation, deploy_automation, run_deployed, start_run and invoke_capability) also uses one execution once the key holder's role allows the call, and so does answer_run_ask, whose answer resumes a waiting run as the REST answer does. run_automation and test_automation also draw one unit for each run or test from the budget of runs that are never stored, and are answered the same way when it is spent or the server is already running as many as it can. When the execution budget is spent, the call is answered with a tool result whose code is RATE_LIMITED and whose data.retryAfterMs names the wait, not with an HTTP 429. Reads, validation and saving use no execution. A settings change (apply_settings) uses one unit of its own settings budget the same way and is answered with RATE_LIMITED once that budget is spent; reading and planning settings use none. Webhook budgets are separate from API-key traffic; both sender and trigger limits must allow a delivery.

Calls to the model endpoints under /api/v1/openai and /api/v1/anthropic count against the general budget only, one request each, streamed or not; what a call may spend is capped separately by budget rules. A person, and each API key, may also have eight of these calls running at once: a ninth answers 429 with code MODEL_API_CONCURRENCY_EXCEEDED and Retry-After: 2.

The execution bucket limits how quickly messages are accepted, not how many turns run at once. Accepted chat messages share a deployment-wide queue across organizations and keys. Each worker replica runs up to 16 turns at once, or WORKER_CONCURRENCY turns when that is higher, and starts the next one as soon as a turn settles. A successful send can therefore wait behind other clients’ work. Queue position and estimated start time are not exposed.

The 429

An HTTP limit refusal includes Retry-After in whole seconds. The JSON body provides the same wait in milliseconds. This illustrative response means wait at least two seconds:

http
HTTP/1.1 429 Too Many Requests
Retry-After: 2
Content-Type: application/json

{
  "error": "Too many requests — retry after 1500 ms",
  "code": "RATE_LIMITED",
  "requestId": "example-request-id",
  "data": {"retryAfterMs": 1500}
}

Branch on code; error is a sentence describing the wait, and requestId identifies the request for investigation. This is the REST response format. The app’s /api/app and webhook limit responses keep the machine code in error, so do not parse that text across surfaces. Tale does not expose remaining-budget counters: track your traffic and honor the server’s wait instruction.

  1. Stop the worker’s immediate retry loop.
  2. Wait at least Retry-After. If multiple workers share the identity, coordinate their pause.
  3. Retry with a bounded exponential delay and jitter when refusals continue. For example, grow a delay from one second up to sixty seconds, always honoring a longer server-provided wait.
  4. Preserve the original idempotency key for operations that support one. A timeout after a run start may mean the run was already accepted.

A spending cap answers 429 too, with code BUDGET_EXCEEDED: a budget rule that applies to the key holder — their own, a team’s, the organization’s, or the API key’s, and for work in a project, the project’s — has been reached. A short wait does not help. Retry-After names the time until the cap’s period resets, and data names the cap: scope, period, limitCode, used, limit, and resetsAt in epoch milliseconds. Nothing is queued; pause the work until resetsAt, or ask an administrator to raise the limit under Policies & Limits. On the model endpoints, both 429s come in the OpenAI or Anthropic error shape with the same code and Retry-After. A spent budget names its cap in the message there instead of in data, and carries x-should-retry: false so the vendors' SDKs do not retry it on their own.

Other 4xx responses usually need a corrected request, credential or permission. Do not treat every failure as a rate limit; use the error model.

Plan polling and retries

An ETag response of 304 still costs a request. It saves response bytes, not budget. Polling one run every five seconds consumes twelve reads per minute before any retries or other work. Leave capacity for those other calls instead of filling the entire budget with polls.

Request only the fields you need, such as ?fields=status,finishedAt on a run. Slow down when a run is waiting for a human, and stop polling terminal runs. Follow Start a run, then poll it for states and idempotent starts.

For larger imports, use supported batch operations such as POST /api/v1/contacts/bulk, and spread batches over time. Creating more keys for the same user does not increase the budget. A workflow that needs an identity and a budget of its own can use a key an Owner or Admin makes for a team, a project, or the organization; do not use key rotation as a retry strategy.