MCVoteStats

Server owners

Reward your players’ votes

Three ways to know that a player has voted (plus a statistics API as a bonus), to combine as suits your server. All of them are free. Set them up in My servers → your server.

1. Votifier (instant)

On every vote, the site contacts your server and sends it the username. Your rewards plugin (VotingPlugin, SuperbVote, NuVotifier + commands…) gives the reward right away.

  1. Install NuVotifier on your server, or on your BungeeCord / Velocity proxy.
  2. Open the Votifier port (8192 by default) in your host’s firewall.
  3. Protocol v2 (recommended): copy the token from plugins/nuvotifier/config.yml (tokens section). For a token dedicated to the site, add a line MCVoteStats: your-token.
  4. Protocol v1: copy the contents of plugins/Votifier/rsa/public.key.
  5. Click “Test”: a test vote is sent to your server and the result is displayed.

Service name sent with each vote: MCVoteStats. If your server does not respond, the site retries 7 times over about 9 hours; the Deliveries tab shows each attempt.

2. Verification API (no port to open)

Your plugin or website asks ours whether a player has voted. Create your API key in the “API and webhooks” tab. Pass the key as the key parameter, or in the X-Api-Key or Authorization: Bearer header. Limit: 600 requests per minute.

Check a vote

GET https://mcvotestats.com/api/v1/votes/check?key=YOUR_KEY&pseudo=Steve
GET https://mcvotestats.com/api/v1/votes/check?key=YOUR_KEY&ip=203.0.113.7
GET https://mcvotestats.com/api/v1/votes/check?key=YOUR_KEY&pseudo=Steve&format=text   → 1 or 0
{
  "ok": true,
  "hasVoted": true,
  "pseudo": "Steve",
  "votedAt": "2026-10-09T12:00:00.000Z",
  "nextVoteAt": "2026-10-09T14:00:00.000Z",
  "claimed": false,
  "window": 120
}

The search covers the last 120 minutes (the vote interval). Use the window (or duration) parameter to change it, from 1 to 1,440 minutes. IP verification recognizes the player whether they connect over IPv4 or IPv6.

Claim the reward

To give only one reward per vote, mark the vote as claimed:

POST https://mcvotestats.com/api/v1/votes/claim?key=YOUR_KEY&pseudo=Steve
→ { "ok": true, "claimed": true, "vote": { "id": "1234", "pseudo": "Steve", "votedAt": "…" } }
→ { "ok": true, "claimed": false, "reason": "ALREADY_CLAIMED" }   (already given)
→ { "ok": true, "claimed": false, "reason": "NO_VOTE" }           (no recent vote)

List votes

GET https://mcvotestats.com/api/v1/votes?key=YOUR_KEY&since=2026-10-09T12:00:00Z&limit=100

Votes received since a date (7 days at most), from oldest to newest. To continue, call again with since set to the value of next.

Server statistics

GET https://mcvotestats.com/api/v1/server?key=YOUR_KEY          (rank, votes this month, top voters)
GET https://mcvotestats.com/api/v1/servers/{slug}              (public, no key)

Java example (Paper)

HttpClient http = HttpClient.newHttpClient();
URI uri = URI.create("https://mcvotestats.com/api/v1/votes/claim?key=" + KEY + "&pseudo=" + player.getName());
http.sendAsync(HttpRequest.newBuilder(uri).POST(HttpRequest.BodyPublishers.noBody()).build(),
               HttpResponse.BodyHandlers.ofString())
    .thenAccept(res -> {
        if (res.body().contains("\"claimed\":true")) {
            // Switch back to the player’s thread (Folia: player.getScheduler()) before giving the reward
            player.getScheduler().run(plugin, t -> giveReward(player), null);
        }
    });

3. HTTP webhook

On every vote, the site sends a JSON POST request to the address of your choice:

{
  "event": "vote",
  "server": { "id": "…", "slug": "my-server", "name": "My server" },
  "vote": { "id": "1234", "pseudo": "Steve", "createdAt": "2026-10-09T12:00:00.000Z" },
  "monthlyVotes": 542
}

Check the signature: the X-TSM-Signature: sha256=… header = HMAC-SHA256 of the secret over X-TSM-Timestamp + "." + raw body. Reject requests whose timestamp is more than 5 minutes old.

// Node.js
const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(req.headers['x-tsm-timestamp'] + '.' + rawBody).digest('hex')
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers['x-tsm-signature']))) return res.status(401).end()

4. Public statistics (no key)

The player count of each tracked server is measured every 5 minutes. The history is public: no key, 120 requests per minute per IP address.

GET https://mcvotestats.com/api/v1/servers/{slug}/history?range=24h|7d|30d|90d|1y|all
GET https://mcvotestats.com/api/v1/servers/{slug}/history?range=30d&format=csv
GET https://mcvotestats.com/api/v1/stats/global?range=7d&mode=skyblock&edition=java
{
  "ok": true,
  "range": "7d",
  "step": "1 h",
  "summary": { "avg": 244, "median": 226, "min": 76, "peak": 553, "uptime": 0.998, "points": 2016 },
  "points": [ { "t": "2026-10-09T12:00:00.000Z", "avg": 236.5, "max": 251 }, … ]
}

avg is the average number of players over the interval, max the peak. Timestamps are in UTC. See the statistics.

Vote link and badge

Give your players the link https://mcvotestats.com/serveur/your-server/voter?pseudo=%player%: the username is already filled in. The “Vote for us” badge (API and webhooks tab) shows your rank live on your website.