사기 방지 Data API
Data API는 Web SDK의 서버 측 대응물입니다. 판정을 가져오고, 통계를 집계하고, 보고 및 대조를 위해 데이터를 내보내는 데 사용하세요 — 당신의 시스템이 합의하는 수치를 끌어옵니다.
Base URL
https://verify.captcha.la인증
모든 Data API 요청은 사기 방지 애플리케이션 자격 증명으로 인증되며, 헤더로 전송됩니다.
X-App-Key: YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRETWARNING
X-App-Secret은 서버 측 전용입니다. 브라우저, 모바일 앱, 또는 공개 저장소에 절대 노출하지 마세요. 페이지 SDK는 항상 공개 appKey만 사용합니다.
엔드포인트
판정 가져오기
단일 방문에 대한 판정을 조회합니다(예: 특정 방문을 대조하기 위해). 식별자는 정확히 하나만 전달하세요: verdict_id(페이지가 받은 판정의 값) 또는 cid(클릭 토큰 트래픽의 클릭 ID).
GET /v1/bot/verdict?verdict_id=VERDICT_ID
GET /v1/bot/verdict?cid=CID_OF_THE_VISIT
X-App-Key: YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRETfound는 판정을 찾았는지 여부를 알려 줍니다. true이면 data에 Verdict Reference에서 설명하는 판정 필드가 담깁니다.
{
"found": true,
"data": {
"cid": null,
"verdict_id": "vd_3f9c0a7e5b1d4c2a9e8f7b6a",
"is_bot": true,
"score": 87,
"level": "high",
"action": "flag",
"reasons": ["automation", "network"],
"consistency": { "ok": false },
"degraded": false,
"created_at": "2026-06-25 00:00:00"
}
}일치하는 항목이 없으면 응답은 {"found": false}입니다. verdict_id와 cid가 모두 없는 요청은 HTTP 400으로 거부됩니다.
TIP
판정은 생성된 지 몇 초 후에 저장되므로, 방문 직후에 조회하면 found: false가 반환될 수 있습니다. 방문을 실시간으로 확인하려면 대신 판정 토큰 검증하기를 사용하세요.
판정 토큰 검증하기
페이지가 Web SDK로부터 받은 verdict_token을 검증합니다. 서버에서 콘텐츠 접근을 제어할 때 사용하세요(서버 측 검증 참고). 검증은 즉시 응답되며 저장된 데이터에 의존하지 않습니다.
POST /v1/bot/verdict/verify
X-App-Key: YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRET
Content-Type: application/json
{ "token": "vt1.eyJ2Ijoi...", "ip": "203.0.113.7", "consume": false }| 파라미터 | 필수 | 설명 |
|---|---|---|
token | 예 | 판정에 포함된 verdict_token. |
ip | 아니오 | 서버에서 본 방문자의 IP 주소. 지정하면 토큰이 방문과 같은 네트워크에서 와야 합니다. 권장. |
consume | 아니오 | true = 토큰을 한 번만 사용할 수 있으며, 두 번째 검증은 already_used를 반환합니다. 기본값 false: 만료될 때까지 다시 검증할 수 있습니다. |
{
"valid": true,
"error": null,
"verdict_id": "vd_3f9c0a7e5b1d4c2a9e8f7b6a",
"expired": false,
"expires_at": 1767225600,
"ip_match": true,
"verdict": {
"is_bot": false,
"level": "low",
"action": "record_only",
"score": 12,
"degraded": false,
"reasons": [],
"issued_at": 1767225000
}
}| 필드 | 설명 |
|---|---|
valid | 토큰이 진본이고, 사용자의 앱에 속하며, 만료되지 않았고, ip(지정한 경우)와 일치하며, consume 사용 시 아직 사용되지 않았으면 true. valid는 토큰이 진짜라는 것만 말해 줄 뿐 방문자가 사람이라는 뜻은 아니므로 verdict도 확인하세요. |
error | null, 또는 토큰이 유효하지 않은 이유: malformed, bad_signature, expired, ip_mismatch, already_used, unavailable(일회성 검사를 완료하지 못함, 재시도하세요). |
expired / expires_at | 만료 상태와 만료 시각(Unix 초). |
ip_match | ip를 지정한 경우 true / false, 그렇지 않으면 null. |
verdict | 서명된 판정: is_bot, level, action, score, degraded, reasons, issued_at. 토큰을 읽을 수 없으면 null. |
일반적인 게이트는 valid가 true이고 verdict.is_bot이 false일 때 콘텐츠를 제공합니다. verdict.degraded와 토큰이 아예 없는 요청을 어떻게 처리할지는 직접 결정하세요. 페일 오픈으로 하면 장애 중에도 실제 방문자는 통과합니다. 페일 클로즈로 하면 유효한 토큰 없이는 아무것도 통과하지 못합니다.
토큰은 기본적으로 10분 후에 만료됩니다. 새 토큰을 받으려면 SDK를 다시 실행하세요 (예: 다음 페이지 로드 시).
집계 통계
시간 범위에 걸친 집계(총계, 봇 수, 그리고 level별·일별 분해)를 가져옵니다. 대시보드와 품질 보고서를 위해 사용합니다.
GET /v1/bot/stats?from=2026-06-01&to=2026-06-30
X-App-Key: YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRET{
"from": "2026-06-01",
"to": "2026-06-30",
"cleaned": true,
"total": 124500,
"bots": 18230,
"by_level": { "low": 100300, "medium": 16900, "high": 6200, "critical": 1100 },
"by_day": [
{ "date": "2026-06-01", "total": 4100, "bots": 590 }
]
}from / to의 기본값은 최근 7일입니다.
내보내기
오프라인 대조를 위해 시간 범위의 방문별 판정 행을 내보냅니다. 행은 최신순으로, 페이지 단위로 반환됩니다.
GET /v1/bot/export?from=2026-06-01&to=2026-06-30&limit=1000
X-App-Key: YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRET| 파라미터 | 설명 |
|---|---|
from / to | 날짜 범위(YYYY-MM-DD). 선택. |
limit | 페이지당 행 수, 1–5000(기본값 1000). |
cursor | 이전 페이지의 next_cursor를 전달하면 다음 페이지를 가져옵니다. |
{
"rows": [
{
"id": 1042, "cid": null, "verdict_id": "vd_3f9c0a7e5b1d4c2a9e8f7b6a",
"is_bot": false, "score": 12, "level": "low", "action": "record_only",
"reasons": [], "consistency": { "ok": true }, "degraded": false,
"created_at": "2026-06-25 00:00:00", "rescored": false
}
],
"next_cursor": 1042
}각 행은 방문의 식별자, 타임스탬프, 그리고 판정 필드를 담으므로, 자신의 로그와 다시 조인할 수 있습니다. 마지막 페이지에서 next_cursor는 null입니다.
정제된 수치
통계와 내보내기는 기본적으로 정제된 기준으로 보고됩니다. 나중에 무효로 식별된 트래픽은 지속적으로 재평가되어 수치에서 제외됩니다. 이 과정은 비동기로 이루어지므로 아주 최근 구간의 수치는 아직 약간 변동될 수 있습니다. 정제 전 수치를 보려면 ?raw=1을 전달하세요. export에서는 각 행에 rescored 플래그도 포함됩니다.
클릭별 대조
유료 트래픽 시나리오에서는 단일 방문을 특정 전달된 클릭으로 다시 묶어 두 당사자가 그에 대해 정산할 수 있습니다. 이는 클릭 토큰을 사용하며 광고 사기 가이드에서 다룹니다.
클릭 토큰 레퍼런스 (트래픽 공급자용)
트래픽 공급자는 자체 서버에서 click token에 서명한 뒤 이를 목적지 URL에 추가합니다. 그러면 해당 방문에 대한 판정이 다시 공급자에게 귀속됩니다. 서명은 오프라인으로 이루어지며 API 호출이 필요하지 않습니다.
서명 자격 증명 발급받기
대시보드에서 앱 → Fraud Prevention → 서명 키 발급으로 이동합니다. 다음을 받게 됩니다.
bot_kid— 공개 키 ID (토큰의pkid로 들어갑니다).bot_hmac_secret— 서명 시크릿으로, 한 번만 표시됩니다. 서버 측에 보관하세요.
토큰 형식
ct.<base64url(payload)>.<base64url(HMAC_SHA256(body, bot_hmac_secret))>body는 JSON 페이로드의 base64url이며, 서명은 그 body 문자열에 대해 계산됩니다.
페이로드 필드
| 필드 | 필수 | 설명 |
|---|---|---|
pkid | 예 | 사용자의 bot_kid. 백엔드는 이를 사용해 시크릿을 조회하고 서명을 검증합니다 |
cid | 예 | 이 클릭의 고유 ID이며 정산 키입니다. 클릭마다 새로운 고유 값을 생성하세요 |
aud | 아니오 | 대상 광고주의 app_key. 설정되면 해당 광고주 페이지에서만 토큰이 인정됩니다. 생략하면 모든 광고주 페이지에서 수락됩니다 |
click_ts | 아니오 | 클릭이 발생한 시각 (unix 초) |
exp | 아니오 | 만료 시각 (unix 초). 만료된 토큰은 거부됩니다 |
링크에 추가하기
광고주의 목적지 URL에 토큰을 _ctk 쿼리 파라미터로 추가합니다.
https://advertiser.example/lp?_ctk=ct.<...>.<...>광고주의 SDK는 _ctk를 자동으로 읽습니다(tokenParam으로 설정 가능). 각 cid는 재전송 방지됩니다. 같은 방문자가 페이지를 새로 고치는 것은 괜찮지만, 다른 곳에서 재사용된 토큰은 플래그 처리됩니다. 보안 모델을 참고하세요.
예시 (의사 코드)
const payload = { pkid, cid, aud: advertiserAppKey, click_ts: now, exp: now + 900 }
const body = base64url(JSON.stringify(payload))
const sig = base64url(hmacSha256(body, botHmacSecret))
const token = `ct.${body}.${sig}`
const url = `${destination}?_ctk=${encodeURIComponent(token)}`Postback (서버 간 통신)
Data API를 폴링하는 대신, 판정이 발생하는 즉시 자신의 엔드포인트로 푸시받을 수 있습니다. 각 방문을 실시간으로 기록하거나 대조하려는 제휴 트래커와 광고주 백엔드에 유용합니다.
활성화하기
대시보드에서 앱을 열고 Fraud Prevention → Postback URL(bot_postback_url)을 설정합니다. 설정하면 모든 판정이 생성된 직후 해당 URL로 POST됩니다. 전달은 비동기이며 격리되어 있어, 엔드포인트가 느리거나 실패해도 페이지 SDK에 이미 반환된 판정에는 영향을 주지 않습니다.
발생 시점
앱에 대해 판정을 생성하는 verify마다 발생합니다. 전달은 최선 노력 방식이며 자동 재시도가 몇 차례 이루어집니다. **at-least-once(최소 1회)**로 취급하고 verdict_id로 중복을 제거하세요.
요청
POST <your bot_postback_url>
Content-Type: application/json
X-Bot-Signature: t=<unix_ts>,v=<hex>본문 (JSON):
{
"cid": "CID_OF_THE_VISIT",
"verdict_id": "vd_3f9c0a7e5b1d4c2a9e8f7b6a",
"is_bot": 1,
"score": 87,
"level": "high",
"action": "flag",
"advertiser_app_id": 12,
"provider_app_id": 34,
"created_at": "2026-06-25 00:00:00"
}| field | description |
|---|---|
cid | 방문의 대조 키 (클릭 토큰이 없었다면 null일 수 있음) |
verdict_id | 판정 ID. 페이지가 받은 값과 동일 |
is_bot | 1 / 0 |
score | 위험 점수 |
level | low / medium / high / critical |
action | 서버 권위 액션 (record_only / flag / challenge / deny) |
advertiser_app_id | verify가 실행된 앱의 ID |
provider_app_id | 클릭 토큰으로 귀속된 경우 트래픽 공급자 ID, 그렇지 않으면 null |
created_at | 판정 시각 (UTC) |
서명 검증하기
X-Bot-Signature 헤더로 요청이 실제로 당사에서 왔는지 확인할 수 있습니다. 형식은 t=<unix_ts>,v=<hex>이며 다음과 같습니다.
hex = HMAC_SHA256( "<unix_ts>." + raw_request_body , bot_hmac_secret )bot_hmac_secret은 Fraud Prevention → 서명 키 발급에서 발급하는 것과 동일한 서명 시크릿입니다. 검증하려면 수신한 원본 본문에 대해 HMAC을 다시 계산하고(JSON을 다시 직렬화하지 마세요) 상수 시간으로 비교합니다. 선택적으로, t가 자신의 시계와 너무 차이 나는 요청을 거부하여 재전송 범위를 제한하세요.
// Express example
const [tsPart, vPart] = req.get('X-Bot-Signature').split(',')
const ts = tsPart.slice(2) // "t=" prefix
const v = vPart.slice(2) // "v=" prefix
const expected = crypto
.createHmac('sha256', BOT_HMAC_SECRET)
.update(ts + '.' + rawBody) // rawBody is the unparsed request body
.digest('hex')
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v))다음 단계
- Verdict Reference — 이 엔드포인트들이 반환하는 필드
- Web SDK — 페이지에서 판정 수집하기