Ranking API
v1 · REST · JSONOverview
- Base URL:
https://toolzip.kr - 응답: JSON (UTF-8). 에러도 HTTP 200 +
ok:false - 유저당 최고 점수 1건만 저장 (lower 점수 제출은 무시됨)
| 상황 | 필요한 인증 | 사용 엔드포인트 |
|---|---|---|
| 외부 사이트/게임이 toolzip 랭킹에 점수 등록·조회 | API Key | submit / top / me |
| toolzip에 올린 게임(iframe 내부)에서 점수·평점 기록 | 세션(로그인 쿠키) | submit-self / me-self / rate / playtime |
API Key 기반 (외부 통합)
Developer 페이지에서 Key 발급 →
api_key + api_secret 확보.
점수 등록엔 HMAC-SHA256 서명 필수. 분당 30회 레이트 리미트.
점수 등록 POST
POST https://toolzip.kr/api/rank?action=submit api_key * (발급받은 Key) game_id * 게임 ID player_name * 1~30자 score * 정수 ts * UNIX timestamp (±5분) sign * HMAC-SHA256(secret, "api_key|game_id|player_name|score|ts") meta 선택, JSON 문자열 (2KB 이하)
# JS 서명 예시
const ts = Math.floor(Date.now() / 1000);
const payload = [apiKey, gameId, name, score, ts].join('|');
const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(secret),
{ name:'HMAC', hash:'SHA-256' }, false, ['sign']);
const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(payload));
const sign = [...new Uint8Array(sig)].map(b => b.toString(16).padStart(2,'0')).join('');
// 응답
{ "ok": true, "data": {
"score": 12345,
"improved": true, // 기존 최고점 갱신 여부
"best_score": 12345, // 현재 최고점
"rank": 7 // 현재 순위 (null = 숨김 처리된 유저)
}}
랭킹 조회 GET
GET https://toolzip.kr/api/rank?action=top&api_key=...&game_id=1&limit=10&offset=0
// 응답
{ "ok": true, "data": {
"game_id": 1, "total": 124,
"items": [ { "rank":1, "player_name":"Alice", "score":9999, "created_at":"..." }, ... ]
}}
특정 플레이어 조회 GET
GET https://toolzip.kr/api/rank?action=me&api_key=...&game_id=1&player_name=Alice
// 응답: { ok, data: { player_name, best_score, rank } }
cURL 예제
API_KEY=tz_xxxxxxxxxxxx
SECRET=yyyyyyyyyyyyyyyy
GAME=1 NAME=Alice SCORE=9999 TS=$(date +%s)
SIGN=$(printf "%s|%s|%s|%s|%s" "$API_KEY" "$GAME" "$NAME" "$SCORE" "$TS" \
| openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
curl -X POST "https://toolzip.kr/api/rank?action=submit" \
-d "api_key=$API_KEY" -d "game_id=$GAME" -d "player_name=$NAME" \
-d "score=$SCORE" -d "ts=$TS" -d "sign=$SIGN"
curl "https://toolzip.kr/api/rank?action=top&api_key=$API_KEY&game_id=$GAME&limit=10"
세션 기반 (toolzip 내부 게임)
toolzip에 업로드된 게임은 같은 도메인에서 iframe으로 실행되므로 로그인 쿠키가 자동 전송됩니다.
API Key 불필요. player_name은 자동으로 현재 유저의 username.
부모창은 iframe에 window.parent.__toolzip = { gameId, username, canSubmit }를 주입합니다.
점수 등록 POST
POST https://toolzip.kr/api/rank?action=submit-self
game_id *, score *, meta (optional)
// 응답
{ "ok": true, "data": {
"score": 1500, "improved": true,
"best_score": 1500, "rank": 3, "total_players": 42,
"is_dev_record": false, // 로그인 유저 == 게임 개발자
"is_admin_record": false // 로그인 유저 == 관리자
}}
내 순위 GET
GET https://toolzip.kr/api/rank?action=me-self&game_id=1
// 응답: { ok, data: { player_name, best_score, rank, total_players, is_dev_record, is_admin_record } }
평점 POST
POST https://toolzip.kr/api/rate?game_id=1 stars=1..10 (0.5단위, 10=5.0)
DELETE https://toolzip.kr/api/rate?game_id=1 (POST + _method=DELETE 도 가능)
GET https://toolzip.kr/api/rate?game_id=1 로그인 유저 평가+평균 조회
// 응답: { ok, data: { avg: 4.2, count: 18, mine: 8 } }
플레이타임 누적 POST
POST https://toolzip.kr/api/playtime game_id *, seconds (1..300)
// 응답: { ok, data: { total_playtime_seconds } }
// 내부 play 페이지가 30초 간격으로 자동 전송.
JS 사용 예
const tz = window.parent && window.parent.__toolzip;
if (tz && tz.username) {
const fd = new FormData();
fd.append('game_id', tz.gameId);
fd.append('score', 9999);
await fetch('/api/rank?action=submit-self', {
method: 'POST', body: fd, credentials: 'include'
});
}
에러 코드
| code | 의미 |
|---|---|
missing_param | 필수 파라미터 누락 |
invalid_key | API Key 유효하지 않음 |
key_revoked | 폐기된 Key |
key_game_mismatch | Key가 연결된 게임과 불일치 |
invalid_game | 존재하지 않거나 승인되지 않은 게임 |
invalid_signature | 서명 불일치 |
expired_ts | timestamp 만료 (±5분) |
rate_limited | 요청 빈도 초과 |
not_logged_in | 세션 기반 엔드포인트에서 로그인 쿠키 없음 |
invalid_stars | 별점이 1..10 범위가 아님 |