Skip to content

Errors and limits

Errors use standard HTTP status codes. Most error bodies share this shape:

{
"code": "not_found",
"message": "Event '123e4567-e89b-12d3-a456-426614174000' not found",
"details": {}
}
  • code is a stable, machine-readable string. Use it in your code.
  • message is a human-readable explanation. It can change, so don’t parse it.
  • details is optional and holds extra information for some errors.

Always check the HTTP status code first: a few errors, such as some request validation errors, can have a different body.

Status code Meaning
200 OK / 201 Created — The request succeeded.
204 No Content — The request succeeded and there is no body (for example, deleting an event).
400 Bad Request bad_request, validation_error, invalid_fields The request is invalid: a missing or wrong field, a bad query parameter, or a value that breaks a rule. invalid_fields lists the problem fields in details.fields.
401 Unauthorized unauthorized The API key is missing, malformed or revoked. See Authentication.
404 Not Found not_found The record doesn’t exist in your organization.
409 Conflict conflict The request clashes with existing data, for example an externalId that is already used by another record.
429 Too Many Requests rate_limit_exceeded You sent too many requests. See Rate limit.
500 Internal Server Error internal_error Something went wrong on our side. Retry later; if it keeps happening, contact support with the X-Request-Id.
502 Bad Gateway proxy_error The API was briefly unreachable. Retry with a backoff.

When a request body or query parameter fails validation, the API returns 400. For errors raised while processing the request, the body looks like this:

{
"code": "validation_error",
"message": "Request validation failed",
"details": { "errors": ["…"] }
}

Each API key can make 60 requests per minute. Requests without a valid key are limited by IP address.

Every response includes:

Header Meaning
X-RateLimit-Limit Requests allowed per minute (60).
X-RateLimit-Remaining Requests left in the current minute.

When you go over the limit, the API returns 429 Too Many Requests with a Retry-After header (in seconds):

{
"code": "rate_limit_exceeded",
"message": "Too many requests. Limit is 60 per 60s."
}

Wait for the number of seconds in Retry-After before sending more requests.

async function safetyRadarFetch(url, options, attempts = 3) {
const res = await fetch(url, options);
if (res.status === 429 && attempts > 1) {
const seconds = Number(res.headers.get('Retry-After') ?? 60);
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
return safetyRadarFetch(url, options, attempts - 1);
}
return res;
}

To stay under the limit:

  • Use the bulk endpoints (POST /events/bulk, /assets/bulk, /users/bulk, /action-items/bulk) to create or update many records in one request.
  • Poll for changes with updatedAfter instead of re-reading whole lists. See Pagination and polling.
  • Use a larger limit to fetch more records per request.
Limit Value
Records per bulk request: events, assets, users 1 to 1,000
Records per bulk request: action items 1 to 100
Request body format JSON only, with Content-Type: application/json

Requests are not automatically deduplicated. If a POST times out, retrying it can create the record twice. To make retries safe, set an externalId on the records you create: a second create with the same externalId returns 409 Conflict instead of a duplicate, and the bulk endpoints update records by externalId.