Skip to content
Docs

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

StatusMeaning
200, 201, 202, 204Success. Creates answer 201; sign-out answers 204 with no body.
400The request is malformed: an unknown filter or sort field, a value outside a list of allowed values, an invalid page[…] value.
401No valid token, on the routes that require one. See Token lifetime.
403Your role can't do this, for example Staff changing a device, or creating a record in another organization.
404The record doesn't exist, or it is outside your organization.
409stale_record: the device changed while you were acting on it. Read it again and retry.
413The request body is too large.
415Wrong Content-Type: JSON:API endpoints need application/vnd.api+json.
422The 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).
429Too many requests. Wait for the Retry-After seconds.
5xxSomething 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.

422: refused by the record's state
{
  "errors": [
    {
      "status": "422",
      "code": "invalid_attribute",
      "title": "InvalidAttribute",
      "detail": "Device 8999001234000020718 is already active.",
      "source": { "pointer": "/data/attributes/status" },
      "meta": {}
    }
  ],
  "jsonapi": { "version": "1.0" }
}
400: unknown filter field
{
  "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.

422: invalid parameters
{
  "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.

Bulk activate200 OK
{
  "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.

Rate limited
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 422 with 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 5xx or a network error, retry with exponential backoff (1s, 2s, 4s…) and give up after a few attempts.