Quickstart

Step 1 — get your key. Create a free API key (1,000 lookups/month, no card). Then pass it in the X-Api-Key header on every request below.

Every endpoint is a single request authenticated with your API key in the X-Api-Key header, and every endpoint returns the same shape: a 0–100 risk_score, an allow | review | block verdict, the signals behind it, and processing_time_sec. Free tier: 1,000 lookups/month.

Strictness. Add &strictness= (0–3) to any scoring endpoint to tune how aggressively verdicts flip — 0 lenient (fewest false positives), 1 balanced (default), 2 strict, 3 paranoid. The risk_score stays the same; only the verdict thresholds move.

curl "https://api.layercall.com/v1/score/ip?ip=185.220.101.1" \
  -H "X-Api-Key: tl_live_your_key"
{
  "ip": "185.220.101.1",
  "risk_score": 80,
  "verdict": "block",
  "signals": {
    "is_vpn": false,
    "is_proxy": true,
    "is_datacenter": true,
    "is_tor": true,
    "recent_abuse": false
  },
  "geo": { "country": "DE", "city": "Frankfurt", "asn": "AS24940", "isp": "Hetzner Online GmbH" },
  "cached": true,
  "request_id": "req_1b8e64d3-…",
  "processing_time_sec": 0.003
}

Unified Score — score a whole signup

GET/POST/v1/score/user

The endpoint the others feed into: pass any combination of ip, email, phone (+ optional phone_country), and domain. Components run in parallel and are combined into one weighted score — a hard block on any single component is never averaged away. Billed as one credit per component checked.

curl -X POST "https://api.layercall.com/v1/score/user" \
  -H "X-Api-Key: tl_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "ip": "146.70.202.5",
    "email": "ceo@mailinator.com",
    "phone": "+14155552671",
    "domain": "just-registered-shop.com"
  }'
{
  "risk_score": 71,
  "verdict": "block",
  "components_checked": ["ip", "email", "phone", "domain"],
  "top_signals": [
    "ip: commercial vpn",
    "email: disposable domain",
    "domain: registered <30 days ago"
  ],
  "components": {
    "ip":     { "risk_score": 55, "verdict": "review", "...": "full detail" },
    "email":  { "risk_score": 70, "verdict": "block",  "...": "full detail" },
    "phone":  { "risk_score": 0,  "verdict": "allow",  "...": "full detail" },
    "domain": { "risk_score": 60, "verdict": "review", "...": "full detail" }
  },
  "request_id": "req_9f2c…",
  "processing_time_sec": 0.052
}

IP Intelligence

GET/v1/score/ip

VPN, proxy, datacenter, Tor, and abuse signals with geo/ASN context. VPN/datacenter detection runs per-IP across 50k+ ranges (not just ASNs), so it catches a VPN running on a cloud provider. Egress severity: Tor > VPN > datacenter (strongest tier counts, not summed), with abuse and our own network reports stacking on top. Cached lookups return in ~0.003 sec.

curl "https://api.layercall.com/v1/score/ip?ip=185.220.101.1" \
  -H "X-Api-Key: tl_live_your_key"

Email Validation

GET/v1/verify/email

Industry-standard status / sub_status vocabulary (valid, invalid, risky, do_not_mail — drop-in compatible with ZeroBounce-style branching), plus: live SMTP mailbox verification (mailbox_exists) and catch-all detection, MX + provider identification, 4,000+ disposable domains refreshed daily, did_you_mean typo correction that also catches typo-squat domains like gamil.com, normalized_email for multi-account dedupe (strips +tags and Gmail dots), machine-generated handle detection, domain age via RDAP, high-abuse TLD flags, first_seen / times_seen velocity from our own network, and a digital_footprintobject (public Gravatar profile + optional breach history — an aged real identity lowers risk). When the mailbox can't be verified we say so (mailbox_not_verified) rather than guessing.

curl "https://api.layercall.com/v1/verify/email?email=ceo@mailinator.com" \
  -H "X-Api-Key: tl_live_your_key"
{
  "email": "ceo@mailinator.com",
  "normalized_email": "ceo@mailinator.com",
  "risk_score": 70,
  "verdict": "block",
  "status": "do_not_mail",
  "sub_status": "disposable",
  "deliverability_score": 30,
  "did_you_mean": null,
  "signals": {
    "syntax_valid": true,
    "mx_found": true,
    "is_disposable": true,
    "is_role_account": false,
    "is_free_provider": false,
    "is_suspicious_handle": false,
    "is_tagged": false,
    "is_risky_tld": false,
    "is_new_domain": false
  },
  "domain": "mailinator.com",
  "domain_age_days": null,
  "mx_provider": null,
  "mx_records": ["mail.mailinator.com"],
  "processing_time_sec": 0.021
}

Phone Validation

GET/v1/lookup/phone

Validity, country, E.164/national/international formats, and line type for 240+ countries — with VoIP and premium-rate risk flags. Accepts E.164 directly or national format with &country=.

curl "https://api.layercall.com/v1/lookup/phone?phone=%2B14155552671" \
  -H "X-Api-Key: tl_live_your_key"
# national format also works: ?phone=4155552671&country=US
{
  "phone": "+14155552671",
  "risk_score": 0,
  "verdict": "allow",
  "signals": {
    "syntax_valid": true,
    "is_possible": true,
    "is_voip": false,
    "is_premium_rate": false,
    "is_toll_free": false
  },
  "number": {
    "e164": "+14155552671",
    "country": "US",
    "national": "(415) 555-2671",
    "international": "+1 415 555 2671",
    "line_type": "fixed_line_or_mobile"
  },
  "processing_time_sec": 0.002
}

Domain Intelligence

GET/v1/score/domain

Registration age via RDAP (newly-registered domains are a strong fraud signal), DNS resolution, MX/SPF/DMARC email posture, and disposable-domain detection.

curl "https://api.layercall.com/v1/score/domain?domain=stripe.com" \
  -H "X-Api-Key: tl_live_your_key"
{
  "domain": "stripe.com",
  "risk_score": 0,
  "verdict": "allow",
  "signals": {
    "resolves": true,
    "mx_found": true,
    "has_spf": true,
    "has_dmarc": true,
    "is_disposable": false,
    "is_free_provider": false,
    "newly_registered": false
  },
  "registration": {
    "created_at": "2005-08-18T00:00:00Z",
    "age_days": 7642,
    "registrar": "MarkMonitor Inc.",
  },
  "processing_time_sec": 0.048
}

Custom Rules — your lists always win

GET/POST/DELETE/v1/rules

Exclude your own infrastructure or block known bad actors: add allow or block rules for any ip, cidr range, email, domain (subdomains included), phone, or asn. Rules override the computed score on every endpoint — an allow rule forces 0/allow, a block rule forces 100/block, and the response shows rule_applied so you always know why. Block rules take precedence over allow rules. Kind is auto-detected per value. Rule management calls are free.

# Never flag our office network or corporate domain
curl -X POST "https://api.layercall.com/v1/rules" \
  -H "X-Api-Key: tl_live_your_key" -H "Content-Type: application/json" \
  -d '{ "action": "allow", "values": ["203.0.113.0/24", "ourcompany.com"] }'

# List, delete one, or clear all
curl "https://api.layercall.com/v1/rules" -H "X-Api-Key: …"
curl -X DELETE "https://api.layercall.com/v1/rules/<rule-id>" -H "X-Api-Key: …"
curl -X DELETE "https://api.layercall.com/v1/rules?confirm=true" -H "X-Api-Key: …"

Report Fraud — grow the shared network

POST/v1/report

Caught a fraudster? Report the IP, email, domain, or phone and it feeds our reputation network — so the next time anyone (including you) scores that value, it carries the abuse history. This is our own data, built from reports like yours: no third-party licenses, no stale lists. Reported values raise abuse_reports and recent_abuse on every future score. Free — reporting makes the product better for everyone.

curl -X POST "https://api.layercall.com/v1/report" \
  -H "X-Api-Key: tl_live_your_key" -H "Content-Type: application/json" \
  -d '{ "values": ["45.9.20.14", "scammer@example.com"] }'

# → { "reported": 2, "results": [ { "kind": "ip", "abuse_reports": 7 }, … ] }

Migration Import — switch providers in one call

POST/v1/rules/import

Moving from IPQualityScore, MaxMind, AbuseIPDB, or anywhere else? Export your allowlist/blocklist there and POST the file here as-is — plain text or CSV, one entry per line. We auto-detect IPs, CIDR ranges, emails, domains, phones, and ASNs, skip headers and junk columns, dedupe, and report exactly what was imported and what was skipped. Up to 10,000 entries per call, free.

curl -X POST "https://api.layercall.com/v1/rules/import?action=block" \
  -H "X-Api-Key: tl_live_your_key" \
  -H "Content-Type: text/csv" \
  --data-binary @old-provider-blocklist.csv
{
  "action": "block",
  "imported": 4210,
  "duplicates": 12,
  "by_kind": { "ip": 3900, "cidr": 180, "domain": 130 },
  "skipped": [ { "line": 1, "value": "ip_address,date_added", "reason": "no ip/cidr/email/domain/phone/asn recognized" } ],
  "processing_time_sec": 0.19
}

Node.js

const res = await fetch("https://api.layercall.com/v1/score/user", {
  method: "POST",
  headers: {
    "X-Api-Key": process.env.LAYERCALL_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ ip, email, phone }),
});
const { verdict, risk_score, top_signals } = await res.json();

if (verdict === "block") {
  // reject the signup or require step-up verification
}

Python

import os, requests

res = requests.post(
    "https://api.layercall.com/v1/score/user",
    json={"ip": ip, "email": email, "phone": phone},
    headers={"X-Api-Key": os.environ["LAYERCALL_API_KEY"]},
    timeout=5,
)
data = res.json()
if data["verdict"] == "block":
    ...  # reject or challenge

Errors

  • 400 — missing or invalid input parameter
  • 401 — no API key provided
  • 403 — invalid or revoked API key
  • 429 — plan quota exceeded
  • 500 — internal error (include request_id when reporting)

Recommended integration pattern

Call /v1/score/user at signup or checkout with everything you have, before creating the account or charging the card. Treat block as a hard rejection or step-up challenge (email verification, captcha). Treat review as a soft flag — let the user through but mark the account for monitoring. Fail open: if the API is unreachable, let the signup proceed rather than losing a legitimate user.