API documentation
The API lets your plugin, bot or website check your players' votes so you can reward them. It follows the usual voting-API format (claim-username, check-ip…): if your reward system is already connected to another voting site, you usually only need to change the address and the token.
Authentication
Your token is in Dashboard → your server → API. Pass it in one of these two ways:
https://arkasaserver.com/v1/votes/last?server_token=YOUR_TOKEN Authorization: Bearer YOUR_TOKEN
All responses are JSON. Limit: 120 requests per minute per server. A vote stays valid for 2 h.
Keep your token secret: it gives access to your server's votes. Prefer the Authorization header over the URL parameter (a URL can end up in logs), and never publish it (screenshot, GitHub, Discord…). If it leaked, create a new one in the API tab: the old one stops working immediately.
Claim a vote by player name
GET /v1/votes/claim-username?playername=Name. This is the main route: it marks the vote as claimed, only once.
{
"success": true,
"claimed": 1,
"message": "Vote found and claimed: give the reward to the player.",
"vote": { "id": 42, "playername": "Dodo", "voted_at": 1791700000, "claimed": true, "claimed_at": 1791700100 }
}
claimed | Meaning | What to do |
|---|---|---|
0 | No vote in the last 2 hours | Invite the player to vote |
1 | Vote found, claimed just now | Give the reward |
2 | Vote already claimed | Nothing (prevents double rewards) |
Other routes
| Route | Description |
|---|---|
GET /v1/votes/claim-ip?ip=1.2.3.4 | Like claim-username, but with the player's IP. |
GET /v1/votes/check-ip?ip=1.2.3.4 | Checks without claiming: HTTP 200 + "success": true if a vote exists, HTTP 404 otherwise. |
GET /v1/votes/check-username?playername=Name | Same, by player name. |
GET /v1/votes/claim-eos?eosid=… | Like claim-username, with the player's EOS ID (32 characters). Only finds votes from players who linked their character. |
GET /v1/votes/check-eos?eosid=… | Checks without claiming, by EOS ID. |
GET /v1/votes/since?after_id=0 | For a bot: votes newer than after_id, oldest first (100 by default, limit up to 500). Returns last_id to keep for the next call. |
POST /v1/votes/{id}/claim | Marks a specific vote as rewarded. claimed: 1 = now, 2 = already done. |
POST /v1/players/link | Confirms a player link: code (shown to the player on the site), eosid (EOS ID of whoever typed the code in game) and playername (optional). |
GET /v1/votes/last | Votes from the last hour (100 maximum). |
GET /v1/votes/unclaimed | Votes still valid and not yet claimed, for batch distribution. |
GET /v1/votes/top-players | This month's voter ranking (player name and number of votes). |
GET /v1/leaderboard | Top voters of the current period (week or month depending on your setting; ?period=week or month to force one, limit up to 100): rank, name, EOS ID, Discord, votes and prize (reward for that place, if you set one). prizes lists the reward of each place. |
GET /v1/leaderboard/winners?periods=5 | Winners of the last finished periods, with the reward of each place as it was announced: handy to hand out prizes. |
GET /v1/server | Server info: monthly votes, total, rank, vote link. |
On error: { "success": false, "error": "Unauthorized", "message": "…" } with the matching HTTP code (400, 401, 404, 429).
For a bot (Discord, RCON…)
Every 30 to 60 seconds, fetch new votes, give the reward, then mark the vote as rewarded. Keep last_id between calls: no vote is missed or rewarded twice.
let lastId = 0; // save it (file, database…) to resume after a restart
setInterval(async () => {
const res = await fetch(`https://arkasaserver.com/v1/votes/since?after_id=${lastId}`, { headers: { Authorization: 'Bearer ' + TOKEN } });
const data = await res.json();
for (const vote of data.votes) {
if (!vote.claimed) {
// vote.eos_id is set when the player linked their character, otherwise use vote.playername
if (vote.eos_id) await rcon(`addpoints ${vote.eos_id} 80`);
await fetch(`https://arkasaserver.com/v1/votes/${vote.id}/claim`, { method: 'POST', headers: { Authorization: 'Bearer ' + TOKEN } });
}
}
lastId = data.last_id;
}, 30000);
Character link (EOS ID)
A player logged in on the site can link their character: the site gives them a code (e.g. K7Q2XM, valid 30 minutes) to type in the game chat. Your bot, which knows which EOS ID wrote that message, confirms the link:
POST https://arkasaserver.com/v1/players/link
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json
{ "code": "K7Q2XM", "eosid": "0002a1b2c3d4e5f60718293a4b5c6d7e", "playername": "Dodo" }
From then on, every vote from this player (logged in on the site) includes their eos_id, in the API and the webhook. The EOS ID is never shown publicly: only the owner of the voted server receives it.
Codes given by the Link button of our Discord bot are confirmed the same way: the response then includes discord_id. Votes cast with the bot's personal vote link include discord_id and eos_id.
JavaScript example (Node.js 18+)
const res = await fetch(`https://arkasaserver.com/v1/votes/claim-username?playername=${encodeURIComponent(name)}`, {
headers: { Authorization: `Bearer ${process.env.VOTE_TOKEN}` },
});
const data = await res.json();
if (data.claimed === 1) {
// give the reward, for example: addpoints EOS_ID 80
}
PHP example
$url = 'https://arkasaserver.com/v1/votes/claim-username?server_token=' . TOKEN . '&playername=' . urlencode($name);
$data = json_decode(file_get_contents($url), true);
if ($data['claimed'] === 1) {
// give the reward
}
Vote webhook
If you set a webhook URL, the site sends you a JSON POST for every vote:
{
"event": "vote",
"server": { "id": 1, "slug": "my-server", "name": "My server" },
"vote": { "id": 42, "playername": "Dodo", "eos_id": null, "voted_at": 1791700000 }
}
Your server must answer with a 2xx code within 5 seconds. Otherwise the site retries up to 6 times: after 1 min, 5 min, 15 min, 1 h and 3 h. The “Send a test” button sends "event": "test".
Always check the signature so nobody can send you fake votes. Two headers come with every request: X-Vote-Timestamp and X-Vote-Signature. The signature is sha256= followed by the HMAC-SHA256 of timestamp + "." + raw body, computed with your secret.
// Node.js / Express
const crypto = require('crypto');
app.post('/vote-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('X-Vote-Timestamp');
const expected = 'sha256=' + crypto.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(ts + '.' + req.body).digest('hex');
const sig = req.get('X-Vote-Signature') || '';
const valid = sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
if (!valid || Math.abs(Date.now() / 1000 - ts) > 300) return res.sendStatus(401);
const { event, vote } = JSON.parse(req.body);
if (event === 'vote') { /* give the reward to vote.playername */ }
res.sendStatus(200);
});
// PHP
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_VOTE_TIMESTAMP'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, WEBHOOK_SECRET);
if (!hash_equals($expected, $_SERVER['HTTP_X_VOTE_SIGNATURE'] ?? '') || abs(time() - (int)$ts) > 300) {
http_response_code(401); exit;
}
$data = json_decode($body, true);
The same vote may exceptionally arrive twice (if your response got lost): use vote.id to avoid rewarding twice.