Players vote much more often when they get something in game for it. Connect vote rewards to your Java or PTS server in 10–15 minutes: your project climbs the monthly TOP, and players get items for every vote.
Quick start
Get Server ID and API key
Your server must be listed on L2Servers.top. Write to us — we send the ID, the secret key and your vote page link.
Contact usInstall the reward script
One PHP file for your website — Java and PTS in the same script. It receives our signed request after every vote and gives items to the character.
⬇ l2s-vote-reward.phpTest and go live
Send a test vote with the tool below, then put the vote button on your website and launcher. We mark your server with 🎁 on the vote page.
Open test toolhttps://l2servers.top/vote/{server}/1 vote / 12 h per IP and characterHMAC-SHA2561st day of monthJava servers: L2J, L2J Mobius, aCis, L2Scripts, Lucera
Two ways — use one or both:
A. Automatic reward (callback) — recommended
- Upload l2s-vote-reward.php to your website (PHP 7.2+ with pdo_mysql, access to the game database).
- Set server_type = java, the database and the reward mode:
- custom_mail — L2J Mobius: items come by in-game mail, online or offline (General.ini → CustomMailManagerEnabled = True).
- items_delayed — L2Scripts, Lucera, Rebellion, Overworld: the item is given on the next login.
- sql — aCis and custom packs: your own INSERT/UPDATE (for example vote points in your donate shop).
- Open ?test=1&char=YourChar on your script — it checks the connection, tables and character lookup.
- Send us the callback URL:
https://your-site.com/l2s-vote-reward.php?user={username}&ip={ip}&time={time}&sig={sig}'api_key' => 'YOUR_API_KEY', 'server_type' => 'java', 'db_dsn' => 'mysql:host=127.0.0.1;port=3306;dbname=l2jgs;charset=utf8mb4', 'db_user' => 'l2j', 'db_pass' => 'secret', 'mode' => 'custom_mail', 'items' => array( array( 57, 1000000 ), array( 1538, 1 ) ), // Adena + Blessed Scroll of EscapeB. In-game command .vote
The player votes on our site and types .vote in game. The server asks our API whether this character (or IP) voted in the last 12 hours and gives the reward once per vote. Works without a website — only the game server is needed.
⬇ L2ServersVote.java (L2J Mobius)
For aCis / L2Scripts / Lucera keep the same logic and change three things: the voiced command interface, the "add item" call and where the last claimed vote is stored.
PTS servers (C++, MSSQL)
On PTS you usually cannot add a Java-style command, and writing items directly into user_item while the character is online is unsafe. That is why the reward script works with PTS through the database — it finds the character in lin2world.dbo.user_data and hands the reward to a system that your server already uses:
PTS config example (PHP needs pdo_sqlsrv on Windows or pdo_dblib on Linux):
'api_key' => 'YOUR_API_KEY', 'server_type' => 'pts', 'db_dsn' => 'sqlsrv:Server=127.0.0.1,1433;Database=lin2world', 'db_user' => 'sa', 'db_pass' => 'secret', 'mode' => 'sql', 'items' => array( array( 57, 1000000 ) ), // 1) extender delivery table (check the name in your extender docs): 'custom_sql' => 'INSERT INTO ItemDelivery (char_id, item_id, item_amount) VALUES (:char_id, :item_id, :count)', // 2) or vote points on the website (separate DB connection): // 'custom_sql' => 'UPDATE site_accounts SET vote_points = vote_points + :count WHERE login = :account', // 'sql_dsn' => 'mysql:host=127.0.0.1;dbname=website;charset=utf8mb4',PTS and the .vote command: if your extender supports custom commands or scripts, it can call the same API as the Java example (/api/vote-check?username=…) — the logic is identical.
API reference
Callback (we call you)
After every accepted vote we send one GET request to your callback URL (timeout 3 s, no retries). Placeholders:
| Placeholder | Value |
|---|---|
{username} | Character name entered by the player (letters, digits, _ and -; may be empty) |
{ip} | Voter IP address |
{time} | Unix time of the vote (UTC) |
{sig} | hex( HMAC-SHA256( username + "|" + ip + "|" + time, API_KEY ) ) |
Verify it yourself (the ready script already does this and rejects old and repeated requests):
// PHP $expected = hash_hmac('sha256', $_GET['user'] . '|' . $_GET['ip'] . '|' . $_GET['time'], $apiKey); if (!hash_equals($expected, $_GET['sig']) || abs(time() - (int) $_GET['time']) > 600) { http_response_code(403); exit; } // Java Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(apiKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); String expected = HexFormat.of().formatHex(mac.doFinal((user + "|" + ip + "|" + time).getBytes(StandardCharsets.UTF_8)));Vote check (you call us)
GET https://l2servers.top/api/vote-check?server=SERVER_ID&key=API_KEY&username=CharName GET https://l2servers.top/api/vote-check?server=SERVER_ID&key=API_KEY&ip=1.2.3.4| HTTP | Response |
|---|---|
200 | {"voted":true,"voted_at":"2026-09-25T10:15:00+00:00","next_vote_at":"2026-09-25T22:15:00+00:00"} |
200 | {"voted":false,"voted_at":null,"next_vote_at":null} |
400 | {"error":"bad_request","message":"Pass username or ip."} |
403 | {"error":"invalid_key","message":"Invalid server ID or API key."} |
Store the last voted_at you rewarded for the account, so one vote gives exactly one reward. Keep the API key secret — use it only on your server, never in the game client or JavaScript.
Test tool
Check your key, see if a character has voted, and send a real signed test request to your script — no need to vote yourself.
Vote button for your website
![]()
<a href="https://l2servers.top/vote/YOUR-SERVER/" target="_blank"><img src="https://l2servers.top/vote-button.svg" width="88" height="31" alt="Vote for us on L2Servers.top"></a>Replace YOUR-SERVER with your address on L2Servers.top (for example /vote/forceplay/). Add ?username=Name to prefill the character name — useful in the launcher and in the website account page.
FAQ
Is it free?
Yes. Votes, the API and the reward script are free for every listed server.
The callback does not arrive
Use a normal domain with https:// (443) or http:// (80), no IP addresses or custom ports, and answer within 3 seconds. Check with the test tool — it shows the HTTP status and the first 300 characters of your answer.
"expired" in the answer
The clock on your web server is wrong by more than 10 minutes. Enable time sync (NTP).
Can players cheat with many characters?
No: one vote per 12 hours per IP address and per character name, plus bot protection. For extra safety give the reward once per account (the Java example does this).
Can I change the key?
Yes — write to us and we generate a new one; the old key stops working immediately.