Statfyr is a lightweight Paper plugin that exposes your server's player statistics through a clean, documented REST API. Dashboards, bots, leaderboard websites, Discord integrations β build anything, in any language, against a single HTTP endpoint.
- π Full stat access β playtime, kills, deaths, blocks mined, items crafted, movement, and more
- π Leaderboards β ranked endpoints for playtime, kills, deaths, mined, and crafted
- β‘ Async loading β stats are fetched off the main thread, zero TPS impact
- ποΈ gzip compression β optional response compression for bandwidth efficiency
- π Bearer authentication β lock your API behind a secret key
- π‘οΈ Rate limiting β per-IP request throttling out of the box
- π CORS support β configurable allowed origins for browser-based clients
- π IP whitelist β restrict access to specific trusted addresses
- π HTTPS / SSL β optional TLS via Java keystore
- π Built-in API docs β browsable documentation at
/api/docs - π§© Pagination & sorting β standard
limit,page, andorderparams across all list endpoints - βοΈ Response caching β configurable TTL to reduce repeated stat lookups
| Requirement | Version |
|---|---|
| Java | 16+ |
| Paper/Spigot | 1.16.5+ |
- Download the latest
.jarfrom the Releases tab - Drop it into your server's
/plugins/folder - Start (or restart) your server
- Edit
plugins/statfyr/config.ymlto your liking - Use
/statfyr reloadto apply changes without a full restart
The API will be live at http://your-server-ip:8080/api by default.
All responses are JSON. The base URL for all endpoints is:
http://your-server:8080/api
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/health |
Server health and version info |
GET |
/api/players |
List all known players |
GET |
/api/player/{player} |
Full stats for a player (UUID or name) |
GET |
/api/player/{player}/summary |
Lightweight summarized stats |
GET |
/api/leaderboard/{type} |
Ranked leaderboard |
playtime Β· kills Β· deaths Β· mined Β· crafted
Get all online players:
GET /api/players?online_only=true&limit=25Get a player's full stats, filtered to mined and crafted categories:
GET /api/player/Steve?summary=true&categories=mined,craftedGet the top 10 playtime leaderboard:
GET /api/leaderboard/playtime?limit=10&order=descAuthenticated request:
GET /api/players
Authorization: Bearer your-secret-key{
"uuid": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"name": "Alex",
"online": false,
"playtime_ticks": 125,
"playtime_seconds": 6,
"playtime_formatted": "6s",
"combat": {
"deaths": 0,
"player_kills": 0,
"mob_kills": 0,
"damage_dealt": 0,
"damage_taken": 0
},
"movement": {
"distance_walked_cm": 0,
"distance_walked_m": 0,
"distance_sprinted_cm": 0,
"distance_sprinted_m": 0,
"distance_flown_cm": 0,
"distance_flown_m": 0,
"distance_swum_cm": 0,
"distance_swum_m": 0,
"total_distance_cm": 0,
"total_distance_m": 0,
"total_distance_km": 0,
"jumps": 0
},
"activity": {
"chests_opened": 0,
"items_crafted": 0,
"items_broken": 0,
"items_used": 0,
"items_picked_up": 0,
"items_dropped": 0,
"blocks_mined": 0
},
"metadata": {
"generated_at": "2026-05-20T11:17:16.252462500Z",
"execution_time_ms": 34,
"movement_enabled": true,
"combat_enabled": true,
"activity_enabled": true
}
}{
"stat": "playtime",
"total": 1,
"limit": 25,
"offset": 0,
"page": 1,
"entries": [
{
"rank": 1,
"uuid": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"name": "Steve",
"online": false,
"value": 125,
"formatted": "6s"
}
],
"metadata": {
"generated_at": "2026-05-20T11:18:07.625703400Z",
"execution_time_ms": 3,
"ascending": false,
"online_only": false
}
}The full config file is generated at plugins/statfyr/config.yml on first run.
http:
port: 8080
bind-address: "0.0.0.0" # Use 127.0.0.1 to restrict to localhost
security:
enable-api-key: false # Enable Bearer token auth
api-key: "" # Set your key here, or via STATFYR_API_KEY env var
enable-rate-limit: true
rate-limit-requests: 120 # Requests per window
rate-limit-window-seconds: 60
enable-cors: true
allowed-origins:
- "*" # Lock this down in production
cache:
ttl-seconds: 60
refresh-seconds: 10Environment variable overrides:
| Variable | Config Key |
|---|---|
STATFYR_API_KEY |
security.api-key |
STATFYR_KEYSTORE_PASSWORD |
https.keystore-password |
For production servers, it is strongly recommended to:
- Enable API key authentication β set
enable-api-key: trueand generate a strong random key - Restrict CORS origins β replace
"*"with your dashboard's actual domain - Enable IP whitelisting β if only known services need access, lock it to those IPs
- Enable HTTPS β generate a keystore and set
https.enabled: true
Generate a keystore for HTTPS:
keytool -genkeypair -alias statfyr \
-keyalg RSA -keysize 2048 \
-storetype JKS \
-keystore plugins/statfyr/keystore.jks \
-validity 3650Interactive API documentation is available at:
http://your-server:8080/api/docs
Disable it in production with docs.enabled: false.
| Code | Meaning |
|---|---|
200 |
Success |
400 |
Bad request (invalid parameters) |
401 |
Unauthorized (missing or invalid API key) |
403 |
Forbidden (IP not whitelisted) |
404 |
Player or resource not found |
429 |
Too many requests (rate limit exceeded) |
500 |
Internal server error |
| Command | Permission | Description |
|---|---|---|
/statfyr reload |
statfyr.reload |
Reload the configuration |
/statfyr status |
statfyr.status |
Show API status and port |
- Comming soon!!
Statfyr is released under the MIT License.
