error object instead of data:
Error format
All API errors useapplication/json as the response media type. The error
object is inspired by RFC7807 Problem Details, but Unkey doesn’t return a
top-level application/problem+json document. The Problem Details-style fields
live inside the Unkey response envelope:
- title: short summary
- detail: human-readable explanation
- status: HTTP status code
- type: URI for documentation
- errors: optional validation details
application/problem+json. Use the same
application/json media type as successful responses so clients, SDKs, and
agents can parse every response with the same content-type rule.
Common error types
| Status | Error type | Description |
|---|---|---|
| 400 | validation-error | Request body failed validation |
| 401 | unauthorized | Missing or invalid authorization |
| 403 | forbidden | Valid authorization but insufficient permissions, only when the caller may know the resource exists |
| 404 | not-found | Resource not found, or the caller may not read it |
| 409 | conflict | Conflicts with current state |
| 429 | rate-limited | Rate limit exceeded |
| 500 | internal-server-error | Unexpected server error |
Authorization failures must not reveal resource existence
A 403 and a 404 carry different information. If an endpoint answers 404 when a resource does not exist and 403 when it exists but the caller may not read it, a caller with no permissions can enumerate which resources exist by telling the two responses apart. The rule: an endpoint returns 403 only when the principal is allowed to know the resource exists, meaning it holds a permission that covers reading the resource, but lacks the permission the operation requires. When the principal may not read the resource, every operation on it returns the same 404 the missing resource produces, with an identical error type, title, and detail. This matches GitHub’s documented behavior of returning 404 instead of 403 for private resources. For read endpoints this collapses to: a permission rejection returns the resource’s not-found error. Two consequences for handler code:- The not-found branch needs no permission check at all, since unauthorized callers receive the identical response either way.
- The masked 404 must be constructed fresh, not by wrapping the authorization error. The error middleware joins every public message in a fault chain into the response detail, and authorization rejections name the missing permissions, including concrete resource IDs, which would leak the existence the 404 is masking.
svc/api/routes/v2_ratelimit_get_override,
including a test that probes an existing and a missing resource with a
zero-permission key and asserts the responses are indistinguishable.
Status codes and domain outcomes
HTTP status codes describe whether Unkey could process the HTTP request. They don’t describe normal product decisions. Use200 when Unkey successfully processes a request, even if the domain result
is negative. For example, key verification can return 200 with data.valid: false, and rate limiting can return 200 with data.success: false.
Use 4xx when the request can’t be processed as submitted, such as invalid JSON,
failed validation, missing authentication, insufficient permissions, or a missing
resource. Use 5xx when Unkey fails to process a valid request.
Don’t use HTTP status codes as business logic for expected decisions. Model the
decision in the response data instead.
Validation errors
For validation errors, Unkey returns:- location: where the error occurred
- message: what went wrong
- fix: suggestion for resolution when available
Error recovery
Error messages are designed to be actionable. Use therequestId when reporting issues.
Error handling best practices
- Check status codes first.
- Parse the error object for details.
- Retry only on 5xx errors when appropriate.
- Log the full error response for debugging.

