API

Votes, vote stats, top voters and listing data as JSON, with example responses for every endpoint, an OpenAPI spec and a demo key you can test with.

Getting started

  • Every server gets an API key on creation · find it in your Dashboard. Keys are rate limited to 120 requests/minute.
  • Send your listing's key as the X-Api-Key header, a key query parameter or a key field in a JSON body. The limit is 120 requests a minute per key; over that you get 429 with a Retry-After header. Times are unix seconds, and months and days are UTC.
  • No listing yet, or building an integration for someone else's? Use the key demo on any endpoint under Votes: it answers with made-up data for an example listing, so you can see real responses before you have a key. Claiming votes with it changes nothing.
curl "https://voxelrank.com/api/v1/stats?days=7" -H "X-Api-Key: demo"

Votes

A listing's votes and vote stats. Needs the listing's key (or the demo key).

GET /api/v1/server Your listing, with its rank and vote counts

Example request

curl "https://voxelrank.com/api/v1/server" \
  -H "X-Api-Key: demo"

Example response 200

{
  "id": 8685,
  "name": "Pear SMP",
  "slug": "pear-smp",
  "ip": "play.pearsmp.net",
  "bedrock": null,
  "apply_url": null,
  "version": "1.21.4",
  "country": "US",
  "tags": [
    "survival",
    "smp"
  ],
  "languages": [
    "en"
  ],
  "online": true,
  "players": 47,
  "max_players": 200,
  "motd": "Pear SMP · Season 3",
  "votes_month": 312,
  "votes_total": 4120,
  "tier": 0,
  "url": "https://voxelrank.com/server/8685-pear-smp",
  "vote_url": "https://voxelrank.com/vote/8685-pear-smp",
  "banner": "https://voxelrank.com/banner/pear-smp.svg",
  "rank": 12,
  "month": "2026-09"
}

Errors

  • 401 Missing or wrong key. {"error":"invalid_key"}
  • 429 More than 120 requests a minute. Wait retry_after seconds (also in the Retry-After header). {"error":"rate_limited","retry_after":12}
GET /api/v1/stats Vote totals, unique voters and votes per day

Parameters

daysinteger (1–90, default 30)How many days the daily series covers, 1 to 90. Default 30.

Example request

curl "https://voxelrank.com/api/v1/stats" \
  -H "X-Api-Key: demo"

Example response 200

{
  "month": "2026-09",
  "votes_today": 18,
  "votes_month": 312,
  "votes_total": 4120,
  "unique_voters_month": 64,
  "rank": 12,
  "daily": [
    {
      "date": "2026-09-25",
      "votes": 24
    },
    {
      "date": "2026-09-26",
      "votes": 17
    },
    {
      "date": "2026-09-27",
      "votes": 18
    }
  ]
}

Errors

  • 401 Missing or wrong key. {"error":"invalid_key"}
  • 429 More than 120 requests a minute. Wait retry_after seconds (also in the Retry-After header). {"error":"rate_limited","retry_after":12}
GET /api/v1/top Top voters of a month

Parameters

monthstringMonth, YYYY-MM (UTC). Default: this month.
limitinteger (1–100, default 10)How many, 1 to 100. Default 10.

Example request

curl "https://voxelrank.com/api/v1/top?month=2026-09" \
  -H "X-Api-Key: demo"

Example response 200 · Most votes first; a tie goes to whoever reached it first.

[
  {
    "username": "Notch",
    "votes": 27,
    "last_vote": 1790000000
  },
  {
    "username": "Alex",
    "votes": 25,
    "last_vote": 1789996400
  }
]

Errors

  • 400 month is not YYYY-MM. {"error":"bad_month"}
  • 401 Missing or wrong key. {"error":"invalid_key"}
  • 429 More than 120 requests a minute. Wait retry_after seconds (also in the Retry-After header). {"error":"rate_limited","retry_after":12}
GET /api/v1/votes Every vote, oldest first, in pages

For syncing votes into your own system: keep the last next_after you saw and ask for after= it next time.

Parameters

afterintegerOnly votes with a larger id. Default 0 (from the start).
sinceintegerOnly votes cast at or after this unix time.
limitinteger (1–500, default 100)Page size, 1 to 500. Default 100.

Example request

curl "https://voxelrank.com/api/v1/votes" \
  -H "X-Api-Key: demo"

Example response 200

{
  "votes": [
    {
      "id": 2045812,
      "username": "Notch",
      "timestamp": 1790000000,
      "claimed": true
    },
    {
      "id": 2045813,
      "username": "Alex",
      "timestamp": 1790000240,
      "claimed": false
    }
  ],
  "next_after": null
}

Errors

  • 401 Missing or wrong key. {"error":"invalid_key"}
  • 429 More than 120 requests a minute. Wait retry_after seconds (also in the Retry-After header). {"error":"rate_limited","retry_after":12}
GET /api/v1/votes/unclaimed Votes not yet rewarded

The reward loop: fetch these, give the rewards, then claim the ids so they are not returned again.

Parameters

limitinteger (1–500, default 100)1 to 500. Default 100.

Example request

curl "https://voxelrank.com/api/v1/votes/unclaimed" \
  -H "X-Api-Key: demo"

Example response 200 · Oldest first.

[
  {
    "id": 2045812,
    "username": "Notch",
    "timestamp": 1790000000
  },
  {
    "id": 2045813,
    "username": "Alex",
    "timestamp": 1790000240
  }
]

Errors

  • 401 Missing or wrong key. {"error":"invalid_key"}
  • 429 More than 120 requests a minute. Wait retry_after seconds (also in the Retry-After header). {"error":"rate_limited","retry_after":12}
POST /api/v1/votes/claim Mark votes as rewarded

Two ways, told apart by the body: - ids: the vote ids you got from /v1/votes/unclaimed. Answers { claimed }. - steamid or username (no ids): every unclaimed vote of that player from the last 24 hours, claimed in one step, so two calls at once cannot both get them. username only matches votes made by typing a name. Answers { steamid | username, claimed, ids, status }. Give the reward when claimed is above 0.

Request body

Example request

curl -X POST "https://voxelrank.com/api/v1/votes/claim" \
  -H "X-Api-Key: demo"

Example response 200

{
  "claimed": 2
}

Errors

  • 400 steamid is not 17 digits, or username is too long. {"error":"bad_steamid"}
  • 401 Missing or wrong key. {"error":"invalid_key"}
  • 429 More than 120 requests a minute. Wait retry_after seconds (also in the Retry-After header). {"error":"rate_limited","retry_after":12}
GET /api/v1/votes/check Did a player vote?

Send username, or steamid for a Steam game. The answer by steamid also has status: 0 = no vote in the last 24 hours, 1 = voted and not claimed yet, 2 = voted and already claimed.

Parameters

usernamestringPlayer name (case-insensitive).
steamidstringA player's steamid64, 17 digits. Used instead of username when sent.

Example request

curl "https://voxelrank.com/api/v1/votes/check?username=Notch&steamid=76561197960287930" \
  -H "X-Api-Key: demo"

Example response 200

{
  "username": "Notch",
  "voted_recently": true,
  "last_vote": 1790000000,
  "total": 140,
  "month": 27
}

Errors

  • 400 steamid is not 17 digits. {"error":"bad_steamid"}
  • 401 Missing or wrong key. {"error":"invalid_key"}
  • 429 More than 120 requests a minute. Wait retry_after seconds (also in the Retry-After header). {"error":"rate_limited","retry_after":12}

Players

Per-player lookups for the in-game plugin. Needs a real key.

GET /api/v1/player/votes Which listings a player voted for, and when they can vote again

Parameters

username *stringPlayer name.
limitinteger (1–200, default 100)1 to 200. Default 100.

Example request

curl "https://voxelrank.com/api/v1/player/votes?username=Notch" \
  -H "X-Api-Key: YOUR_KEY"

Example response 200

{
  "username": "Notch",
  "cooldown_hours": 24,
  "servers": [
    {
      "id": 8685,
      "name": "Pear SMP",
      "slug": "pear-smp",
      "votes": 27,
      "last_vote": 1790000000,
      "cooldown_left": 3600,
      "cooldown_hours": 24,
      "can_vote_now": false
    }
  ]
}

Errors

  • 401 Missing or wrong key. {"error":"invalid_key"}
  • 403 The demo key is not accepted here. {"error":"demo_key_not_allowed"}
  • 429 More than 120 requests a minute. Wait retry_after seconds (also in the Retry-After header). {"error":"rate_limited","retry_after":12}
GET /api/v1/referral/check Did this player come to your server from a listing interaction?

Parameters

username *stringPlayer name.
ipstringOptional: the address the player connects from, to corroborate a join click. Never returned.

Example request

curl "https://voxelrank.com/api/v1/referral/check?username=Notch" \
  -H "X-Api-Key: YOUR_KEY"

Example response 200

{
  "username": "Notch",
  "eligible": true,
  "reason": "ok",
  "source": "vote",
  "seen_ago": 3400,
  "expires_in": 601400,
  "claimed_at": null,
  "corroborated": false,
  "match": "none",
  "ttl_seconds": 604800,
  "disclaimer": "Confirms the username interacted with your listing here, not that the player followed a link. Treat it as a welcome gift, not proof of origin."
}

Errors

  • 401 Missing or wrong key. {"error":"invalid_key"}
  • 429 More than 120 requests a minute. Wait retry_after seconds (also in the Retry-After header). {"error":"rate_limited","retry_after":12}

Public

Listing data anyone can read. No key.

GET /api/servers Search and page through listings

Takes the same filters as the website's own list (copy a filtered list's query string).

Parameters

qstringText search.
tagstringTag slug.
versionstringGame version.
sortstringvotes (default), players, new, rating, …
pageintegerPage, from 1.
onlineOnlystring1 for online listings only.

Example request

curl "https://voxelrank.com/api/servers"

Example response 200

{
  "total": 1,
  "page": 1,
  "pages": 1,
  "servers": [
    {
      "id": 8685,
      "name": "Pear SMP",
      "slug": "pear-smp",
      "ip": "play.pearsmp.net",
      "bedrock": null,
      "apply_url": null,
      "version": "1.21.4",
      "country": "US",
      "tags": [
        "survival",
        "smp"
      ],
      "languages": [
        "en"
      ],
      "online": true,
      "players": 47,
      "max_players": 200,
      "motd": "Pear SMP · Season 3",
      "votes_month": 312,
      "votes_total": 4120,
      "tier": 0,
      "url": "https://voxelrank.com/server/8685-pear-smp",
      "vote_url": "https://voxelrank.com/vote/8685-pear-smp",
      "banner": "https://voxelrank.com/banner/pear-smp.svg"
    }
  ]
}

Errors

  • 403 Past page 25 without a key. {"error":"key_required_for_page","max_page":25}
  • 429 More than 120 requests a minute. Wait retry_after seconds (also in the Retry-After header). {"error":"rate_limited","retry_after":12}
GET /api/server/{slug} One listing by slug

Parameters

slug *string

Example request

curl "https://voxelrank.com/api/server/pear-smp"

Example response 200

{
  "id": 8685,
  "name": "Pear SMP",
  "slug": "pear-smp",
  "ip": "play.pearsmp.net",
  "bedrock": null,
  "apply_url": null,
  "version": "1.21.4",
  "country": "US",
  "tags": [
    "survival",
    "smp"
  ],
  "languages": [
    "en"
  ],
  "online": true,
  "players": 47,
  "max_players": 200,
  "motd": "Pear SMP · Season 3",
  "votes_month": 312,
  "votes_total": 4120,
  "tier": 0,
  "url": "https://voxelrank.com/server/8685-pear-smp",
  "vote_url": "https://voxelrank.com/vote/8685-pear-smp",
  "banner": "https://voxelrank.com/banner/pear-smp.svg",
  "rank": 12,
  "rating": 4.6
}

Errors

  • 404 No such listing. {"error":"not_found"}
GET /api/servers/status Online state and player counts for up to 100 listings

Parameters

ids *stringComma-separated listing ids.

Example request

curl "https://voxelrank.com/api/servers/status?ids=8685%2C120"

Example response 200

{
  "8685": {
    "online": true,
    "players": 47,
    "max": 200,
    "unreliable": false,
    "unknown": false
  }
}

Plugin setup

The companion plugin turns votes cast here into rewards on your server. It polls for unclaimed votes every 60 seconds and runs the reward commands you configure · so votes still arrive even if your server was offline when they were cast.

You do not need the plugin at all if you use Votifier: fill in the Votifier details on your server's edit page and votes are delivered directly, no plugin involved.

Download VotingSite-1.3.0.jar
Version
1.3.0
Size
208 KB
Built
2026-10-01
Requires
Paper 1.21+, Java 21
Verify what you downloaded

SHA-256 of VotingSite-1.3.0.jar:

# Linux / macOS
sha256sum VotingSite-1.3.0.jar

# Windows PowerShell
Get-FileHash VotingSite-1.3.0.jar -Algorithm SHA256

Or download the checksum file and run sha256sum -c VotingSite-1.3.0.jar.sha256. If the hash does not match, do not run the jar.

What it actually does

  • Vote delivery. Pulls unclaimed votes from the site on a timer and catches up on anything missed while the server was offline (mode: poll, works behind NAT/proxies), or has the site push each vote instantly over Votifier (mode: votifier, needs NuVotifier) · or run mode: both and let it de-duplicate by vote id.
  • Rewards. Console commands on every vote, repeating and one-off milestones by all-time vote count, a shared vote-party threshold for the whole server, and a monthly top-3 payout · all in config.yml, no other plugin required.
  • Offline voters. A vote cast while the player is offline is queued and paid out a few seconds after they next join, instead of being lost.
  • Other vote sites. List the other server lists you are on under vote-sites: each gets a clickable line in /voxelrank vote and, if you want, its own rewards. Their votes come over Votifier, through NuVotifier or the plugin's own listener (votifier-listener). VoxelRank itself needs no Votifier port.
  • Welcome kit. A small one-off gift for a player who voted for you or was referred through your listing in the last week, claimable once per player every 30 days. Off by default, and meant as a starter pack · not something worth farming.
  • In-game server browser. /voxelrank browse opens a chest GUI listing every server on the site, filtered and sorted the same way the website is, with the player's own vote streak shown in the corner.
  • Instant-join transfers. On Paper 1.20.5+, a player can be sent straight onto another opted-in server from the browser with one click instead of copying an IP, but only when both servers agree and the destination's own server.properties allows it. Everywhere that cannot happen (older versions, Bedrock, Realms) it falls back to a click-to-copy address, so the browser is never blocked on a feature most player clients cannot use.
  • Optional reporting. Everything under report: in explorer.yml is off by default and toggled separately: player counts, TPS, top balances (via Vault), and a sampled, name-stripped slice of public chat your listing can show visitors. No player data leaves your server unless you switch each one on yourself.
How this differs from classic Votifier

NuVotifier is a transport: it receives a vote pushed from a site and fires an event, which is still exactly what powers this plugin's votifier mode. What it does not do is decide what happens next · that is usually a separate reward plugin, or several, each with their own load order to get right.

This plugin is both halves in one jar, and does not need Votifier at all if you would rather not run it: poll mode pulls votes straight from the site's API, so there is no push to miss and nothing extra to install. Run both and it uses whichever one lands the vote first, and never pays out twice.

Rewards, milestones, vote parties, the monthly top-3, the offline queue, the in-game server browser, transfers and the welcome kit are one plugin's worth of config · not four plugins' worth of compatibility and load order to get the same result.

The one thing it deliberately will not do: let your own server report a vote as cast. That claim cannot be trusted from the server that benefits from it, Votifier or not · so /voxelrank vote always sends the player to vote here, against our captcha and fraud checks, no matter which delivery mode you run.

If you fill in Votifier details on your server's edit page, votes are pushed to your server the instant they are cast (v2 token or v1 RSA, auto-detected) · no polling needed. The REST API stays available as a fallback for votes cast while the server was offline.

Reference implementation: VotingSite-1.3.0.jar (208 KB, Paper 1.21+, Java 21) · it speaks exactly the endpoints below. SHA-256.

Rust: keep the vote plugin you already use

EasyVote (umod) and EasyVoteExtended (codefling) are the most used Rust vote-reward plugins that let you add your own vote site. VoxelRank answers them in the same format as rust-servers.net, so you add VoxelRank to the config you already have and your rewards stay as they are.

EasyVote

Add VoxelRank to both sections of oxide/config/EasyVote.json (carbon/configs/EasyVote.json on Carbon), next to the sites already there. Then run oxide.reload EasyVote. Players type /claim after voting, as before.

"Servers": {
  "ServerName1": {
    "VoxelRank": "123:your-api-key"
  }
},
"VoteSitesAPI": {
  "VoxelRank": {
    "API Claim Reward (GET URL)": "https://voxelrank.com/api/compat?action=custom&object=plugin&element=reward&key={0}&steamid={1}",
    "API Vote status (GET URL)": "https://voxelrank.com/api/compat?object=votes&element=claim&key={0}&steamid={1}",
    "Vote link (URL)": "https://voxelrank.com/vote/{0}"
  }
}

EasyVoteExtended

The same in oxide/config/EasyVoteExtended.json, where the sections have longer names. Then run oxide.reload EasyVoteExtended.

"Server Voting IDs and Keys": {
  "ServerName1": {
    "VoxelRank": "123:your-api-key"
  }
},
"Voting Sites API Information": {
  "VoxelRank": {
    "API Claim Reward (GET URL)": "https://voxelrank.com/api/compat?action=custom&object=plugin&element=reward&key={0}&steamid={1}",
    "API Vote status (GET URL)": "https://voxelrank.com/api/compat?object=votes&element=claim&key={0}&steamid={1}",
    "Vote link (URL)": "https://voxelrank.com/vote/{0}",
    "Site Uses Username Instead of Player Steam ID?": "false"
  }
}

Replace 123 with your listing number (the number at the start of your listing's address, /server/123-…) and your-api-key with the API key from your listing's edit page. Leave {0} and {1} as they are: the plugin fills them in.

VoxelRank answers with a plain 0, 1 or 2, like rust-servers.net: 0 = no vote in the last 24 hours, 1 = voted and not claimed yet, 2 = already claimed. A vote is claimed in one step, so it can never pay out twice.

Would you rather use a plugin made for VoxelRank? Ours is below. It is free, and it also rewards players who voted by typing their name instead of signing in with Steam.

Vote rewards for Rust

A free plugin for Oxide and Carbon. Players vote on your vote page, signed in with Steam or by typing their name, then type /claim in game to get the reward. /vote shows them the link.

Download VoxelRank.cs

  1. Put VoxelRank.cs in oxide/plugins (Oxide) or carbon/plugins (Carbon).
  2. Open the config it creates (oxide/config/VoxelRank.json or carbon/configs/VoxelRank.json) and paste your listing's API key from its edit page.
  3. Set the rewards: console commands ({steamid} and {name} are filled in for you) and items.
  4. Reload the plugin with oxide.reload VoxelRank or c.reload VoxelRank.

Config example

{
  "API key (from your listing's edit page)": "your-api-key",
  "Site URL": "https://voxelrank.com",
  "Vote page URL (empty = ask the site)": "",
  "Seconds a player must wait between /claim uses": 15,
  "Accept votes cast with a typed name (players without Steam sign-in)": true,
  "Tell the whole server when someone claims": true,
  "Console commands per vote ({steamid} and {name} are replaced)": [
    "inventory.giveto {steamid} scrap 25"
  ],
  "Items per vote": [
    {
      "Item short name": "scrap",
      "Amount": 50,
      "Skin ID (0 = none)": 0
    }
  ]
}

The API calls it makes

GET  https://voxelrank.com/api/v1/votes/check?steamid=76561197960287930
POST https://voxelrank.com/api/v1/votes/claim   {"steamid":"76561197960287930"}
POST https://voxelrank.com/api/v1/votes/claim   {"username":"Player123"}
X-Api-Key: your-api-key

The status field in the answer: 0 = no vote in the last 24 hours, 1 = voted and not claimed yet, 2 = voted and already claimed. A claim marks the votes in one step, so two claims at the same moment can never both give a reward.

Players who vote by typing their name are found by that name, and only votes made without Steam sign-in match it. Turn this off in the config if you only want Steam votes.

Vote rewards for other Steam games

  • ARK: the ARK vote-reward plugins we checked (Foppa's Vote Rewards, Vote Rewards by Michidu, SH Vote Rewards) only talk to the vote sites built into them, so none of them can be pointed at VoxelRank. Use the API below.
  • CS2: we found no vote-reward plugin or mod that lets you choose the vote site's address. Use the API below.
  • Valheim: we found no vote-reward plugin or mod that lets you choose the vote site's address. Use the API below.
  • Palworld: we found no vote-reward plugin or mod that lets you choose the vote site's address. Use the API below.
  • DayZ: we found no vote-reward plugin or mod that lets you choose the vote site's address. Use the API below.

Voters of ARK, CS2, Valheim, Palworld and DayZ can sign in with Steam too, so each vote carries their SteamID. We have no ready plugin for these games yet, but your own mod, script or Discord bot can give the reward with two calls to our API.

  1. Check the player: GET /api/v1/votes/check?steamid=… with your listing's API key in the X-Api-Key header.
  2. If status is 1, claim the votes: POST /api/v1/votes/claim with {"steamid": "…"}. Give the reward only when claimed is above 0.
  3. A player who typed a name instead of signing in is claimed with {"username": "…"}.
GET https://voxelrank.com/api/v1/votes/check?steamid=76561197960287930
X-Api-Key: your-api-key

{"steamid":"76561197960287930","voted_recently":true,"last_vote":1767225600,"total":4,"month":2,"status":1}

POST https://voxelrank.com/api/v1/votes/claim
X-Api-Key: your-api-key
Content-Type: application/json

{"steamid":"76561197960287930"}

{"steamid":"76561197960287930","claimed":1,"ids":[1042],"status":1}

The status field in the answer: 0 = no vote in the last 24 hours, 1 = voted and not claimed yet, 2 = voted and already claimed. A claim marks the votes in one step, so two claims at the same moment can never both give a reward.

Any plugin, mod or bot that lets you enter a vote site address in the rust-servers.net format works for these games too. Use these two addresses:

GET https://voxelrank.com/api/compat?object=votes&element=claim&key=your-api-key&steamid=76561197960287930
GET https://voxelrank.com/api/compat?action=custom&object=plugin&element=reward&key=your-api-key&steamid=76561197960287930

VoxelRank answers with a plain 0, 1 or 2, like rust-servers.net: 0 = no vote in the last 24 hours, 1 = voted and not claimed yet, 2 = already claimed. A vote is claimed in one step, so it can never pay out twice.

Player count for Terraria and Palworld

The site cannot read a player count from these two games on its own, so their listings show "Unknown". A small program on your server sends the count to VoxelRank once a minute: the number of players and the slot limit, nothing else. VoxelRank uses it only while reports keep arriving, and never over a count it could measure itself.

Terraria

Put VoxelRank.dll in the ServerPlugins folder and restart the server. It writes tshock/voxelrank.json on the first start. Add your API key there and run /voxelrank reload.

VoxelRank.dll VoxelRankPlugin.cs

Palworld

Turn on the REST API in PalWorldSettings.ini (RESTAPIEnabled=True and an AdminPassword), keep its port closed to the internet, and run the Python program on the same machine. It creates its config file on the first run.

voxelrank_palworld.py

POST https://voxelrank.com/api/v1/heartbeat
X-Api-Key: your-api-key
Content-Type: application/json

{"players":3,"max_players":32}

Banners

Vote banner image (468x60). Add ?small=1 for 350x20.

https://voxelrank.com/banner/<slug>.svg
https://voxelrank.com/banner/<slug>/topvoters.svg
https://voxelrank.com/banner/<slug>/badge.svg