Skip to Content
API referenceRate limits

Rate limits

A rate limit is a cap on how many calls a program may make in a given time, so one busy script can’t slow almyty down for everyone else. This page lists those caps and how a program should react when it hits one (wait and retry). For example, a script that imports hundreds of records should pause when it gets a “too many requests” answer. If you only use the app, you can skip this page; limits for people chatting with your agents are set on the agent (see Channels).

Default limits

Every client IP address gets a general allowance of 100 requests a minute across the API. The operator sets it with RATE_LIMIT_MAX and RATE_LIMIT_TTL (the window, in seconds). Some routes have a tighter limit of their own:

RouteLimit
Sign in (POST /auth/login, POST /auth/token)10 per 5 minutes per IP, and 10 per 15 minutes per account
Register (POST /auth/register)10 per hour
Refresh a session (POST /auth/refresh)20 per 5 minutes
Forgot password (POST /auth/forgot-password)5 per hour per IP, and 3 per hour per account
Start an autonomous run (POST /agents/:id/runs)20 per minute
Invoke or stream an agent (POST /agents/:id/invoke, /stream)30 per minute
Gateway endpoints (/:org/:gateway, the unified root endpoint)60 per minute
OpenAI- and Anthropic-compatible APIs60 per minute per key
Health checks (/health*)No limit

A gateway, and a channel open to visitors, can add limits of its own on top: see below and Channels.

Rate limit headers

Responses carry the standard rate limit headers:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetWhen the window resets
Retry-AfterSeconds until the next request is allowed (only on 429)

Exceeding limits

When you exceed a limit, the API answers 429 Too Many Requests with a Retry-After header and the usual error body, whose error.code is RATE_LIMITED:

{ "success": false, "message": "ThrottlerException: Too Many Requests", "error": { "code": "RATE_LIMITED", "message": "ThrottlerException: Too Many Requests", "statusCode": 429 } }

Handling rate limits

Exponential backoff

The recommended strategy for handling rate limits:

async function requestWithRetry(url, options, maxRetries = 3) { for (let attempt = 0; attempt < maxRetries; attempt++) { const response = await fetch(url, options); if (response.status === 429) { const retryAfter = parseInt(response.headers.get("Retry-After") || "1"); const delay = retryAfter * 1000 * Math.pow(2, attempt); console.log(`Rate limited. Retrying in ${delay}ms...`); await new Promise(resolve => setTimeout(resolve, delay)); continue; } return response; } throw new Error("Max retries exceeded"); }

Python

import time import requests def request_with_retry(url, headers, max_retries=3): for attempt in range(max_retries): response = requests.get(url, headers=headers) if response.status_code == 429: retry_after = int(response.headers.get("Retry-After", 1)) delay = retry_after * (2 ** attempt) print(f"Rate limited. Retrying in {delay}s...") time.sleep(delay) continue return response raise Exception("Max retries exceeded")

Per-gateway and per-tool limits

A gateway can carry its own limits (requests per minute, hour and day), set on the gateway in the app and enforced on its protocol traffic. Individual tools on a gateway can have their own per-minute and per-hour limit as an override, set through Tool scoping:

curl -X PATCH https://api.almyty.com/gateways/{gatewayId}/tools/{gatewayToolId} \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "overrides": { "rateLimit": { "requestsPerMinute": 50 } } }'

Best practices

  1. Respect Retry-After: always wait the specified duration before retrying
  2. Implement backoff: use exponential backoff for retry logic
  3. Cache responses: avoid unnecessary repeated requests
  4. Batch operations: use bulk endpoints where available (e.g. POST /gateways/{gatewayId}/tools/bulk)
  5. Monitor headers: track X-RateLimit-Remaining to slow down before you are refused
  6. Use webhooks: for event-driven workflows, use webhooks instead of polling