Get started
Errors and limits
What each status code means, the shape of error responses, bulk results, rate limits and when to retry.
HoundSat answers with standard HTTP status codes. A 2xx succeeded; a 4xx needs a change on your side, and its message says what to change; a 5xx is ours and is safe to retry later.
Status codes
| Status | Meaning |
|---|---|
200, 201, 202, 204 | Success. Creates answer 201; sign-out answers 204 with no body. |
400 | The request is malformed: an unknown filter or sort field, a value outside a list of allowed values, an invalid page[…] value. |
401 | No valid token, on the routes that require one. See Token lifetime. |
403 | Your role can't do this, for example Staff changing a device, or creating a record in another organization. |
404 | The record doesn't exist, or it is outside your organization. |
409 | stale_record: the device changed while you were acting on it. Read it again and retry. |
413 | The request body is too large. |
415 | Wrong Content-Type: JSON:API endpoints need application/vnd.api+json. |
422 | The request is well formed but can't be done: a missing or invalid value, or a change the record's state refuses (activating a terminated device). |
429 | Too many requests. Wait for the Retry-After seconds. |
5xx | Something failed on our side. Retry with backoff. |
JSON:API errors
/api/json endpoints answer an errors array. Each error has a machine-readable code, a detail written to show to a person, and a source naming the attribute (pointer) or query parameter (parameter) at fault.
{
"errors": [
{
"status": "422",
"code": "invalid_attribute",
"title": "InvalidAttribute",
"detail": "Device 8999001234000020718 is already active.",
"source": { "pointer": "/data/attributes/status" },
"meta": {}
}
],
"jsonapi": { "version": "1.0" }
}{
"errors": [
{
"status": "400",
"code": "invalid_filter",
"title": "InvalidFilter",
"detail": "Unknown filter field `nope`. Filter only on the resource's public attributes and relationships.",
"source": { "parameter": "filter" }
}
]
}Some errors add details in meta: a refused message retry, for example, says why in meta.reason.
Plain JSON errors
/api/auth, /api/me and the CSV exports answer a single object with an error code and a message. Validation errors add fields.
{
"error": "invalid_params",
"message": "Some fields are missing or invalid. Correct them and try again.",
"fields": { "password": ["length must be greater than or equal to 8"] }
}The codes are invalid_credentials (401), unauthenticated (401), invalid_token (400), invalid_params (400 or 422) and rate_limited (429).
Bulk results
Bulk endpoints answer 200 even when some ids fail, with one result per id. A failed result's code is not_found (unknown or outside your organization), forbidden (your role can't), invalid (the record's state refuses) or error, and its message explains it.
{
"results": [
{ "id": "5f0c2b7e-…", "status": "ok", "code": null, "message": null },
{ "id": "0b6d4e2a-…", "status": "error", "code": "invalid",
"message": "Device 8999001234000011428 is terminated and can't be activated again." },
{ "id": "9d3e1b70-…", "status": "error", "code": "not_found",
"message": "Device 9d3e1b70-… wasn't found in your fleet. Refresh the list." }
]
}Rate limits
Sign-in and the password endpoints are limited to 10 requests a minute per IP address, and sign-in to 5 attempts per 15 minutes per email address. Over a limit they answer 429 with a Retry-After header in seconds.
HTTP/1.1 429 Too Many Requests
retry-after: 42
content-type: application/json
{"error": "rate_limited", "message": "Too many attempts. Wait a few minutes, then try again."}Prefer bulk endpoints (500 ids per request) and page[limit]=200 over many small requests: they are faster for you and lighter for everyone.
Retrying safely
- Reads are always safe to retry.
- Device lifecycle changes are safe to repeat: activating a device that is already active or pending answers
422with a clear message instead of acting twice. - Creates aren't idempotent, but devices are unique by ICCID and IMSI: a repeated device import skips rows that already exist.
- After a
5xxor a network error, retry with exponential backoff (1s, 2s, 4s…) and give up after a few attempts.