PlayerLogger API Documentation
Complete reference for the PlayerLogger REST API. Track player statistics, server data, and more.
Overview
The PlayerLogger API provides programmatic access to player statistics and server data from Hytale servers running the PlayerLogger plugin. The API follows RESTful conventions and returns JSON responses.
Real-time player stats, server aggregates, leaderboards, and Discord bot integration support.
Quick Start
Fetch stats for any registered server with a simple GET request:
curl https://api.hytaletravelers.com/?server=hytaletravelers.com
Authentication
The PlayerLogger API uses IP-based authentication for write operations. No API keys are required for read operations.
| Operation | Authentication | Description |
|---|---|---|
| Read (GET) | None | Publicly accessible for fetching server stats |
| Write (POST) | IP-based | Server identified by its public IP address |
Server aliases (names) are bound to the first IP that claims them. Attempting to use another server's name will return a 403 error.
Base URL
All API requests should be made to:
https://api.hytaletravelers.com
CORS Support
The API supports Cross-Origin Resource Sharing (CORS) for all origins, enabling browser-based applications:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type
Rate Limits
To ensure fair usage and server stability:
| Use Case | Recommended Interval |
|---|---|
| Real-time dashboard | 5 seconds minimum |
| Discord bots | 30 seconds minimum |
| General polling | 30 seconds recommended |
Get Server Stats
Retrieve complete statistics for a specific server including all player data and aggregate stats.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
server |
string | Yes* | Server name or IP address |
ip |
string | Yes* | Alternative: Server IP address only |
* One of server or ip is required
Example Request
GET https://api.hytaletravelers.com/?server=hytaletravelers.com
Response 200 OK
{
"stats": {
"totalPlayers": 42,
"onlinePlayers": 5,
"totalPlaytimeSeconds": 360000,
"totalDamageDealt": 15420.5,
"totalPlayerKills": 128,
"totalMobKills": 4521,
"totalDeaths": 89,
"totalPvpDeaths": 34,
"totalPveDeaths": 55,
"totalBlocksPlaced": 15420,
"totalBlocksBroken": 22150
},
"players": [
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"username": "PlayerName",
"playtimeSeconds": 7200,
"playtimeFormatted": "2h 0m 0s",
"online": true,
"firstJoined": 1704067200000,
"firstJoinedFormatted": "Jan 1, 2024",
"lastSeen": 1706500000000,
"lastSeenFormatted": "Jan 29, 2024",
"damageDealt": 1250.5,
"playerKills": 3,
"mobKills": 127,
"deathCount": 5,
"pvpDeaths": 2,
"pveDeaths": 3,
"lastDeathCause": "Killed by Zombie",
"blocksPlaced": 500,
"blocksBroken": 832
}
],
"serverIP": "192.168.1.100",
"serverName": "hytaletravelers.com",
"publicListing": true,
"lastUpdated": 1706500000000
}
List All Servers
Retrieve a list of all registered servers. Optionally filter to only show publicly listed servers.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
public |
string | No | Set to "true" to only return publicly listed servers |
Example Request
GET https://api.hytaletravelers.com/servers?public=true
Response 200 OK
{
"servers": [
"hytaletravelers.com",
"play.anotherserver.net",
"192.168.1.50"
]
}
Upload Stats
Upload player statistics from a game server. Used by the PlayerLogger plugin to sync data.
This endpoint is designed for the PlayerLogger plugin. Server identity is determined by the request's source IP.
Request Body
{
"serverName": "connect.myserver.com",
"publicListing": true,
"lastUpdated": 1706500000000,
"players": [
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"username": "PlayerName",
"playtimeSeconds": 7200,
"playtimeFormatted": "2h 0m 0s",
"online": true,
"firstJoined": 1704067200000,
"firstJoinedFormatted": "Jan 1, 2024",
"lastSeen": 1706500000000,
"lastSeenFormatted": "Jan 29, 2024",
"damageDealt": 1250.5,
"playerKills": 3,
"mobKills": 127,
"deathCount": 5,
"pvpDeaths": 2,
"pveDeaths": 3,
"lastDeathCause": "Killed by Zombie",
"blocksPlaced": 500,
"blocksBroken": 832
}
],
"stats": {
"totalPlayers": 42,
"onlinePlayers": 5,
"totalPlaytimeSeconds": 360000,
"totalDamageDealt": 15420.5,
"totalPlayerKills": 128,
"totalMobKills": 4521,
"totalDeaths": 89,
"totalPvpDeaths": 34,
"totalPveDeaths": 55,
"totalBlocksPlaced": 15420,
"totalBlocksBroken": 22150
}
}
Response 200 OK
{
"success": true,
"message": "Data stored for connect.myserver.com"
}
Fetch Own Data
Allows the plugin to retrieve its own stored data on startup for anti-tampering verification.
Response 200 OK
{
"players": [...],
"stats": {...},
"lastUpdated": 1706500000000,
"serverName": "connect.myserver.com",
"serverIP": "192.168.1.100"
}
Version Check
Returns the latest plugin version from CurseForge. Used by the plugin to check for updates.
Response 200 OK
{
"version": "1.0.0"
}
Local Plugin API
The PlayerLogger plugin includes a built-in webserver that exposes local endpoints. Enable this in your plugin config with webEnabled: true and configure the port with webPort.
These endpoints allow you to build custom dashboards or integrations that run directly on your server without using the public API.
http://localhost:{webPort}
GET /api/players
Returns a list of all players sorted by playtime (descending). Filters out invalid "ghost" entries.
Response 200 OK
[
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"username": "PlayerName",
"playtimeSeconds": 7200,
"playtimeFormatted": "2h 0m 0s",
"online": true,
"firstJoined": 1704067200000,
"firstJoinedFormatted": "Jan 1, 2024",
"lastSeen": 1706500000000,
"lastSeenFormatted": "Jan 29, 2024",
"damageDealt": 1250.5,
"playerKills": 3,
"mobKills": 127,
"deathCount": 5,
"pvpDeaths": 2,
"pveDeaths": 3,
"lastDeathCause": "Killed by Zombie",
"blocksPlaced": 500,
"blocksBroken": 832
}
]
GET /api/stats
Returns aggregated server-wide statistics.
Response 200 OK
{
"totalPlayers": 42,
"onlinePlayers": 5,
"totalPlaytimeSeconds": 360000,
"totalDamageDealt": 15420.5,
"totalPlayerKills": 128,
"totalMobKills": 4521,
"totalDeaths": 89,
"totalPvpDeaths": 34,
"totalPveDeaths": 55,
"totalBlocksPlaced": 15420,
"totalBlocksBroken": 22150
}
GET /api/fetch
Fetch endpoint for API-first mode. Returns all player data with a timestamp, used to load data from remote API on startup.
Response 200 OK
{
"players": [...],
"lastUpdated": 1706500000000
}
POST /api/push
Accept pushed data from other servers. Useful for self-hosted setups where you want to aggregate data from multiple servers.
Request Body
Same format as the public API Upload Stats endpoint.
Response 200 OK
{
"success": true,
"message": "Data received",
"timestamp": 1706500000000
}
Player Object
The player object contains all tracked statistics for an individual player.
Identity Fields
| Field | Type | Description |
|---|---|---|
uuid |
string | Player's unique identifier (UUID) |
username |
string | Player's display name |
online |
boolean | Whether player is currently online |
Playtime & Activity
| Field | Type | Description |
|---|---|---|
playtimeSeconds |
integer | Total time played in seconds |
playtimeFormatted |
string | Human-readable playtime (e.g., "2h 30m 15s") |
firstJoined |
integer | Epoch timestamp (ms) of first join to server |
firstJoinedFormatted |
string | Human-readable first join date |
lastSeen |
integer | Epoch timestamp (ms) of last activity |
lastSeenFormatted |
string | Human-readable last seen date |
Combat Statistics
| Field | Type | Description |
|---|---|---|
damageDealt |
float | Total damage dealt by player |
playerKills |
integer | Number of PvP kills |
mobKills |
integer | Number of mob/PvE kills |
deathCount |
integer | Total number of deaths |
pvpDeaths |
integer | Deaths caused by other players |
pveDeaths |
integer | Deaths caused by mobs or environment |
lastDeathCause |
string|null | Description of last death (e.g., "Killed by Zombie", "Fell to their death") |
Block Interaction
| Field | Type | Description |
|---|---|---|
blocksPlaced |
integer | Total blocks placed |
blocksBroken |
integer | Total blocks broken/mined |
Stats Object
Aggregate statistics for the entire server.
| Field | Type | Description |
|---|---|---|
totalPlayers |
integer | Total unique players tracked |
onlinePlayers |
integer | Currently online player count |
totalPlaytimeSeconds |
integer | Combined playtime of all players |
totalDamageDealt |
float | Combined damage dealt |
totalPlayerKills |
integer | Server-wide PvP kills |
totalMobKills |
integer | Server-wide mob kills |
totalDeaths |
integer | Server-wide death count (all deaths) |
totalPvpDeaths |
integer | Server-wide PvP deaths |
totalPveDeaths |
integer | Server-wide PvE/environmental deaths |
totalBlocksPlaced |
integer | Total blocks placed on server |
totalBlocksBroken |
integer | Total blocks broken on server |
Error Responses
The API returns standard HTTP status codes along with JSON error details.
400 Bad Request
{
"error": "Missing server parameter",
"usage": "GET /?server=connect.myserver.com or GET /?ip=x.x.x.x"
}
403 Forbidden
{
"error": "Server name already claimed",
"message": "The name 'servername' is registered to another server."
}
404 Not Found
{
"error": "Server not found",
"message": "No data for: unknownserver.com"
}
405 Method Not Allowed
{
"error": "Method not allowed"
}
JavaScript Example
Fetch server stats and display a player leaderboard:
// Fetch server statistics
async function getServerStats(serverName) {
const response = await fetch(
`https://api.hytaletravelers.com/?server=${encodeURIComponent(serverName)}`
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
// Get top players by playtime
function getTopPlayers(players, limit = 10) {
return players
.sort((a, b) => b.playtimeSeconds - a.playtimeSeconds)
.slice(0, limit);
}
// Usage example
getServerStats('hytaletravelers.com')
.then(data => {
console.log(`Server: ${data.serverName}`);
console.log(`Online: ${data.stats.onlinePlayers}/${data.stats.totalPlayers}`);
const topPlayers = getTopPlayers(data.players, 5);
topPlayers.forEach((player, i) => {
console.log(`${i + 1}. ${player.username} - ${player.playtimeFormatted}`);
});
})
.catch(err => console.error('Error:', err));
Python Example
Simple Python script to fetch and display server stats:
import requests
def get_server_stats(server_name):
"""Fetch statistics for a specific server."""
url = f"https://api.hytaletravelers.com/?server={server_name}"
response = requests.get(url)
response.raise_for_status()
return response.json()
def get_online_players(data):
"""Filter to only online players."""
return [p for p in data['players'] if p['online']]
# Usage
if __name__ == "__main__":
data = get_server_stats("hytaletravelers.com")
print(f"Server: {data['serverName']}")
print(f"Total Players: {data['stats']['totalPlayers']}")
print(f"Online Now: {data['stats']['onlinePlayers']}")
print("\nOnline Players:")
for player in get_online_players(data):
print(f" - {player['username']} ({player['playtimeFormatted']})")
cURL Examples
Get Server Stats
curl -X GET "https://api.hytaletravelers.com/?server=hytaletravelers.com"
List All Public Servers
curl -X GET "https://api.hytaletravelers.com/servers?public=true"
Pretty Print with jq
curl -s "https://api.hytaletravelers.com/?server=hytaletravelers.com" | jq '.'
Get Only Online Players
curl -s "https://api.hytaletravelers.com/?server=hytaletravelers.com" | jq '.players | map(select(.online == true))'
Interactive Demo
See the API in action with this live splitscreen demo. The left panel shows the implementation code, and the right panel shows the working result.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Server Stats</title>
<style>
body {
font-family: system-ui, sans-serif;
background: #1a1a2e;
color: #eee;
padding: 20px;
margin: 0;
}
.card {
background: #16213e;
border-radius: 12px;
padding: 20px;
margin-bottom: 16px;
}
h1 { color: #58a6ff; margin-top: 0; }
.stat-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.stat {
background: #0f3460;
padding: 12px;
border-radius: 8px;
text-align: center;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #58a6ff;
}
.stat-label {
font-size: 12px;
color: #8b949e;
}
.player {
display: flex;
align-items: center;
padding: 8px;
border-bottom: 1px solid #30363d;
}
.player img {
width: 32px;
height: 32px;
border-radius: 4px;
margin-right: 12px;
}
.online { color: #3fb950; }
.offline { color: #8b949e; }
#error { color: #f85149; }
</style>
</head>
<body>
<div class="card">
<h1 id="serverName">Loading...</h1>
<div class="stat-grid">
<div class="stat">
<div class="stat-value" id="online">-</div>
<div class="stat-label">Online</div>
</div>
<div class="stat">
<div class="stat-value" id="total">-</div>
<div class="stat-label">Total Players</div>
</div>
<div class="stat">
<div class="stat-value" id="kills">-</div>
<div class="stat-label">Mob Kills</div>
</div>
</div>
</div>
<div class="card">
<h2>Top Players</h2>
<div id="players"></div>
</div>
<div id="error"></div>
<script>
const SERVER = 'HytaleTravelers.com';
async function fetchStats() {
try {
const res = await fetch(
`https://api.hytaletravelers.com/?server=${SERVER}`
);
const data = await res.json();
if (data.error) {
document.getElementById('error').textContent = data.error;
return;
}
// Update server info
document.getElementById('serverName').textContent =
data.serverName || SERVER;
document.getElementById('online').textContent =
data.stats.onlinePlayers;
document.getElementById('total').textContent =
data.stats.totalPlayers;
document.getElementById('kills').textContent =
data.stats.totalMobKills.toLocaleString();
// Show top 5 players
const top5 = data.players
.sort((a, b) => b.playtimeSeconds - a.playtimeSeconds)
.slice(0, 5);
document.getElementById('players').innerHTML = top5
.map(p => `
<div class="player">
<div>
<strong>${p.username}</strong>
<span class="${p.online ? 'online' : 'offline'}">
${p.online ? ' ● Online' : ''}
</span>
<div style="font-size:12px;color:#8b949e">
${p.playtimeFormatted}
</div>
</div>
</div>
`).join('');
} catch (err) {
document.getElementById('error').textContent =
'Failed to load: ' + err.message;
}
}
fetchStats();
setInterval(fetchStats, 30000); // Refresh every 30s
</script>
</body>
</html>