New: the AI firewall watches every module and explains each incident in plain words. See how it works

Errors and rate limits

The error body, every HTTP status the REST API answers, the error codes and reasons, the rate limits that answer 429 and how to retry safely.

Every refusal of the management REST API is a JSON body with an HTTP status. This page explains how to read it, which limits exist and how your integration should react.

The error body

Most errors look like this:

{
  "message": "The device limit of this account is reached.",
  "code": "FORBIDDEN",
  "details": { "key": "max_devices", "limit": 43, "current": 43, "adding": 1 }
}
FieldMeaning
messageA sentence in English for people. Do not parse it: it may change.
codeA stable code for the kind of error (table below)
detailsPresent when the client can act on more: reason (a stable machine code), key (a plan limit), keys, issues

Variants:

CaseBody
A body or query that breaks a field rule400 { "message": "Validation error", "issues": [{ "path": ["name"], "message": "…" }] }
Some routes check the body themselves400 { "message": "<the problems, joined with commas>" }
Malformed JSON400 { "message": "Malformed request body" }
An unexpected server error500 { "message": "Internal server error" } (the cause is logged on our side, not shown)

Branch on the status, then on code and details.reason. Each route reference lists its reasons.

Status codes

StatusMeaningWhat to do
200, 201Done (201: created)
202Accepted, still running (a command)Read the result later
400The request is not validFix the request; do not retry as is
401No valid credential: “Invalid or expired API key.”Check the key; do not retry
403The key or its holder may not do this, the key is not allowed from this address, or a plan limit is reachedCheck the key’s permissions and the holder’s role; for details.key, the plan
404Not found, or not visible to youCheck the id and the key’s permissions
405The method is not allowed here (for example writing client attributes)
409A conflict: a duplicate, a record still in use, a state that does not allow the action, or a change by someone elseRead again, then decide
412The record changed since you read it (If-Match)Read again and reapply your change
413The body or file is too largeSend less
415Wrong Content-Type (MCP servers)Send application/json
422The content breaks a business ruleFix the content
428A required precondition header is missing (If-Match)Send it
429A rate limitWait Retry-After seconds, then retry
500An error on our sideRetry later with backoff
503Temporarily unavailable, for example your organisation is being moved: “This workspace is read-only while it is being moved. Please try again later.” (code TENANT_READ_ONLY, Retry-After: 300)Retry after Retry-After; reads keep working

Codes

codeTypical status
VALIDATION_ERROR400, 413, 422, 428
UNAUTHORIZED401
FORBIDDEN403
FEATURE_NOT_AVAILABLE403: your plan does not include the feature
TENANT_SUSPENDED, TENANT_INACTIVE403: your organisation is not active
TENANT_IP_NOT_ALLOWED403: your organisation’s IP rules refuse this address
NOT_FOUND404
CONFLICT409, 412
RATE_LIMIT_EXCEEDED429
INTERNAL_ERROR500, 503

Plan limits

A plan limit is a 403 with details.key naming the limit, details.limit its value and details.current your current count, for example max_devices when you create a device. See Usage and quotas and Plans and the IoT add-on.

Rate limits

Several limits apply to every API key call. Each answers 429 when spent.

LimitDefaultScopeMessage
Address limit120 requests a minute, up to 120 at oncePer calling IP address“Too many requests. Please try again later.”
Plan limitSet by your plan (none if the plan sets none)Per organisation, all keys and signed-in members together“Your plan’s API rate limit has been reached. Please try again shortly or upgrade your plan.”
Key limit1,000 calls per clock minute (Per-Key Rate Limit on the API key settings)Per key“API key rate limit exceeded. Please slow down.”

Some routes add their own:

RouteLimit
Commands10 per device per minute (“Too many commands to this device; try again in a minute.”, Retry-After: 60); 600 per organisation per minute (“Too many requests for your plan. Please upgrade or try again later.”, X-RateLimit-Scope: tenant-plan)
Device settings writes600 per organisation per minute
MCP servers120 read and 20 write tool calls per credential per minute

Headers on a 429

HeaderMeaning
Retry-AfterSeconds until a call will be admitted again
RateLimit-Limit, X-RateLimit-LimitThe limit per minute
RateLimit-Remaining, X-RateLimit-RemainingCalls left (0)
RateLimit-ResetSeconds until the bucket is full again

Not every 429 carries every header. If Retry-After is missing, wait 60 seconds.

The address limit refills continuously: one call comes back every half second, so a steady 2 calls a second never meets it. Spread bursts rather than sending them at once.

Retrying safely

  1. Retry only 429, 500, 502, 503 and 504, and network errors.
  2. Wait Retry-After when present; otherwise back off exponentially (1, 2, 4, 8 … seconds, up to a minute) with some random jitter.
  3. Give up after a few attempts and log the last body.
  4. Before you retry a POST that creates something (a device, a command), check whether the first call took effect: list or read first. A command POST has no idempotency key.
  5. Never retry 400, 401, 403, 404 or 422 unchanged.
async function call(url, init, tries = 5) {
  for (let i = 0; ; i++) {
    const res = await fetch(url, init);
    if (res.ok || i === tries - 1 || ![429, 500, 502, 503, 504].includes(res.status)) return res;
    const wait = Number(res.headers.get('Retry-After')) || Math.min(60, 2 ** i) + Math.random();
    await new Promise((r) => setTimeout(r, wait * 1000));
  }
}

Errors you may meet first

You seeLikely cause
401 on every callA typo in the key, an expired or revoked key, or a key of another organisation (the tenantId in the path must be the key’s)
403 “API key does not have the required permission.”The key lacks the route’s permission. Edit the key’s Module permissions.
403 “You are not permitted to perform this action”The key holds the permission, but its holder no longer does (role changed, group removed, access policy)
403 “This API key may not be used from the current IP address.”The key’s IP allowlist, or the organisation’s default list
404 for a record you can see in the appThe key’s holder sees it through a permission the key does not hold, or the id is of another organisation
429 from one server onlyThe address limit: spread calls or use fewer, larger pages