HTTP API
The server runs an HTTP server on the same port as the main game port (9999 by default), on TCP.
Besides delivering resource files to connecting players, it serves two endpoints you can use
yourself.
Stats
GET /stats
Everything the operator needs to see whether the server is healthy: uptime, player count, tick times, network counters and the cost of every running resource.
The endpoint only exists while statstoken is set in settings.xml. As long as the
setting is empty, the server does not register the route at all and a request returns 404.
Authenticate with the token in the X-Stats-Token header. A missing or wrong token answers
401 Unauthorized.
curl -H "X-Stats-Token: your-token" http://127.0.0.1:9999/stats
{
"version": "2.2.1",
"uptime": 3600,
"players": 7,
"maxplayers": 32,
"tick": { "average": 0.42, "peak": 3.10 },
"enet": {
"sentPackets": 120034, "sentBytes": 41203311,
"receivedPackets": 98122, "receivedBytes": 22930011,
"droppedNoPeer": 0, "sendErrors": 0, "oversized": 0,
"outgoingQueued": 0, "incomingQueued": 0,
"peakRoundTripMs": 91, "worstPacketLossPercent": 1
},
"relay": {
"running": true, "port": 9998, "bound": 7,
"relayed": 884120, "noTarget": 0, "badToken": 0,
"badSource": 0, "malformed": 0, "sendErrors": 0
},
"resources": [
{ "name": "gamemodes/freeroam", "started": true, "peakCallMs": 1.8, "peakMemoryBytes": 4194304 }
]
}
What to watch
| Field | Meaning |
|---|---|
uptime | Seconds since the server started. |
tick.average, tick.peak | Milliseconds one server tick took. A peak in the hundreds means a resource is doing too much work in one go. |
relay.running | false means no game traffic can be forwarded at all, so players will connect but see nobody. |
relay.bound | How many of the connected players actually reached the relay. Anything below players means the relay port is not reachable for some of them. |
enet.peakRoundTripMs, enet.worstPacketLossPercent | Worst ping and worst packet loss across all players. |
resources[].peakCallMs | Longest single script call of that resource. The limit is 5000 ms, see Sandbox & Limits. |
resources[].peakMemoryBytes | Highest memory the script VM reached. The limit is 256 MiB. |
peakCallMs and peakMemoryBytes stay 0 for Squirrel resources, because only the Lua VM
measures them.
The numbers come from a snapshot the server rebuilds once a second, so polling faster than that returns the same values.
Query
GET /query
The public server information the launcher uses to fill the server list: name, episode, player count and names, the resources and addons a player will download, and the total download size.
This is the internal protocol between launcher and server, not a stable API. Its fields, its format and the way it is requested can change without notice in any release. Nothing here is promised to keep working, so if you build on it, expect to fix it after an update.
curl -H "User-Agent: HappinessMP/1" http://127.0.0.1:9999/query
The request has to carry the User-Agent header HappinessMP/1 exactly. Without it the server
does not answer the request as a query at all, which looks like a 404 in a browser.
{
"name": "My Server",
"episode": 0,
"players": 7,
"maxplayers": 32,
"version": "2.2.1",
"resources": ["core/chat", "gamemodes/freeroam"],
"addons": ["vehicles/police"],
"downloadSize": 18234455,
"playerlist": ["Niko", "Roman"]
}
Every request rebuilds the answer from scratch and walks through all resources and all connected players. If you show the player count on a website, cache the result there and refresh it every 30 to 60 seconds. Polling once per second, per visitor, puts that work on the machine that is supposed to be running your server.
This endpoint needs no token and returns player names, so treat it as public information. It is also the answer to "why does my server show 0 players in the list": if the query does not reach your server, the list has nothing to show.