Live: Tor + abuse feeds refreshed every 6 hours

LayerCall

SDKs

Official Node and Python clients, both with zero dependencies.

Try it with no install

export LAYERCALL_API_KEY=tl_live_...
npx layercall ip 8.8.8.8
npx layercall email someone@mailinator.com
npx layercall user --ip 1.2.3.4 --email a@b.com

Node / TypeScript

npm i layercall
import { LayerCall } from "layercall";

const lc = new LayerCall(process.env.LAYERCALL_API_KEY!);

const result = await lc.scoreUser({
  ip: req.ip,
  email: body.email,
  phone: body.phone,
});

if (result.verdict === "review") {
  await sendOtp(body.email);   // a real user clears it themselves
}

Python

pip install layercall
import os
from layercall import LayerCall

lc = LayerCall(os.environ["LAYERCALL_API_KEY"])

result = lc.score_user(ip=request.remote_addr, email=form["email"])

if result["verdict"] == "review":
    send_otp(form["email"])

Two things the clients enforce

Server-side only. The Node client throws if it detects a browser. An API key shipped to a browser is public the moment someone opens devtools, and it bills to your account.

Retries only what can succeed. 429 and 5xx retry with short backoff; other 4xx fail fast, because they will not succeed on a retry and this code sits in front of a user.

null means unknown, never “no”

newly_registered is null when a TLD publishes no RDAP — .de, .ru and .ac.uk among them — and mailbox_exists is null when the provider does not answer honestly. Gmail and Yahoo accept mail for addresses that do not exist, so any vendor claiming certainty there is inventing it. Treating either as a negative finding is the exact mistake those fields exist to prevent.

Prefer a challenge to a rejection on review — see acting on a verdict.

Drop-in middleware

The Node package ships Express and Next.js middleware as separate entry points. Neither needs any code beyond the import: the visitor’s IP is scored on the way in and the result is attached to the request.

import layercall from "layercall/express";

app.use(layercall({ apiKey: process.env.LAYERCALL_API_KEY }));

app.post("/signup", (req, res) => {
  const t = req.trust;            // { scored, verdict, risk_score, result }
  if (t.scored && t.verdict === "block") return res.status(403).end();
  // ...
});
import { withTrust } from "layercall/next";

export const POST = withTrust(async (req, trust) => {
  if (trust.scored && trust.verdict === "block") {
    return Response.json({ error: "blocked" }, { status: 403 });
  }
  return Response.json({ ok: true });
});

Always check scored before acting on verdict. When we are unreachable the middleware fails open — it lets the request through with scored: false rather than taking your site down with ours. That is almost always what you want, but it means an allow you did not check scored against might be an outage rather than a judgement. For a payment or a password reset, treat scored: false as a reason to challenge.