Vote rewards
Vote Rewards API
Integrate your Palworld server with voting rewards through webhook callbacks or polling endpoints.
Overview
The vote rewards system lets you grant in-game rewards after players vote for your server. Choose Push to receive a webhook immediately when a vote is cast, or Pull to have your plugin poll for available votes.
- Push (webhook): PalworldServers.io sends a POST request to your callback URL.
- Pull (polling): Your integration checks API endpoints and claims processed votes.
Base URL: https://palworldservers.io
Authentication
Version 1 vote endpoints authenticate your server with its server key. Find this key in your server's dashboard and keep it private: anyone with the key can access the vote endpoints for that server.
Vote Link
Send players to this URL, replacing both placeholders for your integration.
https://palworldservers.io/server/{serverId}/vote?player_id={PLAYER_ID}The player_id from the URL is sent to your webhook and stored with the vote. When vote rewards are enabled and no player ID is provided, the voter is shown a modal to enter it.
Push: Webhook Callbacks
When a vote happens, we POST this payload to your configured callback URL.
{
"server": "server-id",
"playerId": "PLAYER_ID",
"user": "optional-website-user-id",
"type": "vote",
"timestamp": "2026-01-01T12:00:00.000Z"
}| Header | Value |
|---|---|
| Content-Type | application/json |
| Authorization | Your configured secret |
| X-Webhook-Signature | HMAC-SHA256 signature of the request body |
| User-Agent | PalworldServers-Vote-Webhook/1.0 |
Verify the signature
import crypto from 'crypto'
function verifySignature(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
)
}
const isValid = verifySignature(
rawRequestBody,
req.header('X-Webhook-Signature'),
process.env.WEBHOOK_SECRET
)import hashlib
import hmac
def verify_signature(raw_body, signature, secret):
expected = hmac.new(
secret.encode('utf-8'),
raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(signature, expected)
is_valid = verify_signature(
request.get_data(),
request.headers['X-Webhook-Signature'],
os.environ['WEBHOOK_SECRET'],
)Your endpoint must return a 2xx response after processing the event. Failed deliveries are not retried automatically; their failure count is tracked in the dashboard.
Pull: Polling API Endpoints
Poll these v1 endpoints from your plugin or service to process rewards.
/api/v1/vote/check/{serverKey}/{playerId}Check whether a specific player has voted.
{
"voted": true,
"lastVote": "2026-01-01T12:00:00.000Z",
"canVote": false,
"nextVote": "2026-01-02T00:00:00.000Z",
"isClaimed": false
}/api/v1/vote/unclaimed/{serverKey}?limit=50Get unclaimed votes with player IDs. limit accepts 1–100 and defaults to 50.
{
"server": "server-id",
"votes": [{ "id": "vote-id", "playerId": "PLAYER_ID", "votedAt": "2026-01-01T12:00:00.000Z" }]
}/api/v1/vote/mark-claimed/{serverKey}Mark votes as claimed only after granting their rewards. Submit at most 100 IDs.
{ "voteIds": ["vote-id-1", "vote-id-2"] }{ "success": true, "claimed": 2 }/api/v1/vote/leaderboard/{serverKey}Get the current month's vote leaderboard.
{
"server": { "id": "server-id", "name": "My Server" },
"period": { "month": "2026-01" },
"leaderboard": [{ "rank": 1, "username": "Player", "votes": 12, "isRegistered": true }]
}Code Examples
local http = require('socket.http')
local json = require('json')
local serverKey = 'YOUR_SERVER_KEY'
local playerId = 'PLAYER_ID'
local url = 'https://palworldservers.io/api/v1/vote/check/' .. serverKey .. '/' .. playerId
local body, status = http.request(url)
if status == 200 then
local vote = json.decode(body)
if vote.voted and not vote.isClaimed then
grantVoteReward(playerId)
-- Mark the vote as claimed with POST /mark-claimed/{serverKey}
end
endimport requests
SERVER_KEY = 'YOUR_SERVER_KEY'
player_id = 'PLAYER_ID'
url = f'https://palworldservers.io/api/v1/vote/check/{SERVER_KEY}/{player_id}'
response = requests.get(url, timeout=10)
response.raise_for_status()
vote = response.json()
if vote['voted'] and not vote['isClaimed']:
grant_vote_reward(player_id)
# Mark the matching vote ID claimed after granting the reward.import express from 'express'
import crypto from 'crypto'
const app = express()
app.use(express.raw({ type: 'application/json' }))
app.post('/webhooks/palworld-vote', (req, res) => {
const signature = req.header('X-Webhook-Signature')
const expected = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(req.body)
.digest('hex')
if (!signature || !crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'))) {
return res.sendStatus(401)
}
const event = JSON.parse(req.body.toString('utf8'))
if (event.type === 'vote') grantVoteReward(event.playerId)
return res.sendStatus(204)
})Rate Limits
- Standard API limit: 100 requests per minute per IP address.
- Vote cooldown: 12 hours per user, per server.