Your game sends a message — a new score, or a rank question — and gets a message back it can print straight on screen. No account, no SDK, one HTTP call.
Base URL https://game.easytrans.io/api
X-Game-Key header — ask the leaderboard owner for the value.
Reading (rank, leaderboard) is open to everyone, and the
demo board accepts writes with no key so you can experiment.
Every response contains a ready-to-display message string plus the raw
numbers if you want to draw your own UI.
POST https://game.easytrans.io/api/message
{"type":"score","game":"byow","player":"Milo","score":1200}
→ {"ok":true,"message":"New personal best, Milo: 1200 (was 900) — rank 3 of 41.",
"rank":3,"best":1200,"total_players":41,"top":[...]}
| Method | Path | What it does |
|---|---|---|
| POST | /api/message |
One door for everything — type = score, rank, or top |
| POST | /api/score |
Submit a score |
| GET | /api/rank?player=Milo |
One player's best score and rank |
| GET | /api/leaderboard?limit=10 |
Top N players |
| GET | /healthz |
Returns ok when the server is alive |
| Field | Type | Notes |
|---|---|---|
game | string | Optional, defaults to byow. Each value is its own separate leaderboard. |
player | string | 1–24 chars: letters, digits, space, _ . - #, or Chinese. This is the identity — same name = same player. |
score | integer | Higher is better. −1e9 … 1e9. |
note | string | Optional free text stored with the submission (seed, level, …). |
Plain fetch, works in a browser or in Node 18+.
const API = "https://game.easytrans.io/api";
const KEY = "YOUR_GAME_KEY"; // only needed for sending scores
async function send(payload) {
const r = await fetch(API + "/message", {
method: "POST",
headers: { "Content-Type": "application/json", "X-Game-Key": KEY },
body: JSON.stringify(payload)
});
return r.json();
}
// 1. game over → send the score
const res = await send({ type: "score", player: "Milo", score: 1200 });
console.log(res.message); // "New personal best, Milo: 1200 ..."
console.log(res.rank); // 3
// 2. player asks "where am I?"
const me = await send({ type: "rank", player: "Milo" });
console.log(me.message); // "Milo: best 1200, rank 3 of 41."
// 3. draw the top 10
const board = await send({ type: "top", limit: 10 });
board.entries.forEach(e => console.log(`${e.rank}. ${e.player} ${e.score}`));
const me = await (await fetch(API + "/rank?player=Milo")).json();
const top = await (await fetch(API + "/leaderboard?limit=10")).json();
Java 11+ HttpClient, no dependencies. Drop this class into your game.
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
public class Leaderboard {
private static final String API = "https://game.easytrans.io/api";
private static final String KEY = "YOUR_GAME_KEY"; // only for sending scores
private static final HttpClient HTTP = HttpClient.newHttpClient();
private static String post(String path, String json) throws Exception {
HttpRequest req = HttpRequest.newBuilder(URI.create(API + path))
.header("Content-Type", "application/json")
.header("X-Game-Key", KEY)
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
return HTTP.send(req, HttpResponse.BodyHandlers.ofString()).body();
}
private static String get(String path) throws Exception {
HttpRequest req = HttpRequest.newBuilder(URI.create(API + path)).GET().build();
return HTTP.send(req, HttpResponse.BodyHandlers.ofString()).body();
}
/** Send a score, get back the sentence to show the player. */
public static String submit(String player, int score) throws Exception {
String json = String.format("{\"type\":\"score\",\"player\":\"%s\",\"score\":%d}",
player, score);
return message(post("/message", json));
}
/** Ask for this player's rank. */
public static String rank(String player) throws Exception {
return message(get("/rank?player=" + URLEncoder.encode(player, StandardCharsets.UTF_8)));
}
/** Top N as raw JSON — parse with Gson/Jackson, or just show .message */
public static String top(int n) throws Exception {
return get("/leaderboard?limit=" + n);
}
// Tiny extractor so the example stays dependency-free.
// In real code use Gson: new Gson().fromJson(body, Result.class)
private static String message(String body) {
int i = body.indexOf("\"message\":\"");
if (i < 0) return body;
int start = i + 11, end = start;
StringBuilder sb = new StringBuilder();
while (end < body.length() && body.charAt(end) != '"') {
if (body.charAt(end) == '\\') end++;
sb.append(body.charAt(end++));
}
return sb.toString();
}
public static void main(String[] args) throws Exception {
System.out.println(submit("Milo", 1200));
System.out.println(rank("Milo"));
}
}
curl -s https://game.easytrans.io/api/message \
-H 'Content-Type: application/json' -H 'X-Game-Key: YOUR_GAME_KEY' \
-d '{"type":"score","player":"Milo","score":1200}'
curl -s 'https://game.easytrans.io/api/rank?player=Milo'
curl -s 'https://game.easytrans.io/api/leaderboard?limit=10'
(the demo leaderboard is game=demo, so it won't touch your real board)
| Status | Meaning |
|---|---|
400 | Bad name, bad score, or unknown type |
401 | A game key is required and yours is missing or wrong |
429 | Too many requests — 120 writes / 600 reads per minute per IP |
Every error also returns {"ok":false,"error":"...","message":"Error: ..."}, so the same
display code works for the failure path.
Score writes are locked to a shared key so strangers can't stuff the board.
Reads never need it, and game: "demo" writes never need it either.
headers: { "Content-Type": "application/json", "X-Game-Key": "YOUR_GAME_KEY" }
// or: Authorization: Bearer YOUR_GAME_KEY