API de análisis de APK y URL

Los análisis de URL devuelven JSON de forma síncrona. Los APK se envían por archivo, huellas completas o paquete, y el resultado llega a un webhook firmado.

Primeros pasos

Descripción general

Los análisis de URL terminan en la solicitud; los de APK son asíncronos. Todos los endpoints comparten la ventana de frecuencia de la cuenta.

01Enviar

Enviar la solicitud o cargar el APK

02Revisar

Validar, deduplicar y analizar

03Notificar

Entregar el resultado por webhook firmado

La API de APK admite sondeo

La aceptación incluye job_id. Consulta GET /api/v1/jobs/{job_id} durante unas 24 horas. callback_url es opcional; si se indica, también se envía un webhook firmado.

JSON · common APK fields
{
  "client_reference": "order-20260711-001",
  "callback_url": "https://example.com/webhooks/fvl",
  "use_cache": false
}
Control de acceso

Autenticación

Usa una de las cabeceras en cada solicitud. La clave solo se muestra completa al crearla y debe permanecer en el servidor.

RecomendadoAuthorization: Bearer fvl_xxx
CompatibleX-API-Key: fvl_xxx

No incluyas claves API en navegadores, apps móviles, repositorios públicos ni registros.

Control de acceso

Cuotas y frecuencia

La API está disponible para VIP+, SVIP, SVIP+ y administradores. Todas las claves de una cuenta comparten la frecuencia.

PlanIntervalo mínimoCarga R2 diaria
VIP+10 minutos2 GiB
Pro3 minutos5 GiB
SVIP5 minutos10 GiB
SVIP+1 minuto30 GiB
AdministradorSin límiteSin límite
URL, creación de sesiones, huellas APK y paquetes comparten el límite de la cuenta. Los aciertos de caché, repeticiones idempotentes y trabajos combinados no abren otra ventana.
Cada usuario puede tener una sesión de carga incompleta activa. Crear una sesión R2 descuenta un reanálisis y no se reembolsa tras emitirla.
API

Endpoints

Solicitudes y respuestas usan JSON salvo el PUT prefirmado de R2.

POST/api/v1/url-scanAnalizar una URL de forma síncrona

Envía url y recibe el riesgo y la quota posterior al cargo en la misma respuesta. No usa 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/uploadsCrear una sesión de carga APK

Requiere Idempotency-Key, nombre .apk, size, file_md5, expected_sha256 y campos de callback. La URL dura 30 minutos.

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/completeCompletar la carga y enviar a revisión

Envía upload_token e Idempotency-Key. Se valida el objeto y se envía la revisión prepagada sin otro cargo.

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/fingerprintsEnviar huellas APK

Envía package_name, sha256, file_md5, cert_md5 y los campos de callback sin volver a cargar el 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/checkComprobar un paquete

Comprueba rápidamente el riesgo por paquete. No es un análisis APK completo con hashes y certificado.

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}Consultar un trabajo asíncrono

Usa el job_id de la aceptación para leer pending / processing / completed / failed. Los resultados se conservan unas 24 horas.

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

Cargo antes de emitir; sin reembolso después

Cargo antes de emitir; sin reembolso después

Se validan membresía, frecuencia, cuota, bytes diarios y sesiones antes de descontar un reanálisis y emitir la URL. No se reembolsa por cargas incompletas, caducadas, hashes incorrectos, APK no válidos ni fallos de cola. Los fallos transitorios conservan la muestra para reintento automático.

Retención de muestras

Los APK válidos se deduplican por SHA-256 y archivan; las muestras no válidas o incoherentes van a quarantine. Se conservan 90 días y las cargas incompletas se eliminan al día.

Control de solicitud

Semántica de use_cache

use_cache es un campo raíz y vale false por defecto. R2 se prepaga antes de emitir la URL sin importar el valor; solo controla la reutilización posterior.

falseForzar reanálisis

false omite resultados existentes y consume un reanálisis cuando empieza el análisis real.

cache_status = bypass
truePermitir caché

true devuelve una caché válida sin cargo; si falta o caducó, reanaliza.

cache_status = hit | miss
Entrega de resultados

Webhook firmado

El webhook es el único canal para APK asíncronos. El servidor envía JSON original, marca de tiempo y firma HMAC-SHA256.

X-FVL-TimestampX-FVL-Timestamp: 1720000000
X-FVL-SignatureX-FVL-Signature: sha256=<hex>
  1. Calcula webhook_secret = lowercase_hex(SHA-256(raw_api_key)).
  2. Firma X-FVL-Timestamp + "." + raw_body.
  3. Usa los bytes UTF-8 de webhook_secret para HMAC-SHA256 y compara en tiempo constante.
  4. Valida la hora y devuelve cualquier 2xx; otros estados se reintentan.
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);
}

Lee raw body antes de interpretar JSON. Volver a serializar cambia los bytes y rompe la firma.

Control de solicitud

Idempotencia y deduplicación

Crear y completar cargas, huellas y paquetes requieren Idempotency-Key.

La misma clave y solicitud devuelven el mismo resultado durante 24 horas sin otro cargo.
La misma clave con otra solicitud devuelve HTTP 409.
Reanálisis concurrentes del mismo activo se combinan, pero cada llamada recibe su webhook.
Referencia

Errores

Los errores síncronos usan estado HTTP y JSON uniforme. Los fallos APK asíncronos llegan por scan.failed.

HTTPSignificadoAcción
400Solicitud o campo no válidoCorrige JSON, hashes, paquete o callback.
401Autenticación fallidaComprueba la clave API.
403Acceso denegadoComprueba plan y vencimiento.
409Conflicto de estadoComprueba idempotencia o estado de carga.
429Cuota o frecuencia excedidaRespeta Retry-After.
503Servicio no disponibleReintenta según el endpoint con la misma clave.
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 legible por máquinasGenera clientes, valida solicitudes o importa la API en herramientas.
Referencia

Registro de cambios

v1.0.0

Versión inicial: URL, R2, huellas APK, paquetes, caché, idempotencia y webhooks firmados.