API overview
Authentication, conventions, limits and errors of the hub's read-only REST API (v1).
A read-only JSON API over the OSRS account data your guild's RuneLite plugins send to the hub. It is
pull-only: poll /snapshot and follow the /events cursor feed. There are no webhooks.
- Base URL:
https://<your hub>/api/v1 - OpenAPI 3.1 document:
https://<your hub>/api/v1/openapi.json(public), or the copy on this site - On your hub: an interactive reference at
https://<your hub>/docs/api
The pages in this section are generated from the hub's OpenAPI document. Each one lists parameters and response schemas, and has a playground.
Using the playground
Set the hub server variable to your hub's origin (such as https://hub.example.com) and paste an
API key. Your browser then calls your hub directly: the key goes only from your browser to your hub, not
to this site. The hub allows this because every API response carries Access-Control-Allow-Origin: *.
Authentication
Send a key on every request:
GET /api/v1/me
Authorization: Bearer ohub_<prefix>_<secret>Members create keys on API keys, admins create integration keys on Admin → Integrations. See Which key?. Cookies are ignored.
These all get the same 401 unauthorized with WWW-Authenticate: Bearer: a missing, malformed, unknown,
revoked or expired key, or a key whose creator left the guild.
What a key can read is worked out on every request: its categories, its accounts, and what its creator can see right now. An integration key reads what the guild audience sees.
Conventions
- Success:
{ "data": …, "meta": { "generated_at": "…" } }. Lists addmeta.count. - Errors:
{ "error": { "code": "…", "message": "…" } }. A 400 addsdetails: [{ path, message }]. - Names: every key the hub defines is snake_case. Data passes through unchanged: skill names
(
"Attack"), equipment slots, item and account names, and an event'sdataobject (the plugin's own camelCase). - Timestamps are ISO-8601 UTC. Ids are opaque strings.
- Query parameters: lists are comma-separated (
skills=attack,defence), dates are ISO-8601 withZor an offset, booleans aretrue/false. Unknown parameters are ignored. - Evolution: v1 only changes additively. Ignore fields you don't know.
- Not found vs not readable: anything the key can't read answers
404 not_found, exactly like an unknown id. - Omitted vs not shared: on
/accounts/{id}and/snapshot, a section the key can't read is left out. A section the key may read but the plugin never sent is{ "shared": false, "updated_at": null }on/accounts/{id}, ornullon/snapshot. - Stale locations: a live location older than 2 minutes has
stale: true.
Rate limits
| Limit | Value | On excess |
|---|---|---|
| Per key, all endpoints | 120 requests per sliding minute for a personal key; 600 (or what the admin set) for an integration key | 429 rate_limited with Retry-After |
Per key, /snapshot | 1 request per second | 429 with Retry-After: 1 |
| Failed authentications per client IP | 30 per minute | every request from that IP gets 429 until the window passes |
Every authenticated response carries X-RateLimit-Limit, X-RateLimit-Remaining and
X-RateLimit-Reset (seconds until a request frees up, not a timestamp). Retry-After is always whole
seconds, and a 304 counts too.
Caching
Cache-Control: no-store everywhere, except:
/snapshot:private, no-cachewith a weakETag. Send it back asIf-None-Matchto get304./openapi.json:public, max-age=300.
Errors
| Status | code | When |
|---|---|---|
| 400 | invalid_request | a malformed or out-of-range parameter (details names it) |
| 401 | unauthorized | no valid key |
| 404 | not_found | an unknown or unreadable account, or an unknown path |
| 429 | rate_limited | over a limit; wait Retry-After seconds |
| 503 | unavailable | the hub is busy or its database is unreachable; retry after Retry-After |
| 500 | internal_error | a bug; nothing about it is revealed |
{"error":{"code":"invalid_request","message":"The request is invalid.","details":[{"path":"from","message":"must be an ISO-8601 date-time with Z or an offset"}]}}Endpoints
| Endpoint | Category | What it returns |
|---|---|---|
GET /me | any | the key, its creator and how many accounts it sees |
GET /accounts | the visible accounts, filterable by names, ids, online | |
GET /accounts/{id} | per section | one account's current state |
GET /snapshot | per field | every visible account's current state, for polling |
GET /accounts/{id}/xp, GET /xp | stats | XP series of one or several accounts |
GET /accounts/{id}/gains | stats | XP gained per skill over a period |
GET /events | events | the events cursor feed |
GET /accounts/{id}/sessions | activity | play sessions |
GET /accounts/{id}/equipment-history | equipment | every change of the worn set |
GET /accounts/{id}/wealth | inventory | carried value per day |
GET /accounts/{id}/locations, GET /locations | location_history | location trails |
GET /leaderboards/gains | stats | top 10 gainers per skill |
GET /leaderboards/loot | events | the most valuable drops of a period |
GET /openapi.json | none | the OpenAPI document |
Pick an endpoint in the sidebar for its parameters, response schema and playground.
Home Assistant
OSRS sensors and automations in Home Assistant with the OSRS Data integration.
The key and its creator GET
The key behind the request (kind, name, prefix, categories, scope, rate limit, expiry), its creator’s display name (`user`, null for a service key) and how many accounts it can see right now. Handy as a connection test.