Skip to main content

sentinelsup — Maskbreak Python SDK

Official Python SDK for Maskbreak. Evaluate SDK-backed visits for network and browser risk signals, or look up a public IP for cloud range and Tor signals. These are different evidence sources, not interchangeable checks. VPN/proxy service names are returned when known; a VPN alone routes to review under the default policy, not automatic blocking.

PyPI Python versions license

Zero dependencies — just the standard library. Works with Flask, Django, FastAPI, or bare urllib.

Set up with an AI assistant

Using Claude Code, Cursor, Copilot, or any AI coding assistant? Paste this one prompt and it wires the whole integration — frontend script, backend check, env var, and a test:

Fetch https://maskbreak.com/integrate.md and follow it to add Maskbreak fraud protection to this app — protect signup, login, and checkout. Read the API key from the server-only MASKBREAK_API_KEY environment variable; I will configure the secret separately. Never put it in client-side code. Show me how to test it.

integrate.md is the canonical machine-readable integration guide, kept in sync with the live API.

Install

pip install sentinelsup

Python 3.8+. Get a free API key (no credit card) at maskbreak.com/signup.

Quick start

import os
from sentinel import Sentinel

s = Sentinel(api_key=os.environ["MASKBREAK_API_KEY"])  # or omit — reads the env var itself

result = s.evaluate(
    token=request.json["sentinelToken"],
    fingerprint_event_id=request.json.get("fingerprintEventId"),
)

if result.is_blocked:            # decision == 'block'
    abort(403)

print(result.decision)        # 'allow' | 'review' | 'block' — route on this
print(result.risk_score)      # 0..100
print(result.network)         # {'vpn': True, 'proxy': False, 'datacenter': True, ...}
print(result.reasons)         # ['vpn_detected', 'datacenter_asn', ...]

This is a handler fragment, not a complete signup implementation. Route review to your verification/review flow; only allow is an approval. Keep API keys on the server. An unavailable device layer or raw["degraded"] is not proof of a clean visit; degraded describes the network layer only.

Since v0.2.5, Sentinel() without a key reads MASKBREAK_API_KEY; the older SENTINEL_KEY and SENTINEL_API_KEY names are still read as fallbacks.

Check the signup email against the disposable-domain feed (checked transiently, never stored), or look up an arbitrary IP with no browser token at all:

result = s.evaluate(token=tok, email=data["email"])
if result.raw.get("email", {}).get("disposable"):
    ...  # burner domain — decision is escalated allow → review

info = s.lookup("185.220.101.34")   # GET /v1/lookup/{ip} — same key & quota
print(info["verdict"])              # 'allow' | 'review' | 'block'
print(info["signals"])              # {'vpn': ..., 'proxied': ..., 'tor': ..., 'dch': ..., 'anon': ...}

What you get back

evaluate() returns an EvaluateResult dataclass. The type sketch below uses Python 3.10+ annotation syntax for readability; the package minimum stays 3.8:

@dataclass
class EvaluateResult:
    decision: str | None        # 'allow' | 'review' | 'block'
    risk_score: int | None      # 0..100
    ip: str | None
    country: str | None         # ISO-2
    network: dict               # {vpn, proxy, datacenter, anonymous, tor, residential, service}
    device: dict                # antidetect / automation / emulator signals
    reasons: list[str]          # machine-readable codes
    email: dict | None          # {disposable: bool} — present when you passed email=
    decision_source: str | None # 'rules' | 'exception' when your policy matched
    engine_decision: str | None # engine's own verdict when policy changed the decision
    test: bool                  # True for test-token / test-key calls
    raw: dict                   # full upstream response

    is_suspicious: bool         # True for a non-null decision other than 'allow'
    is_blocked: bool            # True if decision == 'block'

Try the live sample (same shape, no key needed):

curl "https://maskbreak.com/v1/evaluate/sample?scenario=vpn"

Or use the interactive playground.

Frontend setup

Add the Maskbreak SDK to your frontend. One script loads both layers — network (VPN/proxy/datacenter) and device (antidetect/bot/tampering):

<script async src="https://maskbreak.com/assets/sentinel.js"></script>

<!-- Add class="monocle-enriched" to any form you want evaluated -->
<form class="monocle-enriched" id="signup-form">
  <!-- The SDK injects both:
       <input type="hidden" name="monocle"     value="eyJ...">  (network)
       <input type="hidden" name="sentinel_fp" value="a1b2..."> (device) -->
</form>

Forward both fields to your backend with the form submission and pass them to evaluate() as token and fingerprint_event_id — without the second one, the device-layer signals (antidetect, automation, emulator) are unavailable. For fetch/XHR submissions, collect them explicitly:

const { token, fingerprintEventId } = await window.Sentinel.collect();

Examples

Flask — route signup decisions

from flask import Flask, request, abort, jsonify
from sentinel import Sentinel, SentinelError

app = Flask(__name__)
sentinel = Sentinel()  # reads MASKBREAK_API_KEY from env

@app.route("/signup", methods=["POST"])
def signup():
    data = request.get_json()
    try:
        result = sentinel.evaluate(token=data["sentinelToken"],
                                   fingerprint_event_id=data.get("fingerprintEventId"))
    except SentinelError as e:
        # Fail open OR fail closed — your call. Logged either way.
        app.logger.warning("Sentinel error: %s", e)
        result = None

    if result and result.is_blocked:
        abort(403, "Signup blocked")

    if result and result.decision == "review":
        return jsonify({"needs_verification": True}), 202

    # This example explicitly fails open on SDK errors. Choose an endpoint-
    # specific fallback; do not reuse this policy for transfers or withdrawals.
    # ... your normal signup flow
    return jsonify({"ok": True})

Django — middleware for high-value endpoints

from django.http import JsonResponse
from sentinel import Sentinel, SentinelError

sentinel = Sentinel()  # reads MASKBREAK_API_KEY from env

class FraudCheckMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if request.path.startswith("/api/checkout"):
            token = request.META.get("HTTP_X_SENTINEL_TOKEN")
            if not token:
                return JsonResponse({"error": "verification required"}, status=400)
            try:
                result = sentinel.evaluate(token=token,
                    fingerprint_event_id=request.META.get("HTTP_X_SENTINEL_FINGERPRINT_EVENT_ID"))
            except SentinelError:
                return JsonResponse({"error": "verification unavailable"}, status=503)
            if result.is_blocked:
                return JsonResponse({"error": "blocked"}, status=403)
            if result.decision == "review":
                return JsonResponse({"error": "additional verification required"}, status=409)
        return self.get_response(request)

Runnable versions live in examples/.

API

Sentinel(api_key=None, endpoint="https://maskbreak.com", timeout=5.0)

Option Default Description
api_key $MASKBREAK_API_KEY (falls back to $SENTINEL_KEY, then $SENTINEL_API_KEY) Your key starting with sk_live_
endpoint https://maskbreak.com Override base URL (for testing)
timeout 5.0 Per-request timeout in seconds

sentinel.evaluate(token, fingerprint_event_id=None, account_id=None, email=None)

Returns EvaluateResult. Raises SentinelError on network/API failure.

  • fingerprint_event_id — adds the device signal block (antidetect, automation, emulator, …).
  • account_id — your own user id for this session; enables multi-accounting detection (device.linked_accounts / device.multi_account).
  • email — adds email.disposable to the raw response; burner domains escalate allow to review.

This synchronous SDK forwards only these named inputs. It does not expose a timezone input, automatic retries, a circuit breaker, or every REST endpoint. Device availability and degraded remain accessible through raw; missing device evidence must not be treated as a clean device result. Account linking (linked_accounts) is customer-scoped; device first_seen/times_seen history is not customer-scoped.

sentinel.lookup(ip)

Returns the raw response dict for any public IPv4/IPv6 address (wraps GET /v1/lookup/{ip}): verdict (allow/review/block), risk_score (0–100), known, signals ({vpn, proxied, tor, dch, anon} or None), network ({asn, org, country, city}), latency_ms. Shares the per-key hourly quota with evaluate(). known: False means our feeds hold no data — it is not a clean guarantee.

Production bare-IP lookup checks cloud ranges and Tor exits, not live-visit VPN/proxy evidence. Legacy vpn/proxied keys in the shape do not imply those checks ran. Use evaluate() with browser evidence for VPN/proxy checks. When obtaining an IP behind a proxy, trust forwarded headers only from configured trusted proxies; never blindly take the first client-supplied value.

Testing

Deterministic test_* tokens exercise response handling, not detection quality. SDK fixture calls use authentication and quota but do not increment billable usage or trigger webhooks; console-originated live-key fixtures can be stored as test events. Personal rules and exception pins can change fixture decisions.

result = s.evaluate(token="test_vpn")       # also: test_clean, test_proxy, test_datacenter, test_tor
assert result.decision == "review"          # default policy, no overriding rules/pins
assert result.test

No account yet? The public sandbox key accepts the same test tokens:

s = Sentinel(api_key="sk_test_sandbox")     # deterministic fixtures only, no live detection

The public sandbox is separately rate-limited, accepts only supported fixture tokens, and does not store events or run live detection. It is not a production allowance. A personal sk_test_... key runs the live pipeline with real browser evidence and your policy; resulting events can be stored with is_test set, without incrementing usage or firing webhooks. Test keys still have rate limits.

Local checks require no API credentials:

python -m unittest discover -s tests -v
python -m pip install build twine
python -m build
python -m twine check dist/*

The CI matrix targets Python 3.8–3.14 without raising the 3.8 minimum. A configured matrix is not a claim that every interpreter was tested locally; inspect its run.

Errors

Transport/API failures and unusable success responses raise SentinelError. The exception carries .status (HTTP code) and .body (parsed error body) when available. Redirects are rejected to avoid forwarding credentials. Configure the final API base URL; the client does not retry automatically.

from sentinel import Sentinel, SentinelError

try:
    result = sentinel.evaluate(token=tok)
except SentinelError as e:
    if e.status == 429:
        pass    # back off
    elif e.status and 400 <= e.status < 500:
        pass    # bad input, won't recover by retrying
    else:
        pass    # unknown outcome — use the endpoint's explicit fallback policy

Rate limits

Free tier: 1,000 requests/hour per API key. No monthly cap, no credit card.

What Maskbreak detects

VPNs (commercial + self-hosted) · residential proxies (Bright Data, IPRoyal, and similar networks) · datacenter IPs · Tor exit nodes · antidetect browsers (Kameleo, GoLogin, Multilogin, Dolphin{anty}, AdsPower) · headless browsers and automation (Puppeteer, Playwright, Selenium) · AI agents · emulators and virtual machines · browser tampering.

License

MIT © Sentinel Edge Networks LTD. See LICENSE.

Metadata

Release files for sentinelsup 0.2.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sentinelsup 0.2.5
File Size Uploaded
sentinelsup-0.2.5.tar.gz 13.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sentinelsup 0.2.5
File Interpreter ABI Platform
sentinelsup-0.2.5-py3-none-any.whl Python 3 none any Details

Total release size: 23.8 kB

Release files / sentinelsup-0.2.5.tar.gz

Download URL sentinelsup-0.2.5.tar.gz
Size 13.2 kB
Tags Source
SHA-256 checksum
How to use checksums
3a0b8d7af2415bbe4fbd8dc586b2663091a4c209d1fd2b1b7c89bd6ecdd44dc4
BLAKE2b-256 checksum
How to use checksums
90dfd4bdf03ce01aefc84fd4319fb402b4e95b36850dec17dc16978836301abc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / sentinelsup-0.2.5-py3-none-any.whl

Download URL sentinelsup-0.2.5-py3-none-any.whl
Size 10.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8651298303bea5264fd7872ea373d0e05d07f0c516319dc7786bac3f3189f429
BLAKE2b-256 checksum
How to use checksums
ac82307d4219b67dde9e5cf369a47ec9fb67c855dc6c86ad635108a0b51e852b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release history Release notifications | RSS feed

0.2.10

2 release files

0.2.9

2 release files

0.2.8

2 release files

0.2.7

2 release files

0.2.6

2 release files

This release

0.2.5 This release

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.1.0

2 release 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