Authentication
Every /v1 endpoint needs an API key with api:access. Request access through this form. We review requests manually and email approved keys. Health checks are public; data routes are not.
| Header | Value |
|---|---|
X-API-Key | <your api key> |
GET /v1/items HTTP/1.1
Host: tboi-api.pabloviniegra.dev
X-API-Key: sk_live_...
X-Request-ID: 7c9e2f5e-1b3d-4f8a-9b6e-2c0a4f7d8e9b
Accept: application/jsonMake the first request
Start with the list endpoint. This example runs in a server route and requests 20 items.
- Store the key in your server runtime environment.
- Call Platinum Gold from that server route.
- Return only the data your browser needs.
const API_BASE = "https://tboi-api.pabloviniegra.dev";
const API_KEY = process.env.PLATINUM_GOLD_API_KEY;
export async function getItems() {
if (!API_KEY) {
throw new Error("PLATINUM_GOLD_API_KEY is not configured.");
}
const response = await fetch(
`${API_BASE}/v1/items?limit=20`,
{
headers: {
Accept: "application/json",
"X-API-Key": API_KEY,
},
},
);
if (!response.ok) {
throw new Error("Unable to load items.");
}
return response.json();
}Keep this module out of the client bundle. The browser can call your own /api/items route, but it must never receive the API key.
Add request IDs, typed response models, logging, and user-facing error handling after the first call works. The later sections cover those production concerns.
Read the response
A successful list response includes the items and its pagination context. Field names are camelCase on the wire.
{
"items": [
{
"gameId": 1,
"name": "The Sad Onion",
"imageUrl": "https://.../sad_onion.png"
}
],
"total": 732,
"limit": 20,
"offset": 0
}Continue with the full reference
Reference
Once the first request works, use these sections for the rest of the API contract.
Understand the data
An ItemResponse uses stable camelCase fields. Nullable fields are returned as null when the dataset has no value.
| Field | Type | Notes |
|---|---|---|
gameId | integer | Stable identifier across syncs |
name | string | Display name |
description | string | Flavor text or effect description |
quality | integer or null | 0 through 4; null when unspecified |
type | string or null | active, passive, or familiar |
rechargeTime | string or null | Free text such as 12 charges |
imageUrl | string | Absolute URL; load images lazily |
introducedInVersion | string or null | Game version that introduced the item |
{
"apiVersion": "0.1.0",
"datasetVersion": "rebirth-2025-08",
"gameVersion": "Repentance+",
"lastSync": "2025-08-20T10:15:00Z",
"items": 732
}Know the endpoints
Health checks do not need a key. Every /v1 route does, and all responses are JSON.
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET | /health | None | Liveness probe |
GET | /health/ready | None | PostgreSQL and Redis readiness |
GET | /v1/items | X-API-Key | Paginated, filterable item list |
GET | /v1/items/{gameId} | X-API-Key | One item by stable gameId |
GET | /v1/items/random | X-API-Key | One random item honoring filters |
GET | /v1/meta | X-API-Key | Dataset metadata and last sync |
Health checks
/health returns 200 with { "status": "ok" } when the process is up. It is safe to use as a liveness probe. /health/ready returns 200 when PostgreSQL and Redis are reachable, or 503 when the service is degraded.
Filter the item list
All list filters are optional. Combine them in the query string, and cache each complete URL separately.
| Parameter | Type | Default | Notes |
|---|---|---|---|
search | string | None | Case-insensitive item name match |
quality | integer | None | 0 through 4 |
type | string | None | active, passive, or familiar |
version | string | None | Exact introducedInVersion match |
sort | enum | name | name, quality, or game_id |
order | enum | asc | asc or desc |
limit | integer | 20 | 1 through 100 |
offset | integer | 0 | Zero or greater |
The random endpoint accepts only search, quality, type, and version. It does not accept pagination or sorting parameters.
Handle errors
Errors use one JSON shape. Show a useful recovery message in your UI and keep the raw response in your server-side error tracker.
{ "error": { "code": "INVALID_API_KEY", "message": "Invalid API key" } }| HTTP | When | Recovery |
|---|---|---|
401 | Header missing or key invalid | Check the server-side credential and header name |
403 | Key lacks api:access | Request a key with the required scope |
404 | gameId does not exist | Check the stable identifier |
422 | Query value has the wrong type or range | Validate filters before sending the request |
503 | PostgreSQL or Redis is unavailable | Retry with backoff and show service status |
Trace a request
Every response includes X-Request-ID. Send your own ID when possible; otherwise the API generates one. Forward it to your logs and support tickets.
X-Request-ID: 7c9e2f5e-1b3d-4f8a-9b6e-2c0a4f7d8e9bCache responses
Cache at your client boundary using the full query string when the route is safe to reuse. Health checks are never a cache hot path.
| Route | Server TTL | Client recommendation |
|---|---|---|
/v1/items | 15 min | Cache by the complete query string |
/v1/items/{gameId} | 24 h | Cache by stable gameId |
/v1/meta | 24 h | Cache for the session; refresh on user action |
/v1/items/random | None | Cache yourself only if you reuse the result |
/health/* | None | Do not cache |
Plan for change
/v1remains available until a new major version is announced.- New response fields are additive; do not fail on unknown fields.
- Removing or renaming a field requires a major version.
- Load item images lazily and treat
imageUrlas an absolute URL.
Find the reference
Use the interactive reference for exploratory requests. Include the X-Request-ID from a failed call when asking for help.
