Responses
Envelope
Section titled “Envelope”JSON routes return a common envelope:
type ApiEnvelope<T> = { data: T; meta: { registry: string; apiVersion: string; }; nextCursor?: string;};nextCursor only appears on paginated list routes when another page exists.
Metadata
Section titled “Metadata”meta.registry identifies the public API deployment, not the canonical Registry database.
meta.apiVersion identifies the public route version.
Metadata does not prove that an individual record is complete. Use source, confidence and freshness routes for that.
Error Shape
Section titled “Error Shape”Errors use the same metadata boundary:
{ "error": { "code": "not_found", "message": "Entity not found." }, "meta": { "registry": "blackrelay-api", "apiVersion": "v1" }}Common codes:
| Code | Meaning |
|---|---|
bad_request |
Request parameters were not usable. |
not_found |
The route or record was not found. |
method_not_allowed |
The route only supports GET and HEAD. |
rate_limited |
The client exceeded a public request limit. |
internal_error |
The Worker failed while handling the request. |
not_ready |
A readiness dependency is unavailable. |
Errors are intentionally small. Do not parse the human-readable message for application logic; use the HTTP status and error.code.
Status Codes
Section titled “Status Codes”| Status | Typical Meaning |
|---|---|
200 |
Request succeeded. |
204 |
Successful OPTIONS preflight with no body. |
400 |
Query parameters were invalid or unusable. |
404 |
Route or record was not found. |
405 |
Method is not supported. |
429 |
Public request limit was exceeded. |
500 |
Unexpected Worker or storage failure. |
503 |
Readiness dependency is unavailable. |
Methods
Section titled “Methods”The public API supports:
| Method | Behaviour |
|---|---|
GET |
Return the response body. |
HEAD |
Return the same status and headers without a body. |
OPTIONS |
Return CORS preflight headers. |
State-changing methods are rejected.
Headers
Section titled “Headers”JSON responses use:
Content-Type: application/json; charset=utf-8Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, HEAD, OPTIONSAccess-Control-Allow-Headers: Content-TypeX-Content-Type-Options: nosniffMost read responses use short public cache headers. Health, readiness and errors use no-store.
The API is public read-only. CORS allows public browser clients to read the API but it does not grant write access because state-changing methods are rejected.
The API does not require credentials for public reads. Clients should not send cookies or bearer tokens to public routes.
Limits
Section titled “Limits”limit defaults to 50.
Invalid or missing limits fall back to the default.
The maximum list limit is 200.
Clients should prefer small page sizes for interactive views. Use exports when you need bulk data.
Public request limits are enforced by Cloudflare Worker Rate Limit bindings after the Worker checks its cache. Cache hits do not spend the route budget. Uncached GET and HEAD requests are grouped by route class and client IP:
| Route class | Paths | Limit |
|---|---|---|
| General API | /v1/* except the routes below |
300 requests per minute |
| Operations | /metrics, /v1/metrics, /v1/ops/* |
60 requests per minute |
| Exports | /v1/exports/* |
30 requests per minute |
When a request is limited, the API returns HTTP 429, Retry-After: 60 and error.code set to rate_limited. Clients should honour Retry-After, keep interactive page sizes small and use cached export files for bulk reads.
Cursor Rules
Section titled “Cursor Rules”Cursors are opaque. Clients should store and pass them back unchanged. Do not parse or construct cursors in client code.
Entity and current-state lists are ordered by Registry sort key and id.
Events and killmails are ordered newest first by occurrence time and id.
Date And Time Values
Section titled “Date And Time Values”Time fields are UTC timestamps. Treat them as instants, not as local player time.
When displaying time to users:
- keep the raw UTC timestamp available for copy/debug
- format local time in the UI if useful
- avoid mixing cycle labels with local timezone assumptions
Missing Values
Section titled “Missing Values”Missing fields are different from negative claims.
If a system coordinate, tribe name, killer name or source relation is absent, read that as:
not present in this export or not resolved by the current importerDo not treat it as:
the value does not existDisplay Rules
Section titled “Display Rules”For user-facing tools:
- show
displayNamewhen present - keep stable ids copyable somewhere in the interface
- show source and confidence for important claims
- avoid displaying internal prefixes when a clean id is available
- do not hide unresolved ids if they are the only sourced value