Skip to main content

Rate Limiting

Every request is subject to a rate limit. The limits are the same for every company and depend only on the kind of request: read, write, or ingest. Each kind is tracked in its own bucket, so telemetry ingestion never competes with your interactive API calls.

Limits​

Request kindMethods / pathLimit
ReadsGET, HEAD, OPTIONS600 per minute
WritesPOST, PUT, PATCH, DELETE120 per minute
IngestPOST /api/v1/ingest600 per minute

Limits apply over a rolling one-minute window.

Who shares a budget​

RequestsCounted per
Signed in with a Bearer JWT, or sent with a service-account API keyYour company. Everyone in the company shares one budget per request kind
Ingest (POST /api/v1/ingest) and the edge-agent heartbeat (/api/v1/agent/heartbeat)Network address. Edge agents and scripts behind the same plant address share its budget
Anything else made without a credentialNetwork address

The authentication endpoints under /api/v1/auth/ and the /health check are not counted against these limits. Sign-in has its own protection instead: 10 failed attempts within 10 minutes lock the account for 15 minutes, and further attempts return 429 until the lock lifts.

Additional limits​

Two kinds of request are expensive to serve, so they also have a tighter cap of 10 requests per minute per company, on top of the read limit above:

  • Report downloads: GET /api/v1/reports/fleet-summary, /oee, /maintenance, /downtime, and /work-orders, and running a saved report with POST /api/v1/reports/templates/{template_id}/run.
  • AI narratives: GET /api/v1/machines/{machine_id}/prediction/narrative.

Over the cap these return 429 with Retry-After: 60.

Response headers​

Responses within the limit carry the current budget for their bucket:

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 597
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests remaining in the current window

When you are rate limited​

Exceeding a limit returns 429 Too Many Requests with a Retry-After header and a JSON body:

HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1754558400
{
"detail": "Rate limit exceeded. Please retry after the indicated period.",
"retry_after": 12
}
HeaderDescription
Retry-AfterSeconds to wait before the next request will be accepted
X-RateLimit-ResetUnix timestamp when the window resets

Best practices​

Respect Retry-After. On a 429, wait the indicated number of seconds before retrying rather than retrying immediately.

Back off exponentially. For automated clients, add exponential backoff with jitter on repeated 429 responses.

import time
import random

def request_with_backoff(make_request, max_retries=5):
for _ in range(max_retries):
response = make_request()
if response.status_code != 429:
return response
retry_after = int(response.headers.get("Retry-After", 5))
time.sleep(retry_after + random.uniform(0, retry_after * 0.1))
raise RuntimeError("Max retries exceeded")

Batch your telemetry. Send up to 1,000 readings in a single POST /api/v1/ingest request instead of one request per reading. See Sensor Data.

Watch X-RateLimit-Remaining. Throttle your client proactively as the remaining count approaches zero.