Skip to main content

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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

layercall-1.2.1.tar.gz (7.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

layercall-1.2.1-py3-none-any.whl (7.8 kB view details)

Uploaded Python 3

File details

Details for the file layercall-1.2.1.tar.gz.

File metadata

  • Download URL: layercall-1.2.1.tar.gz
  • Upload date:
  • Size: 7.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.3

File hashes

Hashes for layercall-1.2.1.tar.gz
Algorithm Hash digest
SHA256 7d4c69ca44ef535666e6b734c85bef8d03efdfd820a2f8b3399af5ae097144c2
MD5 40039664e32940464240eaea245647c5
BLAKE2b-256 93b3bce9660f16a5dddd40d5dcc467efd68ac359e3fa385ea168a0f8096ea495

See more details on using hashes here.

File details

Details for the file layercall-1.2.1-py3-none-any.whl.

File metadata

  • Download URL: layercall-1.2.1-py3-none-any.whl
  • Upload date:
  • Size: 7.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.3

File hashes

Hashes for layercall-1.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b754aab6a55bd2e87a7f027f84852efb25033329935f85a26222922ce7b17a83
MD5 35a4877757671e2023e97495b75a67e6
BLAKE2b-256 0790fe770d221028df53c15de0e794fceecf6b247edbf03b213f40ada61056ce

See more details on using hashes here.

Release history Release notifications | RSS feed

1.2.9

2 files

1.2.8

2 files

1.2.7

2 files

1.2.6

2 files

1.2.5

2 files

1.2.4

2 files

1.2.2

2 files

This release

1.2.1 This release

2 files

1.2.0

2 files

1.1.1

2 files

1.1.0

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page