Wardog Servers API
이 사이트의 데이터를 무료 읽기 전용 JSON API로 쓸 수 있어요: 게임 내 브라우저에 나오는 모든 WARDOGS 서버(약 1분마다 갱신), 차트용 플레이어 기록, 참가 코드별 서버 기록.
개요
기본 URL: https://api.wardogservers.com. 모든 응답은 JSON이고, 읽기에는 API 키가 필요 없어요.
전체 명세는 https://api.wardogservers.com/openapi.json ↗에 OpenAPI 3.1 문서로 공개돼 있어요. 기준은 그 문서이고, 이 페이지는 읽기 쉽게 정리한 요약이에요.
- 수집기는 모든 지역의 게임 내 서버 브라우저를 약 1분마다 조회해요. 현재 주기는 모든 응답의 meta.refreshSeconds에 나와요.
- 브라우저 요청은 wardogservers.com에서 온 것만 받아요. 다른 도메인의 웹 페이지가 아니라 직접 운영하는 서버, 봇, 스크립트에서 API를 호출하세요.
- 알 수 없거나 중복되었거나 형식이 잘못된 쿼리 파라미터는 무시하지 않고 400으로 거부해요. 그래서 오타가 있으면 조용히 전체 결과를 돌려주는 대신 확실하게 실패해요.
빠른 시작
대부분은 요청 세 가지로 충분해요: 현재 합계, 필터에 맞는 가장 붐비는 서버, 인스턴스 ID로 조회하는 서버 하나.
# 현재 합계
curl https://api.wardogservers.com/v1/stats
# 참가할 자리가 있는 유럽 서부 커뮤니티 서버, 붐비는 순
curl "https://api.wardogservers.com/v1/servers?region=eu-west&type=community&hideFull=true&limit=10"
# 인스턴스 ID로 서버 하나 조회
curl https://api.wardogservers.com/v1/servers/$ID /v1/stats 응답을 줄여 보면 이래요:
{
"data": {
"serverCount": 3544,
"playerCount": 252109,
"officialServerCount": 2765,
"communityServerCount": 779
},
"meta": {
"fetchedAt": "2026-09-13T12:54:57.302Z",
"snapshotId": "95635cb938811a34…",
"refreshSeconds": 60,
"stale": false,
"requestedRegions": ["asia-east", "asia-southeast", "…"],
"observedRegions": ["asia-east", "asia-west", "…"]
}
} 엔드포인트
모든 읽기 엔드포인트는 GET과 HEAD를 받고, If-None-Match를 지원하며, 아래에 설명한 응답 엔벨로프를 써요.
| 엔드포인트 | 반환값 |
|---|---|
GET /v1/servers | 필터, 정렬, 페이지 나누기를 지원하는 서버 목록. |
GET /v1/servers/{id} | 추가 상세 필드가 있는 서버 하나: 원본 속성, 빌드, 레벨·자금 제한. |
GET /v1/snapshot | 현재 스냅샷의 모든 서버를 인스턴스 ID순으로, 필터나 페이지 나누기 없이 반환해요. 전체 목록을 받아 로컬에서 필터하는 클라이언트용이에요. |
GET /v1/regions | 지역별 서버 수와 플레이어 수, 그리고 마지막 조회에서 각 지역을 요청했는지와 관측했는지. |
GET /v1/stats | 합계: 온라인 서버와 플레이어 수, 공식과 커뮤니티로 구분. |
GET /v1/history/players | 차트용 플레이어·서버 시계열. |
GET /v1/history/heatmap | 최근 28일 동안 UTC 요일·시간대별 지표 평균. |
GET /v1/history/peaks | 역대 및 일일 플레이어 최고치, 전체와 서버 유형별. |
GET /v1/history/servers | 수집기가 기록한 서버를 참가 코드별로, 최근에 본 순서대로 반환해요. 지금 오프라인인 서버도 찾을 수 있어요. |
GET /v1/history/servers/ranking | 가동률 × 플레이어 수로 매긴 서버 순위: 최근 하루, 일주일, 한 달 동안의 평균 플레이어 수이며, 오프라인 시간은 0으로 계산해요. |
GET /v1/history/servers/{serverId} | 참가 코드로 지정한 서버 하나의 버킷별 플레이어, 최고치, 최대 인원, 가동률, 맵. |
GET /v1/history/servers/{serverId}/events | 서버 하나의 변경 기록, 최신순: 온라인, 오프라인, 재시작, 빌드, 맵, 모디파이어, 제한 변경. |
GET /readyz | 최신 스냅샷이 있으면 200, 없거나 오래됐으면 503. |
GET /openapi.json | OpenAPI 3.1 문서. |
서버 목록 필터링
/v1/servers의 모든 파라미터는 선택 사항이에요. q를 제외한 값은 대소문자를 구분해요.
| 파라미터 | 값 | 의미 |
|---|---|---|
q | 텍스트, 최대 200자 | 인스턴스 ID, 참가 코드, 이름, 지역, 서버 번호, 맵, 모드를 대소문자 구분 없이 검색해요. |
region | eu-west,eu-central | 지역 ID 하나 이상, 쉼표로 구분. 현재 목록은 /v1/regions에 있어요. |
type | all · official · community, 기본값 all | 공식 서버, 커뮤니티 서버, 또는 둘 다. |
hideEmpty | true · false | 플레이어가 0명인 서버 제외. |
hideFull | true · false | 가득 찬 서버 제외(0/0 포함). |
passwordProtected | true · false | 잠긴 서버만 또는 열린 서버만. 둘 다 보려면 생략. |
sort | -players · players · name · region | -players는 붐비는 순이고 기본값이에요. |
limit | 1–500, 기본값 100 | 페이지 크기. |
offset | 0–100000, 기본값 0 | 필터·정렬된 결과에서 0부터 시작하는 오프셋. |
응답 엔벨로프
성공한 응답에는 항상 data와 meta가 있어요. 목록 엔드포인트에는 pagination이 추가되고, 기록 엔드포인트에는 기간, 그룹, 지표, 버킷 크기를 설명하는 history 객체가 추가돼요.
수치를 현재 값으로 보여 주기 전에 meta.stale을 확인하세요. 수집이 멈췄거나 스냅샷이 refreshSeconds + 60초보다 오래되면 true예요. 오래된 응답에는 Cache-Control: no-store와 X-Data-Stale: true도 함께 붙어요.
| 필드 | 의미 |
|---|---|
meta.fetchedAt | 수집기가 이 스냅샷을 가져온 시각. |
meta.snapshotId | 스냅샷 식별자. 새 데이터가 들어올 때마다 바뀌어요. X-Snapshot-Id 헤더로도 보내요. |
meta.refreshSeconds | 수집기의 조회 주기. 이보다 자주 조회하지 마세요. |
meta.stale | 데이터를 실시간으로 보여 주면 안 될 때 true. |
meta.requestedRegions | 수집기가 게임에 요청한 지역. |
meta.observedRegions | 이 스냅샷에서 서버를 하나 이상 반환한 지역. |
서버 객체
/v1/servers와 /v1/snapshot이 반환하는 요약 형태예요. /v1/servers/{id}에서는 같은 필드에 attributes, build, level, cash를 더해 반환해요.
{
"id": "001ab36e-9985-4a9c-8dff-9dc137826dfe",
"serverId": "756872",
"serverIdUpdatedAt": "2026-09-13T11:14:32.995Z",
"name": "Official #4",
"nativeName": "f14859cce268",
"type": "official",
"region": "eu-south",
"players": 100,
"maxPlayers": 100,
"reservedPlayers": 0,
"passwordProtected": false,
"serverNumber": 4,
"map": { "variant": "Bakurani", "base": "Europe" },
"mode": { "experience": "Bakurani_KOTH_01", "gameMode": "GM_Session_C", "modifiers": [] },
"rulesets": ["standard"]
} | 필드 | 의미 |
|---|---|
id | 실시간 게임 인스턴스 ID. 서버가 재시작하면 바뀌니 영구 키로 쓰지 마세요. |
serverId | 게임 내 참가 코드(문자열): 공식 서버는 앞자리 0을 유지한 10진수, 커뮤니티 서버는 게임 패치 0.11부터 UUID. 아직 수집하지 않았으면 null. |
serverIdUpdatedAt | 참가 코드를 마지막으로 조회한 시각. serverId가 null이면 항상 null. |
name | 공식 서버는 Official #N, 그 밖에는 디코딩된 서버 이름. 비어 있을 수 있어요. |
nativeName | 게임이 보고하는 원본 서버 이름. |
type | official 또는 community. |
region | 지역 ID(예: eu-west). |
players · maxPlayers · reservedPlayers | 현재, 최대, 예약 플레이어 수. |
passwordProtected | 참가에 비밀번호가 필요한지 여부. |
serverNumber | 공식 서버 번호. 커뮤니티 서버는 0. |
map | 맵 변형과 기본 맵. 둘 중 어느 쪽이든 null일 수 있어요. |
mode | experience와 게임 모드 이름, 그리고 experience 문자열에 있는 원본 + 모디파이어(원래 순서대로). |
rulesets | 모디파이어에서 도출한 standard, hardcore, infantry-only. 인식된 게 없으면 정확히 ["standard"]. |
기록
/v1/history/players는 열 형식 시계열을 반환해요: 버킷 시작 시각(Unix 초) 배열 하나와 그룹 키마다 값 배열 하나가 인덱스로 맞춰져 있어요. null 값은 아무것도 기록되지 않은 버킷으로, 보통 수집기가 멈췄던 경우예요. 보간하지 말고 빈 구간으로 표시하세요.
버킷은 기록 기능을 켠 순간부터 스냅샷마다 한 번씩 기록되고, 과거 데이터를 채워 넣지는 않아요. 1분 버킷은 7일, 5분 버킷은 90일, 1시간 버킷은 무기한 보관해요.
| 파라미터 | 값 | 의미 |
|---|---|---|
window | 24h · 7d · 30d · 90d · 1y, 기본값 24h | 현재 시점까지의 기간. 버킷 간격은 24h는 60초, 7d는 300초, 30d와 90d는 3600초, 1y는 21600초예요. |
group | total · type · region · regionType · map · ruleset, 기본값 total | 키마다 시계열 하나: 합계; 공식과 커뮤니티; 지역; region|type; 맵 변형; 또는 주 룰셋. |
metric | players · servers · full · empty · capacity · free, 기본값 players | players, servers, full(가득 참), empty, capacity(maxPlayers 합계), free(capacity에서 players를 뺀 값). 각 값은 버킷 안 스냅샷들의 평균이에요. |
GET /v1/history/players?window=24h&group=type
{
"data": {
"t": [1789217880, 1789217940, 1789218000, "…"],
"series": {
"official": [248112, 248390, null, "…"],
"community": [3902, 3911, null, "…"]
},
"peak": { "official": ["…"], "community": ["…"] }
},
"history": {
"window": "24h", "group": "type", "metric": "players",
"step": 60, "from": 1789217880, "to": 1789304280, "since": 1789257600
},
"meta": { "…": "…" }
} metric=players일 때는 응답에 각 버킷에서 가장 높았던 단일 스냅샷 값을 담은 peak 객체도 들어 있어요. 히트맵 엔드포인트는 최근 28일(온전한 날 기준) 동안 UTC 요일·시간대별 지표 평균을 내고, 지역 하나로 좁힐 수도 있어요. 최고치 엔드포인트는 단일 스냅샷 기준 최고 플레이어 수를 전체와 유형별로, 역대 및 최근 30일(UTC) 각각에 대해 알려 줘요.
서버별 기록
서버별 기록은 참가 코드를 키로 써요. 참가 코드는 인스턴스 ID와 달리 재시작과 맵 변경 후에도 유지돼요. /v1/history/servers/{serverId}에서는 인덱스로 맞춘 배열을 반환해요: 서버가 목록에 있던 동안의 평균 플레이어 수와 단일 조회 최고치, 최대 인원, 버킷 안에서 서버가 목록에 있었던 조회 비율(가동률, 0~1), 버킷이 끝날 때의 맵. 그래서 연속된 맵 값을 보면 로테이션을 알 수 있어요. 코드를 처음 본 순간부터 기록하고 과거 데이터는 채우지 않으며, 그 전까지 이 경로는 404를 반환해요.
| 파라미터 | 값 | 의미 |
|---|---|---|
window | 24h · 7d · 30d · 90d, 기본값 24h | 24h와 7d는 7일간 보관하는 5분 버킷을, 30d와 90d는 90일간 보관하는 1시간 버킷을 써요. |
GET /v1/history/servers/756872?window=24h
{
"data": {
"server": {
"serverId": "756872",
"name": "Official #4",
"type": "official",
"region": "eu-south",
"instanceId": "001ab36e-9985-4a9c-8dff-9dc137826dfe",
"firstSeen": "2026-09-13T00:00:39.000Z",
"lastSeen": "2026-09-14T11:53:22.000Z",
"samples": 2132,
"live": true
},
"t": [1789300800, 1789301100, 1789301400, "…"],
"players": [100, 98, null, "…"],
"peak": [100, 100, null, "…"],
"capacity": [100, 100, null, "…"],
"online": [1, 0.8, null, "…"],
"map": ["Bakurani", "Bakurani", null, "…"]
},
"history": {
"window": "24h", "step": 300,
"from": 1789300800, "to": 1789387200, "since": 1789257600
},
"meta": { "…": "…" }
} /v1/history/servers/{serverId}/events에서는 연속된 조회 결과로 변경 기록을 만들어 최신순으로 반환해요: tracking_started, online, offline, restart(공백 없이 인스턴스가 바뀜), build(changelist), map(variant, world, sky), modifiers, limits(레벨·자금 범위, 비밀번호, 최대 인원). 각 이벤트에는 at, kind, 그리고 해당 종류를 정의하는 필드만 담은 before와 after 객체가 있고, 이전이나 이후 상태가 없으면 null이에요. limit은 1~500이고 기본값은 100이에요.
GET /v1/history/servers/756872/events?limit=3
{
"data": {
"server": { "serverId": "756872", "name": "Official #4", "…": "…" },
"events": [
{
"at": "2026-09-14T12:40:30.000Z",
"kind": "map",
"before": { "map": "Madrid", "world": "Europe", "sky": "DayEarlyFog" },
"after": { "map": "Detroit", "world": "Europe", "sky": "DayLateGray" }
},
{
"at": "2026-09-14T09:12:04.000Z",
"kind": "restart",
"before": { "instanceId": "001ab36e-9985-4a9c-8dff-9dc137826dfe" },
"after": { "instanceId": "7c1e0b52-3d9a-4e1f-9b6e-2f4c8a9d0e11" }
},
{
"at": "2026-09-13T00:00:39.000Z",
"kind": "tracking_started",
"before": null,
"after": { "instanceId": "…", "changelist": "501228", "map": "Bakurani", "…": "…" }
}
]
},
"history": { "limit": 3, "since": 1789257600 },
"meta": { "…": "…" }
} /v1/history/servers는 기록된 서버를 최근에 본 순서로 보여 줘요. q는 참가 코드와는 정확히, 마지막으로 알려진 이름과는 대소문자 구분 없이 일치하는지 보기 때문에 지금 오프라인인 서버도 찾을 수 있어요. 온라인 서버는 이미 /v1/servers에 serverId가 들어 있어요.
| 파라미터 | 값 | 의미 |
|---|---|---|
q | 텍스트, 최대 200자 | 정확히 일치하는 참가 코드, 또는 대소문자 구분 없이 일치하는 마지막으로 알려진 이름. |
type | all · official · community, 기본값 all | 공식 서버, 커뮤니티 서버, 또는 둘 다. |
limit | 1–100, 기본값 20 | 반환할 서버 수. |
캐싱과 폴링
모든 응답에는 ETag가 있어요. 이를 If-None-Match로 다시 보내면 스냅샷이 바뀌지 않은 경우 본문 없이 304로 응답하므로, 양쪽 모두 비용이 거의 들지 않아요. If-None-Match와 함께 1분에 한 번 조회하는 게 의도된 사용 방식이에요.
const res = await fetch('https://api.wardogservers.com/v1/snapshot', {
headers: lastEtag ? { 'if-none-match': lastEtag } : {}
});
if (res.status === 304) return; // 마지막 조회 이후 바뀐 게 없음
lastEtag = res.headers.get('etag');
const { data, meta } = await res.json();
if (meta.stale) console.warn('stale'); // 수집기가 늦어지는 중. 마지막으로 받은 데이터를 계속 표시 최신 응답은 엣지에서 15초 동안 캐시되므로 같은 요청이 몰려도 부담이 적어요. 지금은 엄격한 요청 제한이 없지만, meta.refreshSeconds보다 자주 조회해도 더 새로운 데이터는 받을 수 없고 앞으로 속도가 제한될 수 있어요. 전체 목록이 필요하면 /v1/servers를 페이지별로 넘기지 말고 /v1/snapshot을 한 번 받아서 로컬에서 필터하세요.
오류
오류는 고정된 code와 사람이 읽을 수 있는 message가 담긴 JSON이에요. message는 바뀔 수 있으니 code로 판별하세요.
{
"error": {
"code": "bad_request",
"message": "An unsupported query parameter was supplied."
}
} | 상태 | 발생 조건 |
|---|---|
400 | 알 수 없거나 중복되었거나 잘못된 쿼리 파라미터, 또는 UUID가 아닌 ID. |
401 | API 키가 필요한데 없거나 잘못됨. 공개 접근이 켜져 있는 동안 읽기 요청에는 반환되지 않아요. |
404 | 현재 스냅샷에 해당 ID의 서버가 없거나, 해당 참가 코드의 기록이 없거나, 알 수 없는 경로. |
405 | GET이나 HEAD가 아닌 메서드. |
503 | 쓸 수 있는 스냅샷이 없음. 1분 뒤나 /readyz가 200을 반환할 때 다시 시도하세요. |
이용 조건
API는 커뮤니티 프로젝트에 무료예요: Discord 봇, 오버레이, 대시보드, 연구. 데이터를 공개적으로 보여 줄 때는 Wardog Servers 링크로 출처를 밝히고, BULKHEAD나 Team17의 공식 데이터처럼 보이게 하지 마세요.
읽기 API는 /v1 버전으로 관리돼요. 같은 버전 안에서도 새 필드가 추가될 수 있으니 모르는 키가 있어도 문제없게 처리하세요. 호환성이 깨지는 변경은 새 버전으로 내고, 이 페이지에서 먼저 알릴게요.
질문, 자랑하고 싶은 프로젝트, 필요한 필드가 있다면: Discord @xcausxn.