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-Keyheader, akeyquery parameter or akeyfield 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
demoon 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_afterseconds (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
days | integer (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_afterseconds (also in the Retry-After header).{"error":"rate_limited","retry_after":12}
GET /api/v1/top Top voters of a month
Parameters
month | string | Month, YYYY-MM (UTC). Default: this month. |
limit | integer (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
monthis notYYYY-MM.{"error":"bad_month"} - 401 Missing or wrong key.
{"error":"invalid_key"} - 429 More than 120 requests a minute. Wait
retry_afterseconds (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
after | integer | Only votes with a larger id. Default 0 (from the start). |
since | integer | Only votes cast at or after this unix time. |
limit | integer (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_afterseconds (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
limit | integer (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_afterseconds (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
steamidis not 17 digits, orusernameis too long.{"error":"bad_steamid"} - 401 Missing or wrong key.
{"error":"invalid_key"} - 429 More than 120 requests a minute. Wait
retry_afterseconds (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
username | string | Player name (case-insensitive). |
steamid | string | A 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
steamidis not 17 digits.{"error":"bad_steamid"} - 401 Missing or wrong key.
{"error":"invalid_key"} - 429 More than 120 requests a minute. Wait
retry_afterseconds (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 * | string | Player name. |
limit | integer (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_afterseconds (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 * | string | Player name. |
ip | string | Optional: 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_afterseconds (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
q | string | Text search. |
tag | string | Tag slug. |
version | string | Game version. |
sort | string | votes (default), players, new, rating, … |
page | integer | Page, from 1. |
onlineOnly | string | 1 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_afterseconds (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 * | string | Comma-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.
- 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 runmode: bothand 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 voteand, 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 browseopens 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.propertiesallows 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:inexplorer.ymlis 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.
- Put VoxelRank.cs in oxide/plugins (Oxide) or carbon/plugins (Carbon).
- 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.
- Set the rewards: console commands ({steamid} and {name} are filled in for you) and items.
- 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.
- Check the player: GET /api/v1/votes/check?steamid=… with your listing's API key in the X-Api-Key header.
- If status is 1, claim the votes: POST /api/v1/votes/claim with {"steamid": "…"}. Give the reward only when claimed is above 0.
- 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.
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