{"openapi":"3.1.0","info":{"title":"FabricCraft Public API","version":"1.0.0","description":"Public, read-only statistics for the FabricCraft **survival** and **creative** servers: player profiles, all-time top-100 leaderboards and player head images. No key or sign-up is needed.\n\n## Fair use\n\n- The API is public and anonymous. Each IP address may make **60 JSON requests per minute** (short bursts of up to 10 per second) and 600 head-image requests per minute.\n- Over the limit you get HTTP **429** with a `Retry-After` header: wait that many seconds before retrying. Responses carry `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` headers.\n- Responses are cached: profiles for 60 seconds, leaderboards for 5 minutes, head images for up to 10 minutes and the leaderboard list for 1 hour. Polling faster than that returns the same data.\n- Statistics are refreshed from the game about every 15 minutes.\n- Please credit \"FabricCraft\" and link to https://fabriccraft.net when you show this data. Questions: support@fabriccraft.net.\n\n## What is published\n\nUsername, UUID, head image, public rank and name colour, guild, first-join date, and lifetime counters from the game's own statistics files on survival and creative. Nothing about sessions, when a player was last online, locations, chat, punishments or purchases is published.\n\n## Privacy and opt-out\n\nProfiles are public by default. From Tuesday 13 October 2026, players can hide their profile in game with `/socialsettings public_stats hidden` or in the hub's Social Settings menu, and show it again at any time. Until then, email support@fabriccraft.net with your Minecraft username and we will hide it within 24 hours. A hidden profile answers exactly like an unknown player (404). It disappears within about 5 minutes on the profile, and within about 20 minutes on leaderboards and head images. These times include copies cached by Cloudflare, by visitors' browsers and by the in-game /playerstats command. Only `public_stats` hides the stats profile; a hidden online status does not.\n\n## Units\n\nPlay time is in seconds, distances in centimetres (100 cm = 1 block), damage in tenths of a health point (divide by 20 for hearts).","contact":{"name":"FabricCraft","email":"support@fabriccraft.net","url":"https://fabriccraft.net"}},"components":{"schemas":{"Server":{"type":"string","enum":["survival","creative"],"description":"Game server."},"StatId":{"type":"string","enum":["playtime","deaths","playerKills","mobKills","blocksMined","itemsCrafted","itemsUsed","walk","sprint","swim","elytra","boat","horse","minecart","jumps","damageDealt","damageTaken","villagerTrades","fishCaught","animalsBred","advancements"],"description":"Board id (camelCase)."},"StatUnit":{"type":"string","enum":["seconds","count","cm","hp_tenths"],"description":"seconds = whole seconds; count = plain counter; cm = centimetres; hp_tenths = tenths of a health point (divide by 20 for hearts)."},"NameColor":{"type":"object","properties":{"stops":{"minItems":1,"maxItems":4,"type":"array","items":{"type":"string","pattern":"^#[0-9a-f]{6}$","description":"Lowercase #rrggbb.","example":"#edb300"},"description":"One stop is a solid colour; two to four stops are a left-to-right gradient."}},"required":["stops"],"additionalProperties":false,"description":"The colour the player's name is shown in."},"Rank":{"type":"object","properties":{"id":{"type":"string","enum":["default","newbie","trusted","technical","creative-builder","vip","mvp","youtube","mod","community-manager","head-staff","admin"],"description":"Public rank id. Ranks that are not public are reported as \"default\"."},"name":{"type":"string","minLength":1,"maxLength":32,"description":"Display label of the rank.","example":"VIP"},"color":{"description":"The player's name colour: their chosen name colour if they have one, otherwise their rank's colour.","$ref":"#/components/schemas/NameColor"}},"required":["id","name","color"],"additionalProperties":false},"Guild":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":32},"tag":{"anyOf":[{"type":"string","pattern":"^[A-Za-z0-9]{1,3}$"},{"type":"null"}],"description":"Short guild tag (1-3 letters or digits), or null when the guild has none."}},"required":["name","tag"],"additionalProperties":false},"BlockCount":{"type":"object","properties":{"block":{"type":"string","pattern":"^[a-z0-9_.-]+:[a-z0-9_./-]+$","description":"Namespaced block id.","example":"minecraft:deepslate"},"count":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["block","count"],"additionalProperties":false},"Distance":{"type":"object","properties":{"walkCm":{"type":"integer","minimum":0,"maximum":9007199254740991},"sprintCm":{"type":"integer","minimum":0,"maximum":9007199254740991},"swimCm":{"type":"integer","minimum":0,"maximum":9007199254740991},"elytraCm":{"type":"integer","minimum":0,"maximum":9007199254740991},"boatCm":{"type":"integer","minimum":0,"maximum":9007199254740991},"horseCm":{"type":"integer","minimum":0,"maximum":9007199254740991},"minecartCm":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["walkCm","sprintCm","swimCm","elytraCm","boatCm","horseCm","minecartCm"],"additionalProperties":false,"description":"Distances in centimetres (100 cm = 1 block)."},"CreativeStats":{"type":"object","properties":{"playtimeSeconds":{"type":"integer","minimum":0,"maximum":9007199254740991},"deaths":{"type":"integer","minimum":0,"maximum":9007199254740991},"playerKills":{"type":"integer","minimum":0,"maximum":9007199254740991},"mobKills":{"type":"integer","minimum":0,"maximum":9007199254740991},"blocksMined":{"type":"object","properties":{"total":{"type":"integer","minimum":0,"maximum":9007199254740991},"top":{"maxItems":5,"type":"array","items":{"$ref":"#/components/schemas/BlockCount"},"description":"Up to 5 most-mined blocks, highest count first (ties by block id)."}},"required":["total","top"],"additionalProperties":false},"itemsCrafted":{"type":"integer","minimum":0,"maximum":9007199254740991},"itemsUsed":{"type":"integer","minimum":0,"maximum":9007199254740991},"distance":{"$ref":"#/components/schemas/Distance"},"jumps":{"type":"integer","minimum":0,"maximum":9007199254740991},"damageDealt":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Tenths of a health point (divide by 20 for hearts)."},"damageTaken":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Tenths of a health point (divide by 20 for hearts)."},"villagerTrades":{"type":"integer","minimum":0,"maximum":9007199254740991},"fishCaught":{"type":"integer","minimum":0,"maximum":9007199254740991},"animalsBred":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["playtimeSeconds","deaths","playerKills","mobKills","blocksMined","itemsCrafted","itemsUsed","distance","jumps","damageDealt","damageTaken","villagerTrades","fishCaught","animalsBred"],"additionalProperties":false},"SurvivalStats":{"type":"object","properties":{"playtimeSeconds":{"type":"integer","minimum":0,"maximum":9007199254740991},"deaths":{"type":"integer","minimum":0,"maximum":9007199254740991},"playerKills":{"type":"integer","minimum":0,"maximum":9007199254740991},"mobKills":{"type":"integer","minimum":0,"maximum":9007199254740991},"blocksMined":{"type":"object","properties":{"total":{"type":"integer","minimum":0,"maximum":9007199254740991},"top":{"maxItems":5,"type":"array","items":{"$ref":"#/components/schemas/BlockCount"},"description":"Up to 5 most-mined blocks, highest count first (ties by block id)."}},"required":["total","top"],"additionalProperties":false},"itemsCrafted":{"type":"integer","minimum":0,"maximum":9007199254740991},"itemsUsed":{"type":"integer","minimum":0,"maximum":9007199254740991},"distance":{"$ref":"#/components/schemas/Distance"},"jumps":{"type":"integer","minimum":0,"maximum":9007199254740991},"damageDealt":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Tenths of a health point (divide by 20 for hearts)."},"damageTaken":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Tenths of a health point (divide by 20 for hearts)."},"villagerTrades":{"type":"integer","minimum":0,"maximum":9007199254740991},"fishCaught":{"type":"integer","minimum":0,"maximum":9007199254740991},"animalsBred":{"type":"integer","minimum":0,"maximum":9007199254740991},"advancements":{"anyOf":[{"type":"object","properties":{"completed":{"type":"integer","minimum":0,"maximum":9007199254740991},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Advancements available on the current survival version."}},"required":["completed","total"],"additionalProperties":false},{"type":"null"}],"description":"Completed advancements (recipes excluded), or null when no advancement data exists."}},"required":["playtimeSeconds","deaths","playerKills","mobKills","blocksMined","itemsCrafted","itemsUsed","distance","jumps","damageDealt","damageTaken","villagerTrades","fishCaught","animalsBred","advancements"],"additionalProperties":false},"PlayerProfile":{"type":"object","properties":{"uuid":{"type":"string","pattern":"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","description":"Minecraft account UUID, dashed and lowercase.","example":"0f8c2a51-7d3e-4b9a-a6c1-2e5f9d84b730"},"username":{"type":"string","pattern":"^[A-Za-z0-9_]{1,16}$","description":"Minecraft username in its real capitalisation.","example":"Example_Player"},"head":{"type":"string","format":"uri","description":"Head image URL (64 px by default; append ?size=16|32|64|128)."},"rank":{"$ref":"#/components/schemas/Rank"},"guild":{"anyOf":[{"$ref":"#/components/schemas/Guild"},{"type":"null"}]},"firstJoined":{"anyOf":[{"type":"string","format":"date","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"},{"type":"null"}],"description":"Date (UTC) the player first joined, or null if unknown."},"statsAsOf":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the stats were last refreshed from the game (global, not per player)."},"servers":{"type":"object","properties":{"survival":{"anyOf":[{"$ref":"#/components/schemas/SurvivalStats"},{"type":"null"}],"description":"null when the player has no survival stats."},"creative":{"anyOf":[{"$ref":"#/components/schemas/CreativeStats"},{"type":"null"}],"description":"null when the player has no creative stats."}},"required":["survival","creative"],"additionalProperties":false}},"required":["uuid","username","head","rank","guild","firstJoined","statsAsOf","servers"],"additionalProperties":false},"Board":{"type":"object","properties":{"server":{"$ref":"#/components/schemas/Server"},"stat":{"$ref":"#/components/schemas/StatId"},"label":{"type":"string"},"unit":{"$ref":"#/components/schemas/StatUnit"}},"required":["server","stat","label","unit"],"additionalProperties":false},"BoardCatalogue":{"type":"object","properties":{"boards":{"type":"array","items":{"$ref":"#/components/schemas/Board"},"description":"All 41 leaderboards: 21 survival, 20 creative."}},"required":["boards"],"additionalProperties":false},"LeaderboardEntry":{"type":"object","properties":{"position":{"type":"integer","minimum":1,"maximum":100},"uuid":{"type":"string","pattern":"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","description":"Minecraft account UUID, dashed and lowercase.","example":"0f8c2a51-7d3e-4b9a-a6c1-2e5f9d84b730"},"username":{"type":"string","pattern":"^[A-Za-z0-9_]{1,16}$","description":"Minecraft username in its real capitalisation.","example":"Example_Player"},"head":{"type":"string","format":"uri","description":"Head image URL (64 px by default; append ?size=16|32|64|128)."},"rank":{"$ref":"#/components/schemas/Rank"},"value":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The stat value, in the board's unit."}},"required":["position","uuid","username","head","rank","value"],"additionalProperties":false},"LeaderboardPage":{"type":"object","properties":{"server":{"$ref":"#/components/schemas/Server"},"stat":{"$ref":"#/components/schemas/StatId"},"label":{"type":"string"},"unit":{"$ref":"#/components/schemas/StatUnit"},"builtAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},{"type":"null"}],"description":"When the board was last rebuilt, or null before the first build."},"page":{"type":"integer","minimum":1,"maximum":9007199254740991},"pageSize":{"anyOf":[{"type":"number","const":10},{"type":"number","const":25}]},"totalEntries":{"type":"integer","minimum":0,"maximum":100,"description":"Entries on the whole board (at most 100); pages = ceil(totalEntries / pageSize)."},"entries":{"maxItems":25,"type":"array","items":{"$ref":"#/components/schemas/LeaderboardEntry"}}},"required":["server","stat","label","unit","builtAt","page","pageSize","totalEntries","entries"],"additionalProperties":false},"Error":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_player","invalid_request","not_found","unknown_board","method_not_allowed","rate_limited","unavailable","internal_error"]},"message":{"type":"string"}},"required":["error","message"],"additionalProperties":false}}},"paths":{"/v1/players/{player}":{"get":{"operationId":"getPlayer","summary":"Get a player profile","tags":["players"],"description":"Profile and lifetime stats on survival and creative. Unknown players and players whose stats profile is private get the same 404 response, so the two cannot be told apart. Cached for 60 seconds.","parameters":[{"schema":{"type":"string","pattern":"^(?:[A-Za-z0-9_]{1,16}|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|[0-9a-fA-F]{32})$","example":"Example_Player"},"in":"path","name":"player","required":true,"description":"Minecraft username (case-insensitive) or UUID (dashed or 32 hex digits)."}],"responses":{"200":{"description":"The player profile.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlayerProfile"}}}},"400":{"description":"Invalid player, parameter or page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/players/{player}/head.png":{"get":{"operationId":"getPlayerHead","summary":"Get a player head image","tags":["players"],"description":"The face (with hat layer) of the player's skin as a PNG, scaled with nearest-neighbour. A neutral placeholder is served while the skin is being fetched. Unknown players and players whose stats profile is private get the same 404 response, so the two cannot be told apart.","parameters":[{"schema":{"default":64,"enum":[16,32,64,128],"type":"number"},"in":"query","name":"size","required":false,"description":"Edge length in pixels."},{"schema":{"type":"string","pattern":"^(?:[A-Za-z0-9_]{1,16}|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|[0-9a-fA-F]{32})$","example":"Example_Player"},"in":"path","name":"player","required":true,"description":"Minecraft username (case-insensitive) or UUID (dashed or 32 hex digits)."}],"responses":{"200":{"description":"PNG image.","content":{"image/png":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid player, parameter or page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/leaderboards":{"get":{"operationId":"listLeaderboards","summary":"List all leaderboards","tags":["leaderboards"],"description":"The 41 boards: 21 on survival, 20 on creative. Cached for 1 hour.","responses":{"200":{"description":"The leaderboard catalogue.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoardCatalogue"}}}},"429":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/leaderboards/{server}/{stat}":{"get":{"operationId":"getLeaderboard","summary":"Get a leaderboard page","tags":["leaderboards"],"description":"All-time top 100 for one stat on one server, highest first (ties by UUID). Players with a private profile are left out and the positions close up. Cached for 5 minutes.","parameters":[{"schema":{"default":1,"type":"integer","minimum":1,"maximum":10},"in":"query","name":"page","required":false,"description":"Page number. 1-10 with pageSize=10, 1-4 with pageSize=25."},{"schema":{"default":25,"enum":[10,25],"type":"number"},"in":"query","name":"pageSize","required":false,"description":"Rows per page."},{"schema":{"$ref":"#/components/schemas/Server"},"in":"path","name":"server","required":true},{"schema":{"$ref":"#/components/schemas/StatId"},"in":"path","name":"stat","required":true}],"responses":{"200":{"description":"One page of the leaderboard.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaderboardPage"}}}},"400":{"description":"Invalid player, parameter or page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporarily unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"servers":[{"url":"https://api.fabriccraft.net"}],"tags":[{"name":"players","description":"Player profiles and head images."},{"name":"leaderboards","description":"All-time top-100 leaderboards."}]}