layercall
Official Python client for LayerCall — score an IP, email, phone number, domain, or a whole signup for fraud in a single call.
Zero dependencies. Standard library only. A trust check sits on your signup path, which is the worst place to add a dependency tree that has to resolve against whatever your app already pins.
pip install layercall
Quick start
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"]) # a real user clears it themselves
elif result["verdict"] == "block":
queue_for_review(result)
Get a free API key — 1,000 lookups a month, no card.
Acting on a verdict
| Verdict | Do this | Not this |
|---|---|---|
allow |
Let them through | — |
review |
Step up — OTP, SMS, 3-D Secure | Don't reject. This band includes ordinary VPN users. |
block |
Reject, or send to a human queue | Don't reject silently |
Prefer a challenge over a rejection. A real user clears it in seconds; an attacker cannot. A false positive then costs friction instead of a customer.
Already send an OTP to everyone? Passwordless products can't use an email code as a step-up — it's already mandatory. Score after sign-in and use a different lever: a limited account state, a delayed payout, or a review queue.
Methods
lc.score_ip("185.220.101.1")
lc.verify_email("someone@mailinator.com")
lc.lookup_phone("+14155552671")
lc.lookup_phone("4155552671", country="US")
lc.score_domain("example.com")
lc.score_user(ip=ip, email=email, phone=phone, device_id=device_id)
lc.batch("email", ["a@x.com", "b@y.com"]) # up to 500
Every method takes strictness (0 lenient → 3 paranoid). It moves the
verdict thresholds only; the risk score never changes, so you can re-tune
without re-scoring anything.
None means unknown, never "no"
signals = lc.score_domain("example.de")["signals"]
signals["newly_registered"] # None — .de publishes no RDAP, so age is unknown
None means we could not determine it. It is not a negative finding.
Treating it as "established" is exactly the mistake the field exists to
prevent.
Same for mailbox_exists: Gmail and Yahoo accept mail for addresses that do
not exist, so we return None rather than a guess.
Errors
from layercall import LayerCallError
try:
lc.score_ip(ip)
except LayerCallError as err:
if err.is_quota: # 402 — out of quota or spend cap reached
...
if err.is_auth: # 401/403 — bad or revoked key
...
print(err.request_id) # quote this in a support ticket
except Exception:
pass # fail open: let the signup through
429s and 5xx retry automatically (2 attempts, short backoff). Other 4xx fail fast — they will not succeed on a retry.
Server-side only
An API key in anything a user can read is public and bills to your account.
License
MIT
Release files for layercall 1.2.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| layercall-1.2.5.tar.gz | 7.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| layercall-1.2.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:16.0 kB
Release files / layercall-1.2.5.tar.gz
| Download URL | layercall-1.2.5.tar.gz |
|---|---|
| Size | 7.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ae3a94f170188654e9d71b47e320f2595c4bbfbe919c4433ebc94661fbb066d7
|
|
BLAKE2b-256 checksum How to use checksums |
1768e676fc73d77119179451a4b58379986d0183169f28c5cb05fd69045b132b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 7, 2026.
Transparency logRelease files / layercall-1.2.5-py3-none-any.whl
| Download URL | layercall-1.2.5-py3-none-any.whl |
|---|---|
| Size | 8.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c725051097b631479fd49daf56d53604d88818946b8914ce0aade1bffb251b12
|
|
BLAKE2b-256 checksum How to use checksums |
585df03957a471d0e19a527636d7c3e7a5735229e15ab711410a2befcd3153c7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 7, 2026.
Transparency log