APK 및 URL 검사 API

URL 검사는 JSON을 동기 반환합니다. APK는 파일, 전체 지문 또는 패키지명으로 비동기 제출하고 서명된 Webhook으로 결과를 전달합니다.

시작하기

개요

URL 검사는 요청 안에서 끝나며 APK 검사는 비동기입니다. 모든 검사 엔드포인트는 계정 단위 속도 창을 공유합니다.

01제출

검사 요청 또는 APK 업로드

02검토

검증, 중복 제거 및 검사 실행

03콜백

서명 Webhook으로 결과 전달

APK API는 폴링을 지원합니다

수락 응답에 job_id가 포함됩니다. GET /api/v1/jobs/{job_id}로 약 24시간 폴링할 수 있습니다. callback_url은 선택 사항이며, 지정 시 서명 Webhook도 전달됩니다.

JSON · common APK fields
{
  "client_reference": "order-20260711-001",
  "callback_url": "https://example.com/webhooks/fvl",
  "use_cache": false
}
접근 제어

인증

각 요청에 두 헤더 중 하나를 사용합니다. 키는 생성 시 한 번만 전체 표시되며 서버에 보관해야 합니다.

권장Authorization: Bearer fvl_xxx
호환X-API-Key: fvl_xxx

API 키를 브라우저, 모바일 앱, 공개 저장소 또는 로그에 넣지 마십시오.

접근 제어

할당량 및 속도 제한

API는 VIP+, SVIP, SVIP+ 및 관리자에게 제공됩니다. 한 계정의 모든 API 키가 속도 창을 공유합니다.

요금제최소 간격일일 R2 업로드
VIP+10분2 GiB
Pro3분5 GiB
SVIP5분10 GiB
SVIP+1분30 GiB
관리자무제한무제한
URL, 업로드 세션 생성, APK 지문 및 패키지 검사는 계정 속도를 공유합니다. 캐시 적중, 멱등 재생 및 진행 중 작업 병합은 새 창을 열지 않습니다.
사용자마다 유효한 미완료 업로드는 1개까지입니다. R2 세션 생성 시 재검사 1회를 선차감하며 발급 후 환불되지 않습니다.
API

엔드포인트

서명된 R2 PUT 단계를 제외하고 요청과 응답은 JSON입니다.

POST/api/v1/url-scanURL 동기 검사

url을 보내고 위험 결과와 차감 후 quota를 같은 응답으로 받습니다. Webhook은 사용하지 않습니다.

cURL
curl -X POST https://www.fuckviruslabel.com/api/v1/url-scan \
  -H "Authorization: Bearer fvl_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/path"}'
HTTP 200
{
  "code": 0,
  "message": "success",
  "data": {
    "result": {
      "status": "completed",
      "url": "https://example.com/path",
      "domain": "example.com",
      "flagged": false,
      "mainType": 0,
      "mainTypeName": "安全",
      "provider": "OPPO",
      "websiteType": 0,
      "matchUrl": "",
      "riskDetail": "",
      "cacheStatus": "live",
      "checkedAt": 1783765471000,
      "createdAt": 1783765471000
    },
    "quota": {
      "identity_label": "VIP+",
      "daily_limit": 20,
      "daily_used": 1,
      "daily_remaining": 19,
      "scan_credits": 0,
      "total_remaining": 19,
      "usage_date": "2026-07-11"
    }
  }
}
POST/api/v1/apk/uploadsAPK 업로드 세션 생성

Idempotency-Key, .apk 파일명, size, file_md5, expected_sha256 및 공통 콜백 필드가 필요하며 URL은 30분간 유효합니다.

cURL
curl -X POST https://www.fuckviruslabel.com/api/v1/apk/uploads \
  -H "Authorization: Bearer fvl_xxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: upload-20260711-001" \
  -H "Content-Type: application/json" \
  -d '{
    "client_reference": "order-20260711-001",
    "callback_url": "https://api.example.com/webhooks/fvl",
    "use_cache": false,
    "filename": "release.apk",
    "size": 24117384,
    "file_md5": "0123456789abcdef0123456789abcdef",
    "expected_sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  }'
PUT · R2 required_headers
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/vnd.android.package-archive" \
  -H "Content-Length: 24117384" \
  -H "Content-MD5: ASNFZ4mrze8BI0VniavN7w==" \
  -H "If-None-Match: *" \
  --upload-file ./release.apk
POST/api/v1/apk/uploads/complete업로드 완료 및 검토 제출

upload_token과 Idempotency-Key를 보냅니다. 객체를 확인하고 추가 차감 없이 선결제 검토를 제출합니다.

cURL
curl -X POST https://www.fuckviruslabel.com/api/v1/apk/uploads/complete \
  -H "Authorization: Bearer fvl_xxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: order-20260711-001" \
  -H "Content-Type: application/json" \
  -d '{"upload_token":"upl_xxxxxxxxxxxxxxxx"}'
POST/api/v1/apk/fingerprintsAPK 지문 제출

package_name, sha256, file_md5, cert_md5와 공통 콜백 필드를 보내 APK 재업로드를 생략합니다.

cURL
curl -X POST https://www.fuckviruslabel.com/api/v1/apk/fingerprints \
  -H "Authorization: Bearer fvl_xxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: fingerprint-20260711-001" \
  -H "Content-Type: application/json" \
  -d '{
    "client_reference": "fingerprint-20260711-001",
    "callback_url": "https://api.example.com/webhooks/fvl",
    "use_cache": true,
    "package_name": "com.example.app",
    "sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
    "file_md5": "0123456789abcdef0123456789abcdef",
    "cert_md5": "abcdef0123456789abcdef0123456789",
    "version_code": 108,
    "version_name": "1.0.8",
    "size": 24117384,
    "filename": "release.apk"
  }'
POST/api/v1/packages/check패키지명 검사

기기 측 패키지 위험을 빠르게 확인합니다. 해시와 인증서를 포함한 전체 APK 검사는 아닙니다.

cURL
curl -X POST https://www.fuckviruslabel.com/api/v1/packages/check \
  -H "X-API-Key: fvl_xxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: package-20260711-001" \
  -H "Content-Type: application/json" \
  -d '{
    "client_reference": "package-20260711-001",
    "callback_url": "https://api.example.com/webhooks/fvl",
    "use_cache": false,
    "package_name": "com.example.app"
  }'
HTTP 202
{
  "code": 0,
  "message": "accepted",
  "data": {
    "client_reference": "order-20260711-001",
    "job_id": "acd_xxxxxxxxxxxxxxxx",
    "delivery": "webhook_and_poll",
    "poll_path": "/api/v1/jobs/acd_xxxxxxxxxxxxxxxx"
  }
}
GET/api/v1/jobs/{job_id}비동기 작업 폴링

수락 응답의 job_id로 pending / processing / completed / failed를 조회합니다. 결과는 약 24시간 보관됩니다.

cURL
curl -X POST https://www.fuckviruslabel.com/api/v1/packages/check \
  -H "X-API-Key: fvl_xxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: package-20260711-001" \
  -H "Content-Type: application/json" \
  -d '{
    "client_reference": "package-20260711-001",
    "callback_url": "https://api.example.com/webhooks/fvl",
    "use_cache": false,
    "package_name": "com.example.app"
  }'
HTTP 202
{
  "code": 0,
  "message": "accepted",
  "data": {
    "client_reference": "order-20260711-001",
    "job_id": "acd_xxxxxxxxxxxxxxxx",
    "delivery": "webhook_and_poll",
    "poll_path": "/api/v1/jobs/acd_xxxxxxxxxxxxxxxx"
  }
}
API

발급 전 선차감, 발급 후 환불 없음

발급 전 선차감, 발급 후 환불 없음

멤버십, 계정 속도, 재검사 할당량, 일일 업로드 바이트 및 미완료 세션을 확인한 뒤 1회를 차감하고 URL을 발급합니다. 미완료, 만료, 해시 불일치, 잘못된 APK 또는 큐 실패에도 환불되지 않습니다. 일시적 큐 실패는 샘플을 보존하고 자동 재시도합니다.

샘플 보존

정상 APK는 SHA-256으로 중복 제거해 보관하고 잘못되거나 불일치한 샘플은 quarantine으로 이동합니다. 모두 90일 보존하며 미완료 업로드는 1일 후 삭제합니다.

요청 제어

use_cache 의미

use_cache는 JSON 루트 필드이며 기본값은 false입니다. R2는 값과 관계없이 URL 발급 전에 선차감하고 완료 후 결과 재사용만 제어합니다.

false강제 재검사

false는 기존 결과를 우회하고 실제 검사가 시작될 때 1회를 사용합니다.

cache_status = bypass
true캐시 허용

true는 유효 캐시 적중 시 차감 없이 반환하며 미적중 또는 만료 시 재검사합니다.

cache_status = hit | miss
결과 전달

서명 Webhook

Webhook은 비동기 APK 검사의 유일한 결과 채널입니다. 원본 JSON, 타임스탬프 및 HMAC-SHA256 서명을 보냅니다.

X-FVL-TimestampX-FVL-Timestamp: 1720000000
X-FVL-SignatureX-FVL-Signature: sha256=<hex>
  1. webhook_secret = lowercase_hex(SHA-256(raw_api_key))를 계산합니다.
  2. X-FVL-Timestamp + "." + raw_body에 서명합니다.
  3. webhook_secret의 UTF-8 바이트를 HMAC-SHA256 키로 사용하고 일정 시간 비교합니다.
  4. 타임스탬프를 검증하고 2xx를 반환합니다. 비 2xx는 재시도됩니다.
Webhook JSON
{
  "event": "apk.scan.completed",
  "client_reference": "order-20260711-001",
  "operation": "apk_upload",
  "status": "completed",
  "cache_status": "bypass",
  "result": { "flagged": false, "result_type_name": "安全" },
  "quota": { "refresh_daily_remaining": 29, "total_remaining": 29 },
  "checked_at": "2026-07-11T10:24:31.000Z"
}
Node.js · HMAC-SHA256
import crypto from "node:crypto";

export function verifyFvlWebhook(rawBody, timestamp, signature, apiKey) {
  const webhookSecret = crypto
    .createHash("sha256")
    .update(apiKey, "utf8")
    .digest("hex");
  const expected = "sha256=" + crypto
    .createHmac("sha256", webhookSecret)
    .update(timestamp + "." + rawBody)
    .digest("hex");

  const expectedBytes = Buffer.from(expected, "utf8");
  const signatureBytes = Buffer.from(signature, "utf8");

  return expectedBytes.length === signatureBytes.length &&
    crypto.timingSafeEqual(expectedBytes, signatureBytes);
}

JSON 파싱 전에 raw body를 읽으십시오. 재직렬화하면 서명 검증이 실패합니다.

요청 제어

멱등성 및 중복 제거

업로드 생성·완료, 지문 및 패키지 검사에는 Idempotency-Key가 필요합니다.

같은 키와 요청은 24시간 같은 결과를 반환하며 다시 차감하지 않습니다.
같은 키를 다른 요청에 사용하면 HTTP 409를 반환합니다.
같은 자산의 동시 재검사는 한 번으로 병합되고 각 호출은 자체 Webhook을 받습니다.
참조

오류

동기 오류는 HTTP 상태와 통일된 JSON을 사용합니다. 비동기 APK 검토 실패는 scan.failed Webhook으로 전달됩니다.

HTTP의미조치
400잘못된 요청 또는 필드JSON, 해시, 패키지명 또는 콜백 URL을 수정합니다.
401인증 실패API 키를 확인합니다.
403접근 거부요금제와 만료일을 확인합니다.
409상태 충돌멱등 키 또는 업로드 상태를 확인합니다.
429할당량 또는 속도 제한Retry-After에 따라 나중에 재시도합니다.
503서비스 사용 불가동일한 멱등 키로 엔드포인트 규칙에 따라 재시도합니다.
Node.js
const body = {
  client_reference: "package-20260711-001",
  callback_url: "https://api.example.com/webhooks/fvl",
  use_cache: true,
  package_name: "com.example.app"
};

const response = await fetch(
  "https://www.fuckviruslabel.com/api/v1/packages/check",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer " + process.env.FVL_API_KEY,
      "Content-Type": "application/json",
      "Idempotency-Key": body.client_reference
    },
    body: JSON.stringify(body)
  }
);

if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Python
import os
import requests

payload = {
    "client_reference": "package-20260711-001",
    "callback_url": "https://api.example.com/webhooks/fvl",
    "use_cache": True,
    "package_name": "com.example.app",
}

response = requests.post(
    "https://www.fuckviruslabel.com/api/v1/packages/check",
    headers={
        "Authorization": f"Bearer {os.environ['FVL_API_KEY']}",
        "Idempotency-Key": payload["client_reference"],
    },
    json=payload,
    timeout=20,
)
response.raise_for_status()
print(response.json())
기계 판독 OpenAPI 3.1클라이언트 생성, 요청 검증 또는 API 도구 가져오기에 사용합니다.
참조

변경 기록

v1.0.0

초기 릴리스: URL, R2 업로드, APK 지문, 패키지 검사, 캐시, 멱등성 및 서명 Webhook.