Get Started

API Reference

The Verifence REST API lets you embed document scanning, URL screening, email validation, and bot protection directly in your product. All endpoints return JSON.

Authentication

All requests require an API key in the API-KEY header. Keys are generated from Settings → API Keys in your dashboard.

  • Each key is shown once on creation — store it securely
  • Multiple keys can be created and revoked independently
HTTP header
API-KEY: your_api_key

Base URL

https://api.verifence.com/v1

Rate limits

Requests are limited per API key:

  • 60 requests per minute per API key
  • Batch operations (up to 500 items) count as a single request

When the limit is exceeded the API returns 429 Too Many Requests with a Retry-After header.

Credit costs

Each scan consumes credits from your monthly balance. Requests fail gracefully with a 402 error if your balance is insufficient.

Scan typeCredits consumed
Email address1 credit
URL5 credits
Document (PDF)20 credits

Shield CAPTCHA verifications are counted separately and do not consume credits.

Errors

All error responses share the same shape:

JSON
{
  "error": {
    "code": "insufficient_credits",
    "message": "Your account does not have enough credits for this request."
  }
}
HTTP statusError codeMeaning
401unauthorizedMissing or invalid API key
402insufficient_creditsNot enough credits to complete the request
422validation_errorRequest body failed validation
429rate_limitedRate limit exceeded
500internal_errorServer error — retry with backoff

POST /scan

Upload a file (PDF or Office document) for threat analysis. The scan runs synchronously and returns a score, verdict, and flagged elements. Each scan costs 20 credits.

Request

Send the file as multipart/form-data.

cURL
curl -X POST https://api.verifence.com/v1/scan \
  -H "API-KEY: your_api_key" \
  -F "file=@invoice.pdf"

Response

JSON — verdict: block
{
  "id": "doc_01j2xkr9m4fgn",
  "status": "complete",
  "verdict": "block",
  "score": 42,
  "credits_used": 20,
  "threats": [
    {
      "type": "embedded_javascript",
      "severity": "high",
      "detail": "Obfuscated JS found in /Page[0]/Annots"
    },
    {
      "type": "suspicious_url",
      "severity": "medium",
      "detail": "http://malware-payload.xyz/setup.exe"
    }
  ],
  "scanned_at": "2024-03-15T10:23:41Z"
}

Verdict values

VerdictScore rangeMeaning
ok85–100Low risk — safe to proceed
warn70–84Elevated risk — manual review recommended
block0–69High-confidence threat — do not open

POST /scan/url

Scan a single URL against Google Web Risk and the Verifence internal blocklist. Returns an immediate safe or bad verdict. Each URL costs 5 credits.

Request body

JSON
{
  "url": "https://phish-site.xyz/login"
}
cURL
curl -X POST https://api.verifence.com/v1/scan/url \
  -H "API-KEY: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://phish-site.xyz/login"}'

Response

JSON — bad URL
{
  "url": "https://phish-site.xyz/login",
  "verdict": "bad",
  "credits_used": 5,
  "scanned_at": "2024-03-15T10:23:41Z"
}
JSON — safe URL
{
  "url": "https://example.com",
  "verdict": "safe",
  "credits_used": 5,
  "scanned_at": "2024-03-15T10:23:44Z"
}

Verdict values

VerdictMeaning
safeNo threats detected
badMatched a known threat source

POST /scan/email

Validate a single email address. Returns a verdict and any detected flags. Each email costs 1 credit.

Request body

JSON
{
  "email": "test@mailinator.com"
}
cURL
curl -X POST https://api.verifence.com/v1/scan/email \
  -H "API-KEY: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"email": "test@mailinator.com"}'

Response

JSON — flagged
{
  "email": "test@mailinator.com",
  "verdict": "flag",
  "flags": ["disposable"],
  "suggestion": null,
  "credits_used": 1
}
JSON — typo detected
{
  "email": "gnail@gmial.com",
  "verdict": "flag",
  "flags": ["typo"],
  "suggestion": "gmail.com",
  "credits_used": 1
}

Flag values

FlagMeaning
disposableTemporary or throwaway address
role_accountGeneric role address (admin@, info@)
free_providerConsumer email provider (Gmail, Yahoo)
public_domainNon-business shared domain
typoLikely typo — check suggestion field
custom_blockMatched one of your block rules

POST /shield/siteverify

Verify a Shield CAPTCHA token on your server after a form submission. Call this endpoint before processing the form. Shield verifications do not consume scanning credits — they are counted separately against your monthly verification quota.

Frontend setup

Add the Shield widget to your page and include the hidden token field in your form:

HTML
<!-- In your <head> -->
<script src="https://shield.verifence.com/widget.js" async></script>

<!-- In your <form> -->
<input type="hidden" name="shield-token" id="shield-token" />
<div class="shield-widget" data-sitekey="your_site_key"></div>

Server-side verification

Required: secret and token. Optional: email (runs email rules), text (spam classifier), honeypot (value of your hidden field — must be empty).

JSON request body
{
  "secret": "your_secret_key",
  "token": "shield_token_from_form_submission",
  "email": "user@example.com",
  "text": "Hello, I'd like to get in touch...",
  "honeypot": ""
}
cURL
curl -X POST https://api.verifence.com/v1/shield/siteverify \
  -H "Content-Type: application/json" \
  -d '{
    "secret": "your_secret_key",
    "token": "shield_token_from_form_submission"
  }'

Response — human

JSON
{
  "success": true,
  "score": 12,
  "geo": { "country": "US", "asn": "AS15169" },
  "spam_score": 0.04,
  "language": "en",
  "hostname": "example.com",
  "challenge_ts": "2024-03-15T10:23:41Z"
}

Response — bot detected

JSON
{
  "success": false,
  "score": 94,
  "geo": { "country": "CN", "asn": "AS4134" },
  "hostname": "example.com",
  "challenge_ts": "2024-03-15T10:23:41Z"
}

Score interpretation

The score is an integer from 0 (very likely human) to 100 (very likely bot). When success is false, reject the submission regardless of score.

Score rangeRecommendation
0 – 30Allow — high confidence human
30 – 50Review — low-risk signals present
50 – 100Block — likely bot

Service errors & timeouts

Shield uses a fail-open policy: if the /shield/siteverify call fails due to a network error, timeout, or a 5xx response from our servers, your server should allow the form submission through rather than blocking the user. Log the error so you have visibility into any outage.

This keeps your forms available even during a Verifence service disruption. The trade-off is a brief window where bot protection is reduced — we monitor uptime closely to minimise this risk.

Node.js — fail-open with logging
async function verifyShieldToken(token) {
  try {
    const res = await fetch('https://api.verifence.com/v1/shield/siteverify', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ secret: process.env.SHIELD_SECRET, token }),
      signal: AbortSignal.timeout(3000), // 3-second timeout
    });

    if (!res.ok) {
      console.error('[shield] siteverify error', res.status);
      return { allow: true, reason: 'service_error' }; // fail-open
    }

    const data = await res.json();

    if (!data.success || data.score > 50) {
      return { allow: false, reason: 'bot_detected' };
    }

    return { allow: true, reason: 'ok' };

  } catch (err) {
    console.error('[shield] siteverify unreachable', err.message);
    return { allow: true, reason: 'service_unavailable' }; // fail-open
  }
}
Python — fail-open with logging
import httpx, logging, os

def verify_shield_token(token: str) -> dict:
    try:
        r = httpx.post(
            "https://api.verifence.com/v1/shield/siteverify",
            json={"secret": os.environ["SHIELD_SECRET"], "token": token},
            timeout=3.0,
        )
        r.raise_for_status()
        data = r.json()

        if not data.get("success") or data.get("score", 0) > 50:
            return {"allow": False, "reason": "bot_detected"}

        return {"allow": True, "reason": "ok"}

    except httpx.HTTPStatusError as e:
        logging.error(f"[shield] siteverify error {e.response.status_code}")
        return {"allow": True, "reason": "service_error"}  # fail-open

    except Exception as e:
        logging.error(f"[shield] siteverify unreachable: {e}")
        return {"allow": True, "reason": "service_unavailable"}  # fail-open
What to log — include the reason field, the originating IP, and a timestamp. If you see a spike in service_error or service_unavailable events, check our status page for active incidents.