Добавить сервер Войти

API для владельцев: награды за голоса, виджеты, статистика

Игроки голосуют за ваш сервер на Онлайномере, а вы выдаёте им награду в игре. Это делается тремя способами на выбор: запросом к API при заходе игрока, периодическим опросом очереди или вебхуком, который мы вызываем в момент голоса.

Быстрый старт

  1. Добавьте сервер и подтвердите владение.
  2. В кабинете откройте «Голосование и API» и нажмите «Создать ключ» — он показывается один раз, в базе хранится только хэш.
  3. Дайте игрокам ссылку на голосование: https://onlinomer.ru/vote/5/ (команда /vote в игре, кнопка на сайте).
  4. При заходе игрока вызовите POST /api/v1/votes/claim — в ответе будет, сколько наград выдать.

Авторизация

Все методы для владельца требуют ключ сервера в заголовке. Ключ привязан к одному серверу, id сервера передавать не нужно.

Authorization: Bearer onl_0123456789abcdef0123456789abcdef01234567
# или
X-Api-Key: onl_0123456789abcdef0123456789abcdef01234567

Ответы — JSON в UTF-8, у каждого есть поле ok. Ошибка: {"ok": false, "error": {"code": "...", "message": "..."}} с кодом HTTP 4xx. Лимит — 300 запросов в минуту на сервер (ответ 429). Время везде в ISO 8601, UTC; day — московские сутки голоса.

Награды за голоса

Голос засчитывается в сутки один раз на ник. Награда считается невыданной, пока вы её не заберёте через claim или ack. Невостребованные голоса хранятся в очереди 30 дней.

МетодЧто делает
GET /api/v1/votes/check?nick=SteveГолосовал ли ник сегодня и сколько наград ждёт: voted_today, pending_rewards. Ничего не списывает — удобно для подсказки «проголосуй и получи приз».
POST /api/v1/votes/claim nick=SteveГлавный метод. Атомарно забирает все невыданные награды ника и возвращает их. Повторный вызов вернёт claimed: 0 — выдать награду дважды нельзя.
GET /api/v1/votes/pending?limit=100Очередь невыданных наград всех игроков — для плагина, который раз в минуту проверяет очередь.
POST /api/v1/votes/ack ids=1,2,3Отметить голоса выданными (после pending). Можно JSON: {"ids":[1,2,3]}.
GET /api/v1/votes?after_id=0&limit=100История голосов постранично (next_after_id — курсор). Фильтры: nick, pending=1.
GET /api/v1/serverДанные вашего сервера: онлайн, голоса, место.

Пример: выдача награды при заходе игрока

curl -X POST https://onlinomer.ru/api/v1/votes/claim \
  -H "Authorization: Bearer $ONLINOMER_KEY" \
  -d nick=Steve

{
  "ok": true,
  "nick": "Steve",
  "claimed": 2,
  "votes": [
    {"id": 101, "nick": "Steve", "day": "2026-10-17", "voted_at": "2026-10-17T14:02:11+00:00", "claimed": true, "claimed_at": "2026-10-18T09:00:03+00:00"},
    {"id": 118, "nick": "Steve", "day": "2026-10-18", "voted_at": "2026-10-18T08:44:50+00:00", "claimed": true, "claimed_at": "2026-10-18T09:00:03+00:00"}
  ]
}

Выдайте награду claimed раз.

Minecraft (Java-плагин, Bukkit/Paper)

@EventHandler
public void onJoin(PlayerJoinEvent e) {
  String nick = e.getPlayer().getName();
  Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
    HttpRequest req = HttpRequest.newBuilder(URI.create("https://onlinomer.ru/api/v1/votes/claim"))
      .header("Authorization", "Bearer " + KEY)
      .header("Content-Type", "application/x-www-form-urlencoded")
      .POST(HttpRequest.BodyPublishers.ofString("nick=" + URLEncoder.encode(nick, UTF_8))).build();
    String json = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString()).body();
    int n = JsonParser.parseString(json).getAsJsonObject().get("claimed").getAsInt();
    if (n > 0) Bukkit.getScheduler().runTask(plugin, () -> giveRewards(e.getPlayer(), n));
  });
}

GTA 5 RP (FiveM, Lua)

AddEventHandler('playerJoining', function()
  local src, nick = source, GetPlayerName(source)
  PerformHttpRequest('https://onlinomer.ru/api/v1/votes/claim', function(code, body)
    if code == 200 then
      local r = json.decode(body)
      if r.claimed > 0 then GiveVoteReward(src, r.claimed) end
    end
  end, 'POST', 'nick=' .. nick, {
    ['Authorization'] = 'Bearer ' .. Config.OnlinomerKey,
    ['Content-Type'] = 'application/x-www-form-urlencoded'
  })
end)

Опрос очереди (любой язык)

import requests, time
H = {"Authorization": "Bearer " + KEY}
while True:
    votes = requests.get("https://onlinomer.ru/api/v1/votes/pending", headers=H).json()["votes"]
    given = [v["id"] for v in votes if give_reward_if_online(v["nick"])]
    if given:
        requests.post("https://onlinomer.ru/api/v1/votes/ack", headers=H, json={"ids": given})
    time.sleep(60)

Вебхук: мы звоним вам в момент голоса

Укажите адрес в кабинете — на каждый голос мы отправим POST с JSON:

{
  "event": "vote",
  "vote_id": 118,
  "server_id": 5,
  "nick": "Steve",
  "day": "2026-10-18",
  "voted_at": "2026-10-18T08:44:50+00:00",
  "votes_month": 124,
  "votes_total": 1860
}

Заголовки: X-Onlinomer-Event (vote или ping), X-Onlinomer-Timestamp (секунды) и X-Onlinomer-Signature: sha256=… — HMAC-SHA256 от строки timestamp + "." + тело с вашим секретом из кабинета. Проверяйте подпись и отклоняйте события старше 5 минут.

<?php // PHP
$body = file_get_contents('php://input');
$ts   = $_SERVER['HTTP_X_ONLINOMER_TIMESTAMP'];
$sig  = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, SECRET);
if (!hash_equals($sig, $_SERVER['HTTP_X_ONLINOMER_SIGNATURE']) || abs(time() - $ts) > 300) { http_response_code(403); exit; }
$vote = json_decode($body, true); // выдайте награду $vote['nick'], запомните $vote['vote_id']

Ответьте кодом 2xx в течение 5 секунд. Иначе повторим через 1 минуту, 5 минут, 30 минут, 2 часа и 12 часов (всего 6 попыток), поэтому храните vote_id и не выдавайте награду дважды. Вебхук можно сочетать с claim: пришёл вебхук — выдали сразу, а при заходе игрока claim подберёт то, что не дошло. Адрес должен вести на публичный сервер (внутренние адреса и редиректы не поддерживаются).

Публичные данные (без ключа)

Открыты для запросов из браузера (CORS), кэшируются на 30 секунд.

GET https://onlinomer.ru/api/v1/servers/5GET https://onlinomer.ru/api/v1/servers/5/votes?period=month&limit=10   # лучшие голосующие (month | all)
GET https://onlinomer.ru/api/v1/servers/5/reviews?page=1&limit=10         # отзывы игроков: оценки, тексты, ответы владельца

Подойдёт для таблицы лидеров на вашем сайте или в игре: онлайн, место по голосам, топ ников.

Статистика

В кабинете на странице «Статистика» видно, сколько раз сервер показали в каталоге, открыли страницу, скопировали IP, нажали «Играть», перешли по вашим ссылкам, откуда пришли люди и сколько проголосовало. Всё это же отдаёт GET /api/v1/stats?days=30 (1–90 дней): сводка totals, по дням daily, переходы по ссылкам links и источники referrers.

Как считаем: боты не учитываются; «уникальные» — по дневному хэшу без cookie и без хранения IP; копирование IP, «Играть» и переходы по каждой ссылке считаются раз в сутки на посетителя, чтобы клики нельзя было накрутить.

Виджеты и бейджи для вашего сайта

Карточка с онлайном, голосами и кнопкой «Проголосовать»:

<a href="…адрес страницы вашего сервера на Онлайномере…" data-onlinomer>Наш сервер — онлайн на Онлайномере</a>
<script src="https://onlinomer.ru/embed.js" data-server="5" data-theme="auto" async></script>
<!-- или без скрипта -->
<iframe src="https://onlinomer.ru/widget/5/" width="320" height="200" style="border:0" loading="lazy"></iframe>

Ссылку <a data-onlinomer> оставьте: скрипт ставит виджет над ней, а сама ссылка остаётся подписью. Готовый код со ссылкой на страницу вашего сервера — в кабинете («Виджеты»).

Бейдж для README, форума, Discord: <a href="…страница сервера…"><img src="https://onlinomer.ru/badge/5.svg"></a>, Markdown: [![Онлайномер](https://onlinomer.ru/badge/5.svg)](ссылка), BBCode: [url=ссылка][img]https://onlinomer.ru/badge/5.svg[/img][/url]. Параметр ?type=votes (по умолчанию), online или rank. Тема виджета: light, dark, auto.

Открытые данные и атрибуция

Публичные данные Онлайномера — онлайн, аптайм, рейтинг — можно использовать на своих сайтах, в ботах и исследованиях по лицензии CC BY 4.0: со ссылкой на Онлайномер (onlinomer.ru) рядом с данными. Список серверов игры: GET /api/v1/servers?game=mc&limit=50&page=1, сервер: GET /api/v1/servers/{id}, таблица топ-500: /data/top.csv.

Правила голосования

Вопросы по API — в поддержку.