Status codes
HTTP status codes returned by the Thought Industries REST API and how to handle them.
Status codes
The REST API uses standard HTTP status codes to indicate the outcome of each request. The /v2 endpoints emit a compact set of codes; error response bodies vary by endpoint and are not wrapped in a common envelope.
Codes the API emits
| Code | Meaning | When returned |
|---|---|---|
200 OK | Request succeeded | All successful responses, including creates and deletes — no 201 or 204 is emitted by /v2 endpoints. |
400 Bad Request | Malformed request body or invalid parameters | Missing required fields, wrong types, unsupported content type. |
401 Unauthorized | Missing or invalid API key | See Authentication. Response body is empty. |
404 Not Found | Resource does not exist | A small number of endpoints (for example, redemption codes) return this when a specific record is missing. |
422 Unprocessable Entity | Request was valid but the operation could not be completed | Business-rule violations reported by the underlying GraphQL layer (for example, an invalid status transition). |
429 Too Many Requests | Rate limit exceeded | See Rate limits. |
500 Internal Server Error | Unexpected server failure | Retry after a brief delay; contact support if persistent. |
/v2 endpoints do not return 201, 204, 403, 405, or 409. Treat 2xx as success and any 4xx/5xx as failure.
Error response bodies
Error bodies are not standardized across endpoints. Expect any of the following shapes depending on the failure:
Plain string body
Many controllers set this.body to a short human-readable string alongside a 4xx status.
Invalid ID or SKURate-limit response (429)
The throttle middleware returns a JSON object with an errors array.
Response: 429 Too Many Requests
{
"errors": [
"Rate limit exceeded, retry in 27 seconds"
]
}The response also sets X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After headers.
GraphQL passthrough (422)
When a mutation succeeds at the HTTP layer but fails a business rule, the endpoint returns the raw GraphQL result. Inspect errors[] inside it for details.
{
"errors": [
{ "message": "Cannot transition user from disabled to disabled", "path": ["UpdateUserStatus"] }
]
}Empty body (401)
Authentication failures return 401 with no body — the failure mode is inferred from the status code alone.
Handling errors in code
Because bodies vary, defensively parse the response before reading fields:
const response = await fetch(url, { headers });
if (!response.ok) {
const text = await response.text();
let payload;
try {
payload = JSON.parse(text);
} catch {
payload = text;
}
console.error(`[${response.status}]`, payload);
if (response.status === 429) {
const retryAfter = response.headers.get("Retry-After");
// schedule a retry after `retryAfter` seconds
}
}response = requests.get(url, headers=headers)
if not response.ok:
try:
payload = response.json()
except ValueError:
payload = response.text
print(f"[{response.status_code}]", payload)
if response.status_code == 429:
retry_after = response.headers.get("Retry-After")
# sleep(int(retry_after)) before retrying$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode >= 400) {
$payload = json_decode($response, true);
if ($payload === null) {
$payload = $response;
}
error_log("[{$httpCode}] " . print_r($payload, true));
}Related
- Authentication — resolve 401 errors
- Rate limits — resolve 429 errors
- Pagination — avoid unnecessary requests