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
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 type | Credits consumed |
|---|---|
| Email address | 1 credit |
| URL | 5 credits |
| Document (PDF) | 20 credits |
Shield CAPTCHA verifications are counted separately and do not consume credits.
Errors
All error responses share the same shape:
{
"error": {
"code": "insufficient_credits",
"message": "Your account does not have enough credits for this request."
}
} | HTTP status | Error code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key |
402 | insufficient_credits | Not enough credits to complete the request |
422 | validation_error | Request body failed validation |
429 | rate_limited | Rate limit exceeded |
500 | internal_error | Server 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 -X POST https://api.verifence.com/v1/scan \
-H "API-KEY: your_api_key" \
-F "file=@invoice.pdf" Response
{
"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
| Verdict | Score range | Meaning |
|---|---|---|
ok | 85–100 | Low risk — safe to proceed |
warn | 70–84 | Elevated risk — manual review recommended |
block | 0–69 | High-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
{
"url": "https://phish-site.xyz/login"
} 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
{
"url": "https://phish-site.xyz/login",
"verdict": "bad",
"credits_used": 5,
"scanned_at": "2024-03-15T10:23:41Z"
} {
"url": "https://example.com",
"verdict": "safe",
"credits_used": 5,
"scanned_at": "2024-03-15T10:23:44Z"
} Verdict values
| Verdict | Meaning |
|---|---|
safe | No threats detected |
bad | Matched 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
{
"email": "test@mailinator.com"
} 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
{
"email": "test@mailinator.com",
"verdict": "flag",
"flags": ["disposable"],
"suggestion": null,
"credits_used": 1
} {
"email": "gnail@gmial.com",
"verdict": "flag",
"flags": ["typo"],
"suggestion": "gmail.com",
"credits_used": 1
} Flag values
| Flag | Meaning |
|---|---|
disposable | Temporary or throwaway address |
role_account | Generic role address (admin@, info@) |
free_provider | Consumer email provider (Gmail, Yahoo) |
public_domain | Non-business shared domain |
typo | Likely typo — check suggestion field |
custom_block | Matched 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:
<!-- 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).
{
"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 -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
{
"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
{
"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 range | Recommendation |
|---|---|
0 – 30 | Allow — high confidence human |
30 – 50 | Review — low-risk signals present |
50 – 100 | Block — 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.
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
}
} 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 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.