APK 与网站检测 API

网站检测同步返回 JSON;APK 可通过文件上传、完整特征或包名异步检测,结果发送到经过校验的签名 Webhook。

开始使用

概览

网站检测在请求内同步完成;APK 检测采用异步扫描和签名回调。所有对外检测端点按账号共享同一个会员等级频率窗口,调用记录只在平台管理员的聚合历史中留存。

01提交

发送检测请求或先直传 APK

02审查

校验特征、去重并执行扫描

03回调

通过签名 Webhook 投递结果

APK API 支持轮询

受理响应返回 job_id,可用 GET /api/v1/jobs/{job_id} 轮询结果(约保留 24 小时)。callback_url 可选;提供时同时投递签名 Webhook。

APK 异步提交公共根字段

字段类型说明
client_referencestring · 必填调用方自己的业务标识,1–128 字符;Webhook / 轮询结果原样返回。
callback_urlstring · 可选公网 HTTPS 回调地址;省略时仅通过 job_id 轮询取结果。禁止 localhost、私网、IP 字面量。
use_cacheboolean · 默认 false是否允许直接使用有效缓存。该字段位于请求 JSON 根级。
访问控制

认证

每次请求使用以下任一请求头。密钥只在创建时完整显示,必须存放在服务端。

推荐Authorization: Bearer fvl_xxx
兼容X-API-Key: fvl_xxx

不要把 API 密钥写入浏览器、移动应用、公开仓库或日志。密钥吊销后,使用该密钥签发但尚未提交的上传会话也应视为不可用。

访问控制

额度与权限

网站和 APK 检测 API 仅开放给 VIP+、SVIP、SVIP+ 和管理员;强制复查沿用账号现有每日复查额度。

APK 特征和包名检测命中缓存时不扣复查次数
APK 特征和包名检测未命中缓存或强制复查时按实际扫描扣一次
同一用户、同一资产的并发复查自动合并

OSS 上传是独立的付费审查入口:创建上传会话时先扣一次复查次数并占用频率窗口。即使后续命中缓存或扫描合并,也不返还本次上传预扣。

账号级共享频率

身份组最短间隔限制范围
VIP+10 分钟APK 上传会话创建、APK 特征、包名和网站检测共用;同一账号的所有 API Key 共用。
Pro3 分钟
SVIP5 分钟
SVIP+1 分钟
管理员不限制

对特征和包名检测,有效缓存命中、24 小时内的 Idempotency-Key 重放,以及合并进同一在途 APK 任务的请求不会开启新窗口。OSS 上传在创建会话时开启窗口,完成接口不再计入。超限返回 HTTP 429,并通过 Retry-After 响应头给出最少等待秒数。

单资产复查冷却继续生效:VIP+ 对同一资产为 3 小时,Pro 为 1 小时;SVIP、SVIP+ 和管理员无单资产冷却。该规则与账号级共享频率同时校验。

OSS 每日上传字节额度

身份组每日额度有效未完成会话
VIP+2 GiB同一用户最多 1 个
Pro5 GiB
SVIP10 GiB
SVIP+30 GiB
管理员不限制

网站检测在同步响应中返回 quota;APK 检测可通过 Webhook 和/或 GET /api/v1/jobs/{job_id} 轮询获取。额度或频率受限时接口返回 429。

Website Checks

网站检测

检测 HTTP/HTTPS 网站和域名风险。该端点当前同步执行,并在同一个 HTTP 响应中直接返回 JSON 结果。

POST/api/v1/url-scan同步检测网站
仅 VIP+、Pro、SVIP、SVIP+ 和管理员可调用
请求体只需 url,支持 HTTP 和 HTTPS
不使用 callback_url、Webhook、use_cache 或 Idempotency-Key
cURL · 网站检测
curl -X POST https://www.vendorguard.com.cn/api/v1/url-scan \
  -H "Authorization: Bearer fvl_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/path"}'

每次实际网站检测会开启账号共享频率窗口;超限时返回 429 和 Retry-After。

同步 JSON 响应

HTTP 200 的 data.result 包含标准化 URL、域名、风险判定和检测时间;data.quota 返回本次扣次后的剩余额度。API 不返回公开报告地址。

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"
    }
  }
}
APK Uploads

OSS 直传与审查

大文件直接写入受控的阿里云 OSS 对象(香港地域,vendorguard.oss-cn-hongkong.aliyuncs.com),APK 内容不经过网站服务器。上传后必须调用完成接口才能进入审查队列。

POST/api/v1/apk/uploads创建上传会话

设置 Idempotency-Key,并在公共根字段之外传入 .apk 文件名、最大 500 MiB 的字节数 size、32 位十六进制 file_md5 和必填的 expected_sha256。返回的 upload_url 和 upload_token 有效期为 30 分钟。

签发前预扣,签发后不返还

平台先校验会员、账号频率、复查额度、每日上传字节额度和未完成会话数,再扣一次复查次数并签发 URL。上传未完成、过期、大小或哈希不符、非法 APK、队列失败均不返还;use_cache 不影响该预扣。瞬时入队失败会保留 OSS 样本并由持久化任务自动重投。

cURL · 创建会话
curl -X POST https://www.vendorguard.com.cn/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"
  }'

按原样使用预签名请求

对 upload_url 执行一次 PUT,并原样携带响应中 required_headers 的 Content-Type、Content-Length、Content-MD5 和 x-oss-forbid-overwrite。这四个请求头都参与 OSS V4 签名:缺失或改动返回 403,文件内容与 Content-MD5 不符返回 400 InvalidDigest,同一 URL 重复写入返回 409 FileAlreadyExists。不要添加 Authorization,且不要复用 URL 写入其他对象。

cURL · 上传到 OSS
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/vnd.android.package-archive" \
  -H "Content-Length: 24117384" \
  -H "Content-MD5: ASNFZ4mrze8BI0VniavN7w==" \
  -H "x-oss-forbid-overwrite: true" \
  --upload-file ./release.apk
POST/api/v1/apk/uploads/complete完成上传并提交审查

请求体只传 upload_token,并设置 Idempotency-Key。平台通过 HEAD 校验对象,Scanner 随后重新计算哈希、解析 Manifest 与证书。

本接口只提交已预付的审查,不再次扣除复查次数或占用频率窗口。失败也不返还创建会话时已经扣除的额度;队列暂时不可用时仍返回受理结果,数据库中的待入队记录由 Scanner 每 15 分钟重投,原始样本不会因此删除。

cURL · 完成上传
curl -X POST https://www.vendorguard.com.cn/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"}'
样本保留策略

合法 APK 归档到按 SHA-256 去重的 apks/,无效或不一致样本进入 quarantine/;两者保留 90 天。未完成上传 1 天后自动清理。

APK Fingerprints

APK 特征检测

已在调用方完成解析时,可直接提交稳定特征,不必再次上传 APK。

POST/api/v1/apk/fingerprints提交 APK 特征

必填 package_name、sha256、file_md5 和 cert_md5。版本、大小、文件名和应用名用于审计与后台搜索。

实际创建新扫描任务时受账号共享频率限制;超限时返回 429 和 Retry-After。

cURL
curl -X POST https://www.vendorguard.com.cn/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"
  }'
Package Checks

包名检测

用于快速核验设备侧包名风险。包名会转换为小写并按标准格式校验。

POST/api/v1/packages/check检测包名
不是完整 APK 云扫

仅包名缺少文件哈希与证书维度。如果需要完整判断,请上传 APK 或提交完整特征。

实际创建新包名检测任务时受账号共享频率限制;超限时返回 429 和 Retry-After。

cURL
curl -X POST https://www.vendorguard.com.cn/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"
  }'

统一受理响应

三个 APK 提交接口均返回 HTTP 202。该响应仅表示已接收,最终成功或失败以 Webhook 为准。

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"
  }
}
请求控制

use_cache 语义

该布尔字段必须位于请求 JSON 根级,默认值为 false。

false强制复查

未传时也走此路径。绕过已有结果,实际发起扫描时扣一次复查次数。

Webhook: cache_status = bypass
true允许缓存

有效缓存命中时直接回调且不扣次;无缓存或已过期时自动复查并扣次。

Webhook: cache_status = hit | miss

缓存有效期沿用当前安全、风险、严重和未知状态策略,不作为固定 API 承诺。OSS 上传会话无论 use_cache 取值如何,均在签发 URL 前预扣一次审查次数;该字段只决定完成上传后是否允许复用检测结果。

结果投递

签名 Webhook

Webhook 是 APK 异步检测的唯一结果通道;网站检测不发送 Webhook。服务端发送 JSON 原始字节,并附带时间戳和 HMAC-SHA256 签名。

时间戳X-FVL-Timestamp
签名X-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 密钥计算 SHA-256,并与签名头做恒定时间比较。
  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 · 验签
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);
}

必须先读取 raw body 再解析 JSON。重新序列化对象会改变字节序列,导致验签失败。Webhook 采用至少一次投递;调用方应以 client_reference、事件类型和检测时间做幂等处理。

请求控制

幂等与后台去重

创建上传会话、完成上传、特征检测和包名检测必须提供 Idempotency-Key。

同一密钥与相同请求在 24 小时内返回同一结果,不重复扣次;上传会话重放不会延长 URL 有效期
同一密钥对应不同请求返回 409,避免误关联
相同资产并发复查合并为一次实际扫描,每个调用仍收到自己的 Webhook

管理员后台按 SHA-256 或标准化包名聚合,只累计调用次数,不为高频查询生成成千上万条记录。

参考

错误码

同步请求错误使用 HTTP 状态码和统一 JSON:{"code": status, "message": "..."}。

HTTP含义处理方式
400请求体或字段无效修正 JSON、哈希、包名或回调地址后重试。
401认证失败API 密钥缺失、无效或已吊销。
403无权调用账号身份组不支持 API,或会员已到期。
409状态冲突幂等键对应不同请求、已有有效未完成上传,或上传会话已完成/失效。
429请求受限检测额度或每日上传字节额度已用完,或触发账号级会员频率限制。
503服务暂不可用扫描或队列服务不可用;按端点规则重试,已签发的上传额度不返还。

APK 格式无效、Manifest 解析失败或声明哈希不一致属于异步审查失败,通过 scan.failed Webhook 返回。

参考

代码示例

下面示例均使用包名检测和根级 use_cache,并以业务引用作为幂等键。

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.vendorguard.com.cn/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.vendorguard.com.cn/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.1.0 · 迁移至 vendorguard.com.cn 与阿里云 OSS

API Base URL 改为 https://www.vendorguard.com.cn;旧域名 www.fuckviruslabel.com 的 GET 请求 301、其他方法 308 跳转到新域名,请尽快更新调用地址。APK 直传存储由 Cloudflare R2 改为阿里云 OSS(香港):upload_url 主机为 vendorguard.oss-cn-hongkong.aliyuncs.com,required_headers 中的 If-None-Match: * 改为 x-oss-forbid-overwrite: true,请继续原样携带全部 required_headers。网站人机验证改为阿里云 ESA AI 验证码,API Key 调用不受影响;Webhook 签名与 X-FVL-* 请求头保持不变。

v1.0.0 · 检测 API 首次发布

纳入同步网站检测,并新增受控上传、APK 特征检测、包名检测、根级缓存控制、幂等提交和强制签名 Webhook。