Source: https://dsii.uk/api

# JSON API

Updated 24 October 2026

The data behind these pages, for bots, launchers and community sites. Free to use; no key needed.

## Ground rules

- Plain `GET` requests, JSON responses (UTF-8, non-ASCII escaped), `Access-Control-Allow-Origin: *`.
- **Caching.** Live data is rebuilt at most every 5 seconds and `/api/stats` at most every 60 seconds; asking more often returns the same data. Polling every 15-30 seconds is plenty.
- **Rate limit.** About 2 requests per second per address with bursts of 60; beyond that you get `429 Too Many Requests` with `Retry-After`.
- **Public data only.** Nicknames, game names, map, mode, version and counts, and the heroes in the party vault. No IP addresses, e-mails, account ids or anything else that identifies a player beyond the nickname they chose to show in the game.
- Strings come from players: treat them as untrusted and escape them when you display them.
- Times are Unix timestamps (seconds, UTC).

## `GET /api/status`

Service health and headline counts.

```
{"server": "use1.dsii.uk", "time": 1792700000, "all_up": true,
 "services": [{"name": "Chat (peerchat)", "protocol": "TCP", "port": 6667, "up": true}, ...],
 "players": {"chat": 5, "signed_in": 2, "in_games": 4},
 "games": 2,
 "lobbies": {"portals_in_use": 2, "staging_rooms": 1},
 "relay": {"hosts": 1, "connections": 2}}
```

- `players.chat`: nicknames connected to the chat server (everyone in Internet or GameSpy mode).
- `players.signed_in`: GameSpy-mode account logins.
- `players.in_games`: the sum of the player counts the hosted games report.
- `relay`: hosts registered with the relay and joiner connections going through it.

## `GET /api/games`

Games in the server list, most players first.

```
{"time": 1792700000, "games": [
  {"name": "Friday co-op", "map": "ds2_world", "state": "playing", "mode": "4x1", "mode_name": "",
   "heroes_per_player": 1, "players": 2, "max_players": 4, "password": false,
   "version": "2.30.0.4277", "edition": "Broken World", "portal": "Agarrus", "network": "Internet"}]}
```

- `state`: `staging` (waiting in the staging room), `playing`, or `unknown`.
- `mode`: players × heroes each, e.g. `4x1`, `3x2`, `8x1` (`mode_name` "Old School Mode"), `6x2` ("Couples Mode++").
- `edition`: `Broken World` when the game reports Broken World's version 2.30.0.4277, `Dungeon Siege II` for other versions, `unknown` before the host has reported one.
- `network`: `Internet` or `GameSpy`, the mode whose portal the game was hosted from.

## `GET /api/lobbies`

Who is where.

```
{"time": 1792700000, "chat_users": ["Arden", "Valdis"],
 "portals": [{"name": "Agarrus", "network": "Internet", "groupid": 1, "players": ["Arden"], "games": 1}, ...],
 "staging_rooms": [{"players": ["Arden", "Valdis"]}],
 "other_rooms": {"rooms": 0, "players": 0}}
```

Staging rooms are listed without their names (the names encode the host's addresses). Other chat rooms are only counted.

## `GET /api/stats?range=24h|7d|30d`

Totals, peaks and history. `range` defaults to `24h`.

```
{"time": 1792700000, "range": "24h", "bucket_seconds": 900, "recording_since": 1792600000,
 "totals": {"accounts": 9, "profiles": 9, "vault_profiles": 9,
            "parties": {"ds2": 3, "broken_world": 1},
            "stats_records": {"newgame": 0, "updgame": 0},
            "games_hosted": {"total": 12, "last_24h": 3, "last_7d": 12, "last_30d": 12,
                             "by_edition": {"Dungeon Siege II": 10, "Broken World": 2},
                             "by_mode": {"4x1": 11, "8x1": 1}}},
 "peaks": {"chat": {"value": 7, "time": 1792650000}, ...},
 "series": {"chat": [[1792613700, 2], [1792614600, null], ...], "signed_in": [...], "in_games": [...],
            "games": [...], "relay_hosts": [...], "relay_conns": [...]}}
```

- `series`: one point per bucket (`bucket_seconds`: 15 minutes for 24h, 2 hours for 7d, 6 hours for 30d) with the **highest** minute sample in that bucket; `null` where nothing was recorded (server down).
- `games_hosted`: each game in the server list counted once, when it first reports its version (a server restart can count a running game twice).
- `parties`: the party counts in the accounts' vault indexes.
- `stats_records`: game-stats reports received from hosts (the game sends these only if its host stats reporting is enabled, which has not been observed so far).

## `GET /api/armory/characters`

The heroes in the online party vault (the [Armory](https://dsii.uk/armory)), highest level first. Rebuilt from the vault at most once a minute. Optional parameters: `q` (part of a character, party or player name), `class` (e.g. `Sorceress`), `edition` (`ds2` or `bw`), `min_level`, `max_level`, `sort` (`level`, `name` or `updated`), `page`, `per_page` (up to 200, default 50).

```
{"time": 1792700000, "total": 16, "page": 1, "per_page": 50, "characters": [
  {"id": "e5w5so6zxrferq6h-0", "name": "Alanna", "class": "Sorceress", "level": 39, "party": "Alanna",
   "party_id": "e5w5so6zxrferq6h", "edition": "Broken World", "world": "Dungeon Siege II",
   "difficulty": "Mercenary", "player": "Arden", "updated": 1792690000}, ...]}
```

- `id`: the character's page is `/armory/character/<id>`. Ids stay the same while the party keeps its vault slot; they cannot be turned back into account data.
- `player`: the in-game nickname of the account that uploaded the party. `updated`: when the party was last uploaded.

## `GET /api/armory/character/<id>`

Everything on a character page: `level`, `class`, `attributes` (Strength, Dexterity, Intelligence), `skills` (Melee, Ranged, Combat Magic, Nature Magic), `powers_and_skills`, `gold`, `health` and `mana` at the last save, `location`, `race`, `equipment` (slot and item: `name`, `base`, `level`, `quality` (normal, magic, rare, unique, set), `modifiers` with their `effects`, and where the game's templates give them `kind`, `armor`, `damage`, `requirements`, `effects`, `set`, `spell` and `value`), `spellbook`, `inventory` (items with a `count`), `teleporters` per difficulty, and `party` with its members, `progress` per difficulty (unlocked, completed, quests, furthest act, per-act quest counts, places discovered, monsterbook entries, with totals) and `collections` (bestiary, pets, companions), and `appearance` (the hero template, skin and hair textures, head and skin tone). `icon` and `portrait` are the game's texture names. Unknown ids answer `404` with `{"error": "not found"}`.

## `GET /api/armory/character/<id>/model`

The hero in 3D, put together the way the game does it: `parts` (each with a `mesh` URL, a glTF binary file, its texture keys per `tex` slot, the body's hidden sub-meshes in `hide`, and for weapons and shields the grip bone in `attach` and a `scale`), `textures` (an image URL, or for the skin the image `layers` to blend and the `alpha` image), `scale`, `stance` and `stance_name`, and the `animations` of that stance (`id`, `label`, `group`, glTF `url`, `loop`, `duration`), `idle` first. Files under `/armory-assets/` never change (their name is their content hash). `404` with `{"error": "not found"}` for unknown ids and `{"error": "no model"}` where the server has no 3D data.
