API

Publiczne endpointy do odczytu

Wszystkie endpointy odczytu zwracają JSON i są dostępne bez klucza, bez logowania i bez rejestracji. Adres bazowy to ta sama domena, na której czytasz tę stronę.

Endpointy odczytu wysyłają nagłówek Access-Control-Allow-Origin: *, więc możesz z nich korzystać przez fetch() bezpośrednio z przeglądarki, z dowolnej domeny. Endpointy zapisu (dodawanie i usuwanie serwera z listy) tego nagłówka nie mają — celowo.

Zanim na tym zbudujesz To projekt hobbystyczny, nie usługa komercyjna. Nie ma żadnego SLA: serwis może zniknąć, zmienić kształt odpowiedzi albo przestać działać bez zapowiedzi i bez okresu przejściowego. Dane pochodzą z publicznego API FiveM i są tak dobre jak ono. Jeśli budujesz coś, na czym Ci zależy — trzymaj własny cache i przewiduj, że tego API pewnego dnia nie będzie.

Dashboard

GET/api/stats

Aktualny stan wszystkich śledzonych serwerów plus suma zbiorcza. Dane pochodzą z pamięci podręcznej odświeżanej co POLL_INTERVAL_MS (domyślnie 20 s), więc odpytywanie częściej niż co 20 s nic nie wnosi.

Przykładowe żądanie
curl https://stats.bykens.eu/api/stats
Przykładowa odpowiedź (skrócona do jednego serwera)
{
  "servers": [
    {
      "code": "okz5dj",
      "label": "Intouch.gg",
      "hostname": "INTOUCH",
      "banner": "https://cdn-intouch.realmgaming.eu/baner.png",
      "players": 121,
      "max": 230,
      "queue": 2,
      "upvotes": 0,
      "framework": "ESX",
      "resourceCount": 412,
      "updatedAt": "2026-09-14T11:29:10.380Z",
      "trend":     { "players": "flat", "queue": "flat" },
      "peakToday": { "players": 121, "queue": 2 },
      "uptime": {
        "day":  { "percent": 100, "checks": 288 },
        "week": { "percent": 99.4, "checks": 2016 }
      },
      "health": {
        "grade": "A",
        "powody": [],
        "decyduje": "uptime",
        "uptime":   { "znany": true, "powod": null, "percent": 99.4, "checks": 2016, "hours": 168, "band": "ponad-99" },
        "wskaznik": { "znany": true, "powod": null, "status": "brak-wzorca", "rodzinyPotwierdzone": [], "rodzinyObserwowane": [], "observations": 41 }
      }
    }
  ],
  "totals": { "players": 246, "max": 931, "queue": 2 },
  "generatedAt": "2026-09-14T11:29:21.793Z"
}
Pola warte uwagi
PoleZnaczenie
trendup / down / flat — kierunek zmian z ostatnich ~2,5 min, z martwą strefą, żeby nie migał przy szumie
uptime.*.percentProcent udanych odczytów w oknie. null znaczy „za mało danych", co jest czymś innym niż 0
frameworkRozpoznany framework (ESX, QBCore, Qbox, vRP) albo null, gdy żaden marker nie pasuje. Te same reguły co na liście serwerów
resourceCountLiczba resource'ów zgłoszona przez serwer. null, gdy serwer nie udostępnia ich listy
errorObecne tylko gdy serwer nie odpowiada. Wtedy brak players, trend i peakToday
health.gradeOcena zdrowia: A / B / C albo null. A — uptime 7 dni powyżej 99% i żadna rodzina obserwacji licznika nie jest potwierdzona. B — uptime od 95% do 99% i to samo o obserwacjach. C — uptime poniżej 95% albo jakakolwiek rodzina potwierdzona
health.powodyPuste przy ocenie. Gdy grade jest null, wymienia czego brakuje: uptime-brak-odczytow, uptime-za-krotkie-okno (okno krótsze niż doba), wskaznik-bez-rozstrzygniecia. Litera zgadnięta z połowy przesłanek wyglądałaby jak policzona, więc jej nie ma
health.decydujeCo dało tę literę: uptime, wskaznik albo oba. null, gdy oceny nie ma
health.uptime / health.wskaznikUzasadnienie, zawsze obecne — także wtedy, gdy litery nie ma. Obie liczby, długość okna uptime w godzinach (hours), status wskaźnika i lista potwierdzonych rodzin
health.wskaznik.rodzinyObserwowaneRodziny widziane w oknie, także te jeszcze niepotwierdzone. Do oceny nie wchodzą — litera stoi wyłącznie na rodzinyPotwierdzone. Są po to, żeby dało się napisać „widzimy coś, ale to się jeszcze nie utrzymuje" zamiast milczeć tak samo jak przy pustym oknie. Pusta lista, gdy znany jest false
Czego ocena nie mówi: czy serwer jest dobry, uczciwy albo wart grania. uptime to nasza widoczność serwera — przerwa po naszej stronie wygląda tak samo jak awaria serwera. wskaznik opisuje nasze pomiary licznika graczy, nie zachowanie administracji.
Limity: brak rate limitu. Dane odświeżane co ~20 s.
GET/api/kronika

Trwała oś czasu zdarzeń: awaria, powrót, rekord dnia, prawie pełny. Okno 90 dni, najnowsze pierwsze. To samo, co pokazuje strona Historia.

Parametry
ParametrZnaczenie
codeKod cfx.re jednego serwera. Bez parametru — wszystkie
typeonline, offline, full, record-players, record-queue
limitIle wpisów na stronę (domyślnie 50, najwyżej 500)
offsetIle pominąć, licząc od najnowszego
{
  "wpisy": [
    { "id": 812, "at": "2026-09-18T09:14:02.118Z", "type": "offline", "code": "lerb6bv", "label": "Intouch.gg", "value": null }
  ],
  "razem": 137,
  "offset": 0,
  "limit": 50,
  "przeglad": {
    "serwery": [{ "code": "lerb6bv", "label": "Intouch.gg", "ile": 41 }],
    "typy":    [{ "type": "offline", "ile": 22 }],
    "razem": 137,
    "najstarszy": "2026-06-21T18:02:11.004Z",
    "oknoDni": 90,
    "sufit": 10000
  }
}
Różnica wobec /api/events: tamto oddaje ostatnie ~20 zdarzeń z pamięci procesu i pustoszeje przy restarcie usługi — to jest pasek „co się właśnie dzieje”. Kronika idzie z dysku i restart przeżywa.
Limity: brak rate limitu. Okno 90 dni i sufit 10 000 wpisów; starsze odpadają przy zapisie.
GET/api/history

Historia sumy wszystkich serwerów — próbka co HISTORY_INTERVAL_MS (domyślnie 5 min), maksymalnie 288 próbek, czyli pełne 24 h. Z parametrem ?code= zwraca historię jednego serwera (samą tablicę, bez intervalMs).

Przykładowe żądania
curl https://stats.bykens.eu/api/history
curl https://stats.bykens.eu/api/history?code=okz5dj
Przykładowa odpowiedź (bez parametru)
{
  "totals": [
    { "timestamp": "2026-09-14T11:29:10.381Z", "players": 246, "max": 931, "queue": 2 }
  ],
  "intervalMs": 300000
}
ParametrZnaczenie
codeKod serwera z /api/stats. Nieznany kod → 404 z {"error": "Nieznany kod serwera"}
Limity: brak rate limitu. Historia obejmuje maksymalnie ostatnie 24 h i nie przetrwa dłużej — starsze próbki są nadpisywane.
GET/api/hourly

Heatmapa godzin szczytu: 168 komórek (7 dni tygodnia × 24 godziny) ze średnią liczbą graczy i kolejki. Narasta przez cały czas życia serwisu, więc im dłużej chodzi, tym wiarygodniejsza. Komórki bez próbek zwracają null, a nie zero — to celowe rozróżnienie „brak danych" od „zero graczy".

Przykładowe żądanie
curl https://stats.bykens.eu/api/hourly
Przykładowa odpowiedź (2 z 168 komórek)
{
  "cells": [
    { "day": 0, "hour": 0,  "players": null, "queue": null, "samples": 0 },
    { "day": 1, "hour": 13, "players": 246,  "queue": 2,    "samples": 12 }
  ],
  "days": 1,
  "since": "2026-09-14",
  "intervalMs": 300000
}
PoleZnaczenie
dayDzień tygodnia, 0 = niedziela (jak w JavaScriptowym getDay())
samplesLiczba próbek uśrednionych w tej komórce. Przy 1 próbce liczba jest praktycznie bez wartości
daysZ ilu różnych dni w ogóle zebrano dane
Limity: brak rate limitu.
GET/api/events

Ostatnie ~20 zdarzeń (serwer padł, wrócił, zapełnił się, pobił rekord), najnowsze pierwsze. Żyją wyłącznie w pamięci — restart usługi czyści listę i tak ma być: to podgląd ostatnich minut, nie dziennik zdarzeń.

Przykładowe żądanie
curl https://stats.bykens.eu/api/events
Przykładowa odpowiedź
{
  "events": [
    { "id": 7, "at": "2026-09-14T11:31:02.118Z", "type": "record",
      "code": "okz5dj", "label": "Intouch.gg", "value": 231 }
  ]
}
Limity: brak rate limitu. Zaraz po restarcie usługi lista bywa pusta ({"events": []}) — to nie błąd.

Lista serwerów

GET/api/lookup/servers

Wszystkie serwery dodane przez odwiedzających, z kompletem danych wyciągniętych z API FiveM. Czyta wyłącznie z pamięci podręcznej — Twoje żądanie nigdy nie wywoła zapytania do cfx.

Przykładowe żądanie
curl https://stats.bykens.eu/api/lookup/servers
Przykładowa odpowiedź (skrócona)
{
  "servers": [
    {
      "code": "yokb5k",
      "hostname": "Realm Roleplay | WL-OFF",
      "projectName": "RealmRP", "projectDesc": "Polski serwer roleplay",
      "players": 50, "max": 500, "queue": 0, "peakPlayers": 297,
      "framework": null, "resourceCount": 324,
      "locale": "pl-PL", "tags": ["roleplay", "wl-off"],
      "gameName": null, "whitelist": false, "whitelistInfo": null,
      "oneSync": "on", "gameBuild": "3570", "scriptHook": false,
      "pureLevel": null,
      "discord": "https://dc.realmrp.pl", "store": "https://sklep.realmrp.pl",
      "playerNames": { "total": 226, "anonymized": true },
      "endpoints": ["https://30123-spillway.example.eu/"],
      "endpointsHidden": false, "private": false,
      "online": true, "error": null,
      "joinUrl": "https://cfx.re/join/yokb5k"
    }
  ],
  "count": 1,
  "limit": 300,
  "batchMax": 10,
  "intervalMs": 60000,
  "clientRefreshMs": 15000
}
PoleZnaczenie
onlinefalse = serwer nie odpowiada, ale wpis zostaje z ostatnimi znanymi danymi. null = jeszcze nie sprawdzony
endpointsHiddentrue gdy serwer ukrywa swój bezpośredni adres
counters Pełny opis dwóch liczników z odpowiedzi cfx — zawsze obecny, także gdy się zgadzają (state: brak-pola, nieprawidlowa-wartosc, zgodne, rozbieznosc). „Sprawdzone i zgodne" to inna informacja niż „nie sprawdzono", a reported: 0 to pomiar, nie brak pomiaru. direction mówi, w którą stronę: zawyzanie (serwer zgłasza więcej — zwykle gracze na ekranie ładowania) albo zanizanie. percent jest liczony względem liczby cfx (percentOf); przy clients = 0 jest null, bo mianownik jest zerem
selfReportedPole zgodnościowe dla starszej strony: null albo trzy liczby, obecne tylko przy rozbieżności. Nowy kod czyta counters
health Ocena zdrowia — ta sama, co w /api/stats, na karcie serwera i na odznace: jeden moduł, jedne progi. grade to A / B / C albo null z listą powody, a uptime i wskaznik niosą uzasadnienie także wtedy, gdy litery nie ma. Pełny opis reguły — przy /api/stats wyżej
activityReport Raport wskaźnika licznika graczy. Zawsze obecny dla serwera online — także wtedy, gdy nic się nie zapaliło, bo „sprawdziliśmy i nic" to inna odpowiedź niż „nie było czego sprawdzić".
status: brak-danych / za-malo-danych / nieaktualne / swiezosc-nieznana / brak-wzorca / obserwacje.
level: brak / informacja (jedna rodzina obserwacji) / wzorzec (dwie różne, potwierdzone rodziny przy pełnej jakości danych).
families: malaZmiennosc (sygnały lowCv i stall — dwa opisy tego samego zjawiska, liczą się jako jedna przesłanka) i rozjazdLicznikow (countersDiffer).
familyDependence: obecne tylko przy co najmniej dwóch rodzinach potwierdzonych na pełnej jakości własnych danych — pary tych rodzin z zadeklarowaną zależnością: nieznana (niezmierzona, dziś jedyny stan), zalezne (występują razem częściej niż przypadkiem — para nie daje wzorzec) albo niezalezne-w-probie, z podstawą w basis.
quality: liczba różnych obserwacji źródłowych, rozpiętość okna, największa luka, świeżość (swieze / nieaktualne / nieznana) i sourceRepeats — ile naszych odczytów oddało tę samą obserwację.
dataQuality: jakość danych w wersji zwięzłej — basis, grade (pelna / ograniczona / niewystarczajaca), limits (wszystkie powody obniżenia oceny, nie pierwszy), coveragePercent, largestGapMinutes, sourceAgeSeconds. Ten sam opis ma każdy sygnał w signals, z własnym basis: lowCv i stall stoją na oknie obserwacji (okno-obserwacji), a countersDiffer na pojedynczym odczycie (pojedynczy-odczyt) — dwie liczby z jednej odpowiedzi okna nie potrzebują. Pełna wersja jest w /api/serwer. Pole niczego nie bramkuje: o tym, co się zapala, decydują te same warunki co bez niego.
To opis pomiarów licznika, nie ocena serwera. Liczone wyłącznie z liczb na poziomie serwera, bez żadnych danych o pojedynczych graczach. Bufor obserwacji żyje w pamięci, więc po restarcie usługi status to brak-danych. Progi są ustawieniami początkowymi i wymagają walidacji
activityPatternPole zgodnościowe dla starszej strony: null poza poziomem wzorzec. Nowy kod czyta activityReport
projectNameNazwa, którą serwer nadaje sobie sam (sv_projectName). null, gdy pokrywa się z hostname
projectDescOpis własnymi słowami serwera, obcięty do 400 znaków
peakPlayersRekord graczy zgłoszony przez sam serwer — cfx nie dokumentuje, z jakiego okna. null, gdy serwer go nie podał albo gdy jest niższy od bieżącej liczby graczy
gameNamenull = GTA V (stan domyślny). "rdr3" = to serwer RedM, nie FiveM
whitelisttrue/false z sv_appearAllowlisted, null gdy serwer nic nie podał
whitelistInfoInstrukcja dołączenia (sv_allowlistInstructions), obcięta do 200 znaków
pureLevelPoziom pure mode, gdy większy od zera. null = stan domyślny
playerNamesSam fakt, czy serwer anonimizuje swoją listę graczy: anonymized to true, false albo null (nie da się rozstrzygnąć). Nicków ten endpoint nie zwraca i zwracać nie będzie
extraVarsSurowy zrzut zmiennych serwera, których nie mapujemy jawnie
clientRefreshMsJak często warto odpytywać. Backend odświeża dane co intervalMs, ale rozkłada zapytania po kolei
batchMaxIle kodów wolno wrzucić w jednym zgłoszeniu do /api/lookup/add-many
Limity: brak rate limitu na odczyt. Lista mieści maksymalnie 300 serwerów. Przy pełnej liście odpowiedź waży około 0,6 MB.
GET/api/serwer?server=KOD

Jeden serwer, pełne rozliczenie wskaźnika licznika graczy — to, co pokazuje strona /serwer?server=KOD, otwierana kliknięciem w nazwę serwera na dashboardzie albo na liście. Jedyne miejsce, w którym obie ścieżki wskaźnika spotykają się w jednej odpowiedzi (activityReport z bieżących odczytów i patternReport z dobowych rollupów), i jedyne, które oddaje pomiary bez progów.

Dlaczego osobno od /api/lookup/servers: ten endpoint oddaje współczynnik zmienności i długość zastoju także wtedy, gdy nie sięgają progu. W hurtowej odpowiedzi listy ta sama liczba jechałaby dla trzystu serwerów przy każdym odświeżeniu każdej otwartej karty. Tutaj pojawia się wyłącznie wtedy, gdy ktoś świadomie otworzył stronę jednego serwera.

Przykładowe żądanie
curl "https://stats.bykens.eu/api/serwer?server=okz5dj"
Przykładowa odpowiedź (skrócona)
{
  "code": "okz5dj", "pinned": true, "online": true,
  "hostname": "INTOUCH", "players": 235, "max": 249, "queue": 2,
  "lastSeen": "2026-09-15T22:39:09.694985519Z",
  "observedAtMs": 1789511949694,
  "checkedAt": "2026-09-15T22:42:57.069Z",

  "counters": { "state": "zgodne", "reported": 246, "counted": 235,
                "diff": 11, "direction": "zawyzanie", "percent": 5,
                "percentOf": "counted", "countedZero": false },
  "countersThresholds": { "minPercent": 25, "minPlayers": 10 },

  "activityReport": { "status": "brak-wzorca", "level": "brak",
                      "families": [], "confirmedFamilies": [], "signals": [],
                      "quality": { "observations": 26, "spanMinutes": 118, "grade": "pelna" },
                      "dataQuality": { "basis": "okno-obserwacji",
                        "coverage": { "percent": 100, "bucketMinutes": 10,
                                      "bucketsWithObservation": 11, "bucketsTotal": 11,
                                      "spanMinutes": 118, "observations": 26,
                                      "minObservations": 12, "minSpanMinutes": 90 },
                        "largestGap": { "minutes": 7, "maxAllowedMinutes": 29, "includesNow": false },
                        "age": { "sourceObservationSeconds": 212, "sinceLastReadingSeconds": 14,
                                 "staleAfterMinutes": 15, "freshness": "swieze" },
                        "sourceRepeats": 7, "grade": "pelna", "limits": [] } },

  "measurements": {
    "window": { "observations": 26, "spanMinutes": 118, "maxGapMinutes": 7,
                "sufficient": true, "minObservations": 12, "minSpanMinutes": 90,
                "maxGapShare": 0.25, "bufferWindowHours": 6, "samplesMax": 180,
                "staleAfterMinutes": 15 },
    "variation": { "computable": true, "reason": null, "meets": false,
                   "cvPercent": 4.7, "threshold": 2, "baseThreshold": 2, "mean": 231,
                   "minMeanPlayers": 20, "observations": 26, "minutes": 118,
                   "capacityBand": "z-zapasem", "capacitySharePercent": 46,
                   "capacityToleranceSlots": 25, "strictness": 1 },
    "stall":     { "computable": true, "reason": null, "meets": false,
                   "players": 235, "minutes": 5, "typicalMinutes": 9, "changes": 12,
                   "requiredMinutes": 60, "minMinutes": 60, "factor": 6,
                   "baseMinMinutes": 60, "baseFactor": 6,
                   "minChanges": 3, "minPlayers": 20, "atCapacity": false,
                   "capacityBand": "z-zapasem", "capacitySharePercent": 47,
                   "capacityToleranceSlots": 25, "strictness": 1 },
    "families": {
      "malaZmiennosc":    { "met": false, "confirmed": false, "forMinutes": null,
                            "observations": 0, "missingForMinutes": 118,
                            "releaseInMinutes": null,
                            "wasConfirmed": true, "timesConfirmed": 1,
                            "sinceReleasedMinutes": 98, "confirmMinutes": 45,
                            "confirmMinObservations": 6, "releaseMinutes": 20 },
      "rozjazdLicznikow": { "met": false, "confirmed": false, "...": "jak wyżej" }
    }
  },

  "availability": { "hourlyProfile": false, "hourlyProfileReason": "tylko-przypiete" },

  "patternReport": { "status": "obserwacje", "level": "informacja", "...": "jak w /api/trends" },
  "daily": {
    "days": [
      { "date": "2026-09-05", "coveragePercent": 100, "avgPlayers": 100,
        "peakPlayers": 110, "hasProfile": true, "hoursCovered": 24,
        "hoursExpected": 24, "maxGapHours": 0, "ratio": 1.1,
        "used": true, "limited": false, "reason": null },
      { "date": "2026-09-08", "coveragePercent": 42, "avgPlayers": 100,
        "peakPlayers": 110, "hasProfile": true, "hoursCovered": 24,
        "hoursExpected": 24, "maxGapHours": 0, "ratio": null,
        "used": false, "limited": false, "reason": "pokrycie-doby" }
    ],
    "config": { "windowDays": 14, "minDays": 10, "minCoveragePercent": 90,
                "minHoursOf24": 22, "maxGapHours": 2, "ratioMax": 1.5,
                "minSharePercent": 80, "minMeanPlayers": 20 }
  },
  "generatedAt": "2026-09-16T00:42:57.201Z"
}
PoleZnaczenie
onlinetrue / false / null — ostatnie to serwer jeszcze nie sprawdzony, a nie serwer niedostępny. Trzy różne stany, bo to trzy różne odpowiedzi
activityReport.dataQuality Jakość danych w pełnej wersji (lista dostaje zwięzłą). Ten sam kształt przy raporcie i przy każdym sygnale; basis mówi, na czym stoi dany wynik.
coverage.percent: odsetek pełnych 10-minutowych kubełków z co najmniej jedną obserwacją źródłową, od pierwszej obserwacji do teraz. Kubełek, w którym wypada „teraz", nie wchodzi do rachunku; okno krótsze niż kubełek ma null. Przerwa dłuższa niż 15 min kasuje bufor, więc pokrycie spada poniżej 100% tylko przez dziury 10–15 min — dłuższe zaczynają okno od nowa i widać je po observations i spanMinutes.
largestGap: największa luka, dozwolone maksimum (¼ okna) i includesNow — czy to luka od ostatniej obserwacji do teraz.
age: wiek obserwacji źródłowej i czas od naszego ostatniego odczytu, liczone w chwili odpowiedzi. Dla sygnału countersDiffer coverage i largestGap są null — nie dotyczą.
limits: za-malo-obserwacji, okno-za-krotkie, luka-ponad-cwierc-okna, swiezosc-nieznana, nieaktualne. Pokrycie jest wystawiane, nie bramkowane
measurements Pomiary BEZ progów. activityReport.signals mówi „zapalić czy nie"; to pole mówi ile dokładnie wyszło i ile brakowało do progu. Dla serwera niedostępnego jest null — wskaźnika wtedy nie liczymy w ogóle.
computable: czy tej liczby wolno użyć. false oznacza, że liczba jest policzona, ale nie znaczy nic — powód w reason: brak-obserwacji, okno-niewystarczajace, ponizej-pulapu-graczy, komplet-na-serwerze (stała liczba przy komplecie to skutek limitu, nie własność licznika — dotyczy obu pomiarów, a komplet to limit minus max(2 sloty, 5% limitu), bo sloty trzymają też gracze na ekranie ładowania), za-malo-zmian.
capacityBand: położenie względem limitu slotów — komplet, blisko-limitu (powyżej 90%, progi zaostrzone strictness-krotnie: CV 2% → 1%, zastój 60 → 120 min i 6× → 12×), z-zapasem albo null (limit nieznany, progi bazowe). Dla variation liczone dla całego okna, dla stall — dla wartości, na której licznik stoi.
meets: czy próg jest przekroczony. Przy computable: false jest zawsze false.
Progi jadą razem z liczbami (threshold, minMinutes, factor, requiredMinutes — to progi obowiązujące przy danym położeniu; bazowe w baseThreshold, baseMinMinutes, baseFactor), żeby nikt nie trzymał własnej kopii — ta rozjeżdża się przy pierwszej zmianie
measurements.families Stan maszyny potwierdzania dla obu rodzin ścieżki listy — i jedyna historia, jaką ten wskaźnik ma. met to „warunek jest spełniony teraz", confirmed to „trwa nieprzerwanie dość długo, żeby się liczyć". confirmed: true przy met: false znaczy, że rodzina była aktywna przed chwilą i właśnie się wygasza — releaseInMinutes mówi, ile zostało.
Po zgaśnięciu wszystkie pola powyżej wracają do wartości „nic się nie działo", więc rodzina wygasła wyglądałaby identycznie jak taka, która nigdy się nie zapaliła. Odróżniają je trzy pola: wasConfirmed, timesConfirmed i sinceReleasedMinutes (ile minut temu zgasła). releaseInMinutes i sinceReleasedMinutes to dwie różne rzeczy i nigdy nie są niepuste naraz: pierwsze odlicza do zgaśnięcia, drugie liczy od niego.
Dalej wstecz nie widzimy i nie jest to dziennik zdarzeń: bufor żyje w pamięci, więc licznik timesConfirmed zeruje i restart usługi, i przerwa w odczytach dłuższa niż próg świeżości
availability Czego dla tego serwera nie zmierzymy — i że to się nie zmieni. hourlyProfile: false (z powodem tylko-przypiete) znaczy, że profilu godzinowego dla niego nie zbieramy: miara profileShape nie policzy się nigdy, a nie „jeszcze nie", i wszystkie jego doby mają trwale limited: true.
Dziś ta wartość równa się pinned i to jest celowa nadmiarowość: reguła „przypięty ⇒ jest profil" ma siedzieć w jednym miejscu. Gdyby profil kiedyś objął całą listę, pinned zostałoby bez zmian, a klient wnioskujący z niego zacząłby kłamać; tutaj zmienia się jedna linijka.
Dotyczy to też serwera odpiętego: rollupy są trwałe, więc zachowuje stare doby z profilem, choć nowych już nie zbieramy
daily.days Doba po dobie z okna czternastu dni, od najstarszej. used mówi, czy weszła do rachunku; reason jest niepusty dokładnie wtedy, gdy nie weszła: pokrycie-doby, za-malo-graczy, brak-szczytu, brak-godzin, pokrycie-godzin, dziura-w-dobie. limited: true to doba, która weszła, ale bez profilu godzinowego — i to ta etykieta blokuje potem podniesienie poziomu.
Powód pochodzi z tej samej listy warunków, z której korzysta sam wskaźnik
daily.configProgi ścieżki dobowej, podane wprost. minDays: 10 to inna liczba niż czternaście dni raportu /api/trends: wskaźnik potrzebuje dziesięciu dób z czternastu i nie wymaga ciągłości
countersThresholdsProgi rozjazdu liczników — oba muszą być przekroczone naraz. Sam procent zapalałby małe serwery, sama liczba duże
Kody odpowiedzi400 — brak parametru albo to nie jest kod cfx. 404 — poprawny kod, ale tego serwera nie ma na liście
Limity: brak rate limitu. Czyta wyłącznie z pamięci podręcznej — Twoje żądanie nigdy nie wywoła zapytania do cfx i nie przesuwa stanu wskaźnika. Profil godzinowy zbieramy tylko dla serwerów przypiętych, więc dla pozycji dołożonej z listy wszystkie doby mają limited: true, a miara profileShape nie policzy się nigdy — to ograniczenie kosztu zapisu, nie brak danych, na które trzeba poczekać.

Trendy

GET/api/trends

Porównanie tydzień do tygodnia dla każdego śledzonego serwera: średnia graczy, średnia kolejka i szczyty z ostatnich siedmiu pełnych dni wobec siedmiu poprzednich. Liczone z dziennych rollupów — zbioru całkowicie osobnego od /api/history (24 h) i /api/hourly (siatka 7×24 bez daty).

Dzisiejszy dzień nie wchodzi do żadnego okna — jest niepełny. Jeśli w oknie czternastu dni brakuje choć jednego, ten serwer dostaje ready: false i żadnych pól porównania: nie ma current, previous ani change. To celowe — liczba wyliczona z dziur wygląda tak samo jak prawdziwa.

Przykładowe żądanie
curl https://stats.bykens.eu/api/trends
Przykładowa odpowiedź (skrócona)
{
  "ready": true,
  "daysNeeded": 14,
  "weekDays": 7,
  "windows": {
    "current":  { "from": "2026-09-07", "to": "2026-09-13" },
    "previous": { "from": "2026-08-31", "to": "2026-09-06" }
  },
  "minSamplesPerDay": 144,
  "expectedSamplesPerDay": 288,
  "flatPercent": 5,
  "servers": [
    {
      "code": "okz5dj", "label": "Intouch.gg",
      "ready": true, "daysCollected": 31, "daysMissing": 0, "waitDays": 0,
      "current":  { "days": 7, "avgPlayers": 198.4, "avgQueue": 12.7, "peakPlayers": 250, "peakQueue": 61 },
      "previous": { "days": 7, "avgPlayers": 181.2, "avgQueue": 10.3, "peakPlayers": 248, "peakQueue": 54 },
      "change": {
        "players": { "comparable": true, "from": 181.2, "to": 198.4, "percent": 9,  "direction": "up" },
        "queue":   { "comparable": true, "from": 10.3,  "to": 12.7,  "percent": 23, "direction": "up" }
      }
    },
    {
      "code": "jjj4kl", "label": "Onbeat",
      "ready": false, "daysCollected": 11, "daysMissing": 3, "waitDays": 3,
      "pattern": null,
      "patternReport": {
        "status": "obserwacje",
        "level": "informacja",
        "families": ["ksztaltDoby"],
        "confirmedFamilies": [],
        "quality": { "days": 11, "limitedDays": 0, "daysWithProfile": 11, "minDays": 10, "grade": "pelna" },
        "signals": [
          { "id": "lowPeakToAverage", "family": "ksztaltDoby", "experimental": true, "confirmed": false,
            "data": { "days": 11, "lowDays": 11, "limitedDays": 0, "typicalRatio": 1.02,
                      "highestRatio": 1.05, "threshold": 1.5, "from": "2026-09-03", "to": "2026-09-13" } },
          { "id": "profileShape", "family": "ksztaltDoby", "experimental": true, "confirmed": false,
            "data": { "days": 11, "hours": 24, "profileAmplitude": 0.06, "profileAgreement": 0.31 } }
        ]
      }
    }
  ],
  "generatedAt": "2026-09-14T16:20:03.918Z"
}
PoleZnaczenie
readyNa poziomie serwera: czy w oknie czternastu dni jest komplet. Na poziomie całej odpowiedzi: czy choć jeden serwer ma komplet — nowy serwer nie blokuje raportu dla pozostałych
waitDaysZa ile dni okno będzie pełne. Brakujący dzień wypada z okna dopiero, gdy przesunie się poza czternasty, więc to nie zawsze jest po prostu daysMissing
daysCollectedIle pełnych dni mamy w ogóle (okno rollupów to 90 dni), niezależnie od okna porównania
minSamplesPerDayOd ilu odczytów dzień liczy się jako pełny. Dzień poniżej progu jest traktowany jak brak dnia, a nie jak dzień z niższą średnią
change.*.comparablefalse, gdy poprzednia średnia była bliska zeru — wtedy percent jest null, bo skok z 0,2 na 0,6 to formalnie „+200%", a w praktyce dalej brak kolejki
change.*.directionup / down / flat. flat poniżej flatPercent — percent jest wtedy dalej podany
avgPlayers / avgQueueŚrednia ze średnich dziennych, nie ze wszystkich próbek — krótszy dzień ma ważyć tyle samo, co pełny
patternReport Wskaźnik licznika graczy liczony z trwałych rollupów dziennych zamiast z bufora obserwacji. Zawsze obecny, ze status jak w activityReport (za-malo-danych / brak-wzorca / obserwacje).
Dziś zawiera dwie miary, obie eksperymentalne — nie podnoszą level ponad informacja i nie mają progów alarmowania:
lowPeakToAverage — niski stosunek szczytu doby do średniej tej doby, w ≥ 80% z minimum 10 dób. Nazwa jest dosłowna, bo miara opisuje amplitudę względem poziomu, a nie rytm dobowy: płynny, powtarzalny rytm od 60 graczy w nocy do 140 wieczorem ma średnią 100 i stosunek 1,40, czyli poniżej progu 1,5. Ta miara nie dowodzi braku rytmu dobowego ani manipulacji.
profileShape — profileAmplitude (rozpiętość medianowego profilu godzinowego, 0 = płaska linia) i profileAgreement (powtarzalność profilu między dobami, 1 = ten sam kształt co dzień). To dwie różne rzeczy niż powyższa i często jej przeczą.
Do rachunku wchodzą wyłącznie doby o pokryciu ≥ 90%, pokryciu godzin ≥ 22 z 24 i największej ciągłej luce ≤ 2 h — samo pokrycie nie wystarcza, bo braki skupione w jednej porze doby to pomiar czegoś innego. Doby zebrane przed wprowadzeniem profilu godzinowego mają quality.grade: "ograniczona".
dataQuality (basis: "doby-rollupu", także przy każdej mierze): coverage — ile dób weszło do rachunku z okna 14 (daysUsed, percent), najsłabsze pokrycie przyjętej doby; largestGap.hours — najdłuższa luka w profilu godzinowym przyjętych dób; age.daysSinceLastDay — ile dób temu była ostatnia przyjęta doba; limits: za-malo-dob, doby-bez-profilu.
Profil godzinowy zbieramy tylko dla serwerów przypiętych — przy 300 pozycjach listy to ~19 MB w jednym pliku zapisywanym co pięć minut.
Progi (1,5; 10 dób; 80%) dobrano na trzech serwerach i jednej dobie — są wstępne i nie określają częstości fałszywych alarmów. Brak obserwacji nie potwierdza, że liczba graczy jest prawdziwa
patternPole zgodnościowe dla starszej strony. Dziś zawsze null: wszystkie miary tej ścieżki są eksperymentalne i nie osiągają poziomu wzorzec. Nowy kod czyta patternReport
Limity: brak rate limitu. Treść zmienia się raz na dobę — odpytywanie częściej niż co kilka godzin nic nie wnosi.

Resource'y

GET/api/resources

Ranking najpopularniejszych resource'ów — na ilu ze śledzonych serwerów występuje każda nazwa. Liczony na żywo z pamięci procesu (dashboard plus lista serwerów, każdy serwer raz), z krótkim, kilkusekundowym oknem pamięci podręcznej. Nic tu nie jest zapisywane na dysk, więc to stan z tej chwili, a nie historia.

Przykładowe żądania
curl https://stats.bykens.eu/api/resources
curl "https://stats.bykens.eu/api/resources?limit=50&builtin=1"
Przykładowa odpowiedź (skrócona)
{
  "top": [
    { "rank": 1, "name": "ox_lib",    "servers": 4, "percent": 100, "builtin": false },
    { "rank": 2, "name": "oxmysql",   "servers": 4, "percent": 100, "builtin": false },
    { "rank": 3, "name": "pma-voice", "servers": 4, "percent": 100, "builtin": false }
  ],
  "serversTotal": 5,
  "serversCounted": 4,
  "resourcesTotal": 883,
  "builtinTotal": 4,
  "builtinIncluded": false,
  "limit": 20,
  "generatedAt": "2026-09-14T16:02:11.004Z"
}
ParametrZnaczenie
limitIle pozycji zwrócić. Domyślnie 20, maksymalnie 100
builtin1 dokłada resource'y z paczki FiveM i txAdmina (hardcap, monitor, sessionmanager…). Domyślnie ich nie ma — stoją prawie wszędzie, więc bez nich ranking mówi o tym, co administratorzy faktycznie wybrali
PoleZnaczenie
serversTotalIle serwerów w ogóle śledzimy (dashboard + lista, bez podwójnego liczenia)
serversCountedZ ilu z nich udało się wziąć listę resource'ów. To jest mianownik percent — serwer, który nie odpowiada, nie jest serwerem z zerem resource'ów, więc wypada z obliczeń
percentservers / serversCounted, zaokrąglone. Mówi na ilu serwerach, nie ilu graczy to widzi
builtinTotalIle rozpoznanych wbudowanych nazw jest w całym zbiorze — niezależnie od limit i od builtin
Limity: brak rate limitu. Wynik przeliczany nie częściej niż raz na 10 s.

Zapis — bez CORS

Poniższe endpointy nie wysyłają nagłówka Access-Control-Allow-Origin, więc nie wywołasz ich z przeglądarki z obcej domeny. Z curla czy z własnego backendu — owszem. Obowiązuje rate limit liczony po prawdziwym adresie IP żądania, a nie po nagłówku, który łatwo podrobić.

POST/api/lookup/add

Dodaje serwer do wspólnej listy. Przyjmuje sam kod cfx.re albo pełny link (cfx.re/join/…, servers.fivem.net/servers/detail/…). Serwer musi istnieć — nieistniejący kod nie trafia na listę.

Przykładowe żądanie
curl -X POST https://stats.bykens.eu/api/lookup/add \
  -H 'Content-Type: application/json' \
  -d '{"code":"okz5dj"}'
Przykładowa odpowiedź
{ "ok": true, "code": "okz5dj", "added": true, "server": { … } }
OdpowiedźKiedy
200 alreadyOnListSerwer już jest na liście — traktowane jak sukces
400 bad_codeKod nie ma formatu cfx.re (6–7 znaków alfanumerycznych)
404 not_foundSerwer o tym kodzie nie istnieje albo nie odpowiada
409 list_fullLista osiągnęła 300 pozycji
429 rate_limitPrzekroczony limit prób
Limity: maksymalnie 5 prób na 10 minut z jednego adresu IP. Liczone są wszystkie żądania, także te ze złym kodem. Licznik jest osobny od licznika usuwania.
POST/api/lookup/add-many

Dodaje kilka serwerów jednym żądaniem. Pole codes to jeden kawałek tekstu — kod albo link na linię, albo oddzielone przecinkami; oba warianty działają zamiennie. Każda pozycja przechodzi dokładnie tę samą ścieżkę co przy /api/lookup/add: format kodu, deduplikacja, sufit długości listy i wymóg istnienia serwera.

Przykładowe żądanie
curl -X POST https://stats.bykens.eu/api/lookup/add-many \
  -H 'Content-Type: application/json' \
  -d '{"codes":"okz5dj\njjj4kl, yokb5k"}'
Przykładowa odpowiedź
{
  "ok": true,
  "results": [
    { "input": "okz5dj", "code": "okz5dj", "status": "added", "server": { … } },
    { "input": "jjj4kl", "code": "jjj4kl", "status": "already", "server": null },
    { "input": "abc!!!", "code": null,     "status": "bad_code", "server": null }
  ],
  "added": ["okz5dj"],
  "summary": { "added": 1, "already": 1, "bad_code": 1,
               "not_found": 0, "list_full": 0, "duplicate_in_batch": 0 },
  "count": 42,
  "limit": 300
}
status pozycjiZnaczenie
addedDopisany do listy; server zawiera komplet danych z odpytania weryfikującego
alreadyTen kod już był na liście
duplicate_in_batchTen sam kod wystąpił w zgłoszeniu więcej niż raz
not_foundSerwer o tym kodzie nie istnieje albo nie odpowiada; powód w polu error
bad_codeTo nie jest kod cfx.re ani link; code jest wtedy null, a input trzyma wklejony tekst
list_fullLista zapełniła się w trakcie tego zgłoszenia
OdpowiedźKiedy
200Zgłoszenie rozpatrzone — także wtedy, gdy żadna pozycja się nie dodała. O losie każdego kodu mówi jego status
400 emptyPuste zgłoszenie
400 batch_too_bigWięcej niż 10 pozycji — zgłoszenie odrzucane w całości, nie przycinane
400 batch_too_longSam tekst zgłoszenia dłuższy niż 2000 znaków
429 rate_limitPrzekroczony limit prób
Limity: najwyżej 10 kodów w jednym zgłoszeniu. Rate limit jest wspólny z /api/lookup/add (5 żądań na 10 minut z jednego IP) i liczy ŻĄDANIA, nie kody — jedna paczka to jedno trafienie.
DELETE/api/lookup/remove

Usuwa serwer z listy. Bez logowania — usunąć może każdy, tak samo jak każdy może dodać. Kod można podać w ciele żądania albo jako ?code=.

Przykładowe żądanie
curl -X DELETE https://stats.bykens.eu/api/lookup/remove \
  -H 'Content-Type: application/json' \
  -d '{"code":"okz5dj"}'
Przykładowa odpowiedź
{ "ok": true, "code": "okz5dj", "removed": true }
Limity: maksymalnie 5 prób na 10 minut z jednego adresu IP, na liczniku osobnym od dodawania. Usunięcie serwera, którego nie ma na liście, zwraca 200 z alreadyGone — stan jest przecież taki, jakiego chciałeś.

Kanał zdarzeń (RSS)

GET/feed.xml

Ten sam ticker co /api/events, tyle że w formacie Atom — do wrzucenia w dowolny czytnik RSS. Strona główna ma <link rel="alternate" type="application/atom+xml">, więc czytniki znajdą kanał same po podaniu samego adresu serwisu.

To nie jest archiwum Zdarzenia żyją wyłącznie w pamięci procesu i restart usługi je kasuje — dokładnie tak samo jak pasek zdarzeń na stronie głównej. Kanał pokazuje ostatnie kilkadziesiąt minut, a po restarcie bywa pusty. Pusty kanał to poprawny kanał, nie awaria.
Przykładowe żądanie
curl https://stats.bykens.eu/feed.xml
Przykładowa odpowiedź (skrócona do jednego wpisu)
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Statystyki serwerów FiveM — zdarzenia</title>
  <id>https://stats.bykens.eu/feed.xml</id>
  <link rel="self" type="application/atom+xml" href="https://stats.bykens.eu/feed.xml"/>
  <link rel="alternate" type="text/html" href="https://stats.bykens.eu/"/>
  <updated>2026-09-14T16:59:06.800Z</updated>
  <author><name>Statystyki serwerów FiveM</name></author>
  <entry>
    <title>Rekord kolejki: Intouch.gg (51)</title>
    <link rel="alternate" type="text/html" href="https://stats.bykens.eu/"/>
    <id>https://stats.bykens.eu/feed.xml#okz5dj-record-queue-2026-09-14T16:40:00.000Z</id>
    <updated>2026-09-14T16:40:00.000Z</updated>
    <published>2026-09-14T16:40:00.000Z</published>
    <category term="record-queue"/>
    <content type="text">Nowy dzisiejszy rekord kolejki na Intouch.gg: 51.</content>
  </entry>
</feed>
Rodzaje zdarzeń (category term)
termZnaczenie
onlineSerwer znowu odpowiada
offlineSerwer przestał odpowiadać
fullPrzekroczony próg zapełnienia (z histerezą, żeby nie migało)
record-playersNowy dzisiejszy rekord graczy
record-queueNowy dzisiejszy rekord kolejki
PoleUwaga
entry/idSkładane z kodu serwera, rodzaju zdarzenia i znacznika czasu — nie z licznika w pamięci. Licznik po restarcie zaczyna od jedynki, więc to samo id oznaczałoby raz jedno zdarzenie, raz zupełnie inne, a czytnik po id rozpoznaje, co już widział
entry/linkProwadzi na stronę główną. Zdarzenia nie mają własnych podstron i udawanie, że mają, dałoby martwy link
id i linkSkładane z adresu, pod którym pobrano kanał — ta sama usługa odpowiada na localhost i na domenie
Limity: brak rate limitu. Bufor mieści 20 ostatnich zdarzeń, bez cache po drodze.

Monitoring

GET/health

Dla zewnętrznych monitoringów (UptimeRobot i podobne). Bez logowania, z CORS-em, bez cache. Odpowiada na pytanie „czy ta usługa działa", a nie „czy serwery FiveM są online".

Przykładowe żądanie
curl -i https://stats.bykens.eu/health
Przykładowa odpowiedź
{
  "status": "ok",
  "uptimeSeconds": 9,
  "lastSuccessfulPollAt": "2026-09-14T14:14:07.393Z",
  "staleAfterSeconds": 60,
  "serversTracked": 3,
  "calibrationSampleSize": 0
}
Kod / statusZnaczenie
200 okPętla odpytywania działa — ostatni udany odczyt jest świeższy niż staleAfterSeconds
200 startingUsługa właśnie wstała i nie zdążyła jeszcze nic pobrać. To rozruch, nie awaria
503 staleOd staleAfterSeconds nie udało się pobrać danych z żadnego serwera — coś utknęło
calibrationSampleSizeIle serwerów próby kalibracyjnej dzieli dziś budżet odpytywania z resztą. 0 poza przebiegiem zbierającym. Serwery próby nie są liczone w serversTracked i nie pojawiają się w żadnym innym endpoincie
Co NIE jest błędem: pojedynczy serwer FiveM offline. Znacznik ostatniego udanego odczytu stawiamy, gdy odpowie choć jeden serwer — inaczej /health zamieniłby się w alarm o cudzej awarii. Próg to trzykrotność POLL_INTERVAL_MS (przy domyślnych 20 s → 60 s).

Odznaka do README

GET/badge.svg

Obrazek SVG z aktualną liczbą graczy — do wklejenia w README na GitHubie, na stronę serwera albo gdziekolwiek działa <img>. Generowany u nas, nie przez shields.io, więc nie zależy od obcej usługi. Wysyła Cache-Control: no-cache, żeby nie pokazywać liczby sprzed godzin, i Access-Control-Allow-Origin: *, żeby dało się go osadzić wszędzie.

Do wklejenia w Markdown
![Gracze](https://stats.bykens.eu/badge.svg)
![Gracze](https://stats.bykens.eu/badge.svg?type=players)
![Kolejka](https://stats.bykens.eu/badge.svg?type=queue)
![Intouch](https://stats.bykens.eu/badge.svg?server=okz5dj)
W HTML-u
<img src="https://stats.bykens.eu/badge.svg" alt="Gracze online" />
ParametrZnaczenie
typetotal (domyślnie, gracze + kolejka), players albo queue. Nieznana wartość cicho wraca do total — odznaka w cudzym README ma się narysować zawsze, a nie wywrócić na literówce
serverKod jednego ze śledzonych serwerów zamiast sumy. Nieznany kod daje odznakę z napisem „nieznany kod serwera", a nie błąd HTTP — po drugiej stronie jest <img>, więc 500 pokazałoby tylko ikonę zepsutego obrazka
Limity: brak rate limitu. Odznaka zawsze zwraca HTTP 200 — także gdy serwer jest offline (napis „offline") albo nie ma jeszcze danych („brak danych").
GET/badge-uptime.svg?server=KOD

Dostępność serwera z ostatnich 24 h. Ta sama szata co /badge.svg, ale parametr server jest tu obowiązkowy — uptime liczymy per serwer i nie ma czegoś takiego jak „uptime sumy".

Do wklejenia w Markdown
![Uptime](https://stats.bykens.eu/badge-uptime.svg?server=okz5dj)
Co widaćKiedy
99,6% na turkusieOd 99% w górę
97,3% na bursztynieOd 95% do 99% — w skali doby 99% to już kwadrans niedostępności, więc próg jest celowo surowy
42,1% na czerwieniPoniżej 95%
brak danych na szaroZa mało odczytów, żeby cokolwiek powiedzieć. To nie to samo co 0% — zero byłoby oskarżeniem serwera o awarię, której nie zmierzyliśmy
BŁĄD: podaj ?server=kodBrak parametru
BŁĄD: nieznany kod serweraKod spoza śledzonej trójki
Uwaga: odznaka działa także dla serwera, który właśnie nie odpowiada — wtedy ta liczba jest najbardziej potrzebna, więc nie zastępujemy jej napisem „offline".
GET/badge-ocena.svg?server=KOD

Ocena zdrowia serwera — ta sama litera, którą liczy /api/stats i karta serwera. Odznaka nie pokazuje samej litery: obok niej stoi powód, bo ocena bez podstawy jest wyrokiem, a nie pomiarem. Parametr server obowiązkowy.

Do wklejenia w Markdown
![Ocena](https://stats.bykens.eu/badge-ocena.svg?server=okz5dj)
Co widaćKiedy
A · 99,6% na turkusieUptime 7 dni powyżej 99% i żadna rodzina obserwacji licznika nie jest potwierdzona
B · 97,3% na bursztynieUptime od 95% do 99%, obserwacje jak wyżej
C · 90,1% na czerwieniUptime poniżej 95%
C · wzorzec licznika na czerwieniUptime w porządku, ale utrzymuje się obserwacja licznika
brak oceny na szaroKtóregoś z dwóch pomiarów nie da się policzyć — na przykład uptime ma jeszcze mniej niż dobę obserwacji. Szary, nigdy czerwony: brak pomiaru nie jest zarzutem wobec serwera
Pełny rachunek — obie liczby, reguła i powody braku litery — jest na karcie serwera: /serwer?server=KOD.
GET/badge-framework.svg?server=KOD

Rozpoznany framework serwera — ESX, QBCore, Qbox, vRP, rg_core. Te same reguły wykrywania co na liście serwerów i w /api/stats, bo jest to jedna kopia w fivem-parse.js. Parametr server obowiązkowy.

Do wklejenia w Markdown
![Framework](https://stats.bykens.eu/badge-framework.svg?server=okz5dj)
Co widaćKiedy
rg_core na turkusieRozpoznano framework
nieznany na szaroSerwer odpowiedział, ale żaden ze znanych markerów nie pasuje. Nigdy pusta prawa część — odznaka bez wartości wygląda na zepsutą, a nie na „nie wiemy"
offline na czerwieniSerwer nie odpowiada. Po błędzie nie mamy już poprzedniego odczytu, a „nieznany" sugerowałoby, że serwer odpowiedział i nic nie rozpoznaliśmy
Limity: brak rate limitu. Jak wszystkie odznaki — zawsze HTTP 200, no-cache i CORS *.

Widget do osadzenia

GET/widget?server=KOD

Nie API, tylko gotowa strona HTML do wstawienia w <iframe>: jedna karta, przezroczyste tło, bez nagłówka i sortowania. Bez parametru pokazuje sumę wszystkich śledzonych serwerów. Odświeża się sama co 15 s.

Do wklejenia na własną stronę
<iframe
  src="https://stats.bykens.eu/widget?server=okz5dj"
  width="330" height="160" style="border:0"
  title="Statystyki serwera FiveM" loading="lazy"></iframe>
ParametrZnaczenie
serverKod serwera. Pominięty → suma wszystkich. Nieznany → karta „brak danych", nie błąd HTTP
Limity: karta ma stałą wysokość 157 px, więc height="160" nigdy nie wywoła paska przewijania.