AlgaPSA enforces a token-bucket rate limit on every external /api/v1/* call. Each request consumes a token from a bucket scoped to your API key. When the bucket is empty AlgaPSA returns 429 Too Many Requests with Retry-After guidance. Successful responses also carry the current limit and remaining tokens so you can self-throttle.
Promise.all, a batch sync — can spend the full burst before the steady-state limit kicks in.Defaults are configurable per tenant and per key — if you have a documented need for higher headroom, contact support with the call pattern you expect.
Both 2xx and 429 responses include rate-limit headers. Track these in your client to slow down before you hit the limit instead of waiting for a 429.
120.-1 means AlgaPSA could not reach Redis to count the request — the request was allowed through (see fail-open).Throttled requests return 429 Too Many Requests with a JSON envelope and an additional Retry-After header in seconds.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 4
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 2026-05-07T15:24:15.482Z
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests",
"details": {
"retry_after_ms": 3812,
"remaining": 0
}
}
}The simplest correct strategy is: read Retry-After on a 429 and wait that long, with jitter, before retrying. For sustained pressure, drop your request rate proactively when X-RateLimit-Remaining approaches zero.
async function callAlga(path: string, init?: RequestInit): Promise<Response> {
for (let attempt = 0; attempt < 5; attempt++) {
const res = await fetch(`https://algapsa.com${path}`, {
...init,
headers: { "X-API-Key": process.env.ALGA_API_KEY!, ...(init?.headers ?? {}) },
});
if (res.status !== 429) return res;
const retryAfter = Number(res.headers.get("Retry-After") ?? "1");
const jitter = Math.random() * 250;
await new Promise((r) => setTimeout(r, retryAfter * 1000 + jitter));
}
throw new Error("rate limited after 5 attempts");
}If AlgaPSA cannot reach the rate-limit store (Redis outage, network partition), the request is allowed through and X-RateLimit-Remaining is set to -1. This means AlgaPSA stays available during infrastructure incidents at the cost of brief unmetered traffic. Treat -1 as "limit unknown" in your client and keep your normal pacing.
The following routes are exempt from API rate limiting and never return 429:
/api/health, /api/healthz, /api/readyz — liveness probes./api/v1/meta/health — API health check./api/v1/mobile/auth/* — mobile auth uses a separate, narrower limiter./api/internal/ext-*.Outbound webhook deliveries from AlgaPSA to your URL run through a separate per-webhook bucket (default 100 per minute, configurable on each webhook). The API rate limit on this page only governs your calls into AlgaPSA. See the Webhooks reference for delivery semantics, retry schedule, and idempotency.