fingerprintchecker (Python)
Install with pip install fingerprintchecker; import it as fpchecker.
Server SDK for FingerPrintChecker. Python 3.9+, standard-library HTTP, one runtime dependency (cryptography, for sealed results).
from fpchecker import FpChecker, unseal_result, verify_webhook_signature, FpCheckerApiError
fp = FpChecker(os.environ["FPCHECKER_SECRET_KEY"])
verdict = fp.check_eligibility({"visitorId": "v_..."})
if verdict.get("ok") is False:
print("refused:", verdict.get("error") or verdict.get("reason"))
Client
FpChecker(secret_key, base_url="https://api.fpchecker.com", timeout=10, *, publishable_key=None) has one method per OpenAPI operationId, in snake_case: create_intent, get_challenge, identify, bot_check, check_eligibility, release_eligibility, check_quota, bind_subject, relate_subjects, send_feedback, get_claim_flags, search_events, get_event, update_event, get_visitor, delete_visitor, get_health, get_status. Bodies and query parameters are plain dicts; responses are dicts.
- The secret key goes in the
X-Secret-Keyheader, never in a URL, and is not shown byrepr().get_challengeandidentifyuse the publishable key (publishable_key=) inX-Publishable-Keyinstead;get_healthandget_statussend no key. - 2xx responses are returned as data, including refusals such as
{"ok": False, "error": ...}. Checkok. - Non-2xx raises
FpCheckerApiErrorwithstatus,code(errororreasonfrom the body),retry_after(seconds) andbody. - Network failures and timeouts also raise
FpCheckerApiError, withstatus0,code"network_error"or"timeout", and the original exception as__cause__. - Redirects are never followed (they would forward the secret key to another origin).
base_urlmust be https, except localhost, 127.0.0.1 and [::1] (otherwiseValueError). timeout(positive finite seconds) applies per network operation (connect, each read), not to the whole call.- Only GET requests retry (429, 5xx, network failures and timeouts; at most 2 retries, honouring
Retry-After, wait capped at 10 s). POST, PATCH and DELETE are never retried.
Bot check
bot_check(*, method, url, ip, headers) classifies a request your backend received (header-only: no script ran in the sender). It is not charged and writes no event. Forward the original URL (the host and scheme the visitor used, not your origin behind a CDN or load balancer) and the client IP. bot_check_headers keeps only the headers the check needs and drops Cookie, Authorization and everything else.
import os
from fastapi import FastAPI, Request
from fastapi.concurrency import run_in_threadpool
from fastapi.responses import JSONResponse
from fpchecker import FpChecker, FpCheckerApiError, bot_check_headers
fp = FpChecker(os.environ["FPCHECKER_SECRET_KEY"])
app = FastAPI()
@app.middleware("http")
async def block_bad_bots(request: Request, call_next):
if request.client is None: # no peer address (e.g. some test clients): nothing to check
return await call_next(request)
try:
verdict = await run_in_threadpool( # bot_check blocks; keep it off the event loop
fp.bot_check,
method=request.method,
url=str(request.url), # original host and scheme; behind a proxy rebuild it from X-Forwarded-*
ip=request.client.host, # behind a proxy use the forwarded client address
headers=bot_check_headers(request.headers),
)
except FpCheckerApiError:
return await call_next(request) # fail open: an outage of the check must not take your site down
if verdict["bot"]["result"] == "bad":
return JSONResponse({"error": "forbidden"}, status_code=403)
return await call_next(request) # "good" = a verified or signed directory bot; verdict["bot"]["info"] says which
bot.result is good, bad or not_detected; bot.info (id, name, provider, category, identity) is set when the request named a directory bot. A 400 raises FpCheckerApiError with code invalid_request or invalid_ip and the offending field in body.
Rules of use:
- Call
bot_checkonce per request and reuse the answer; do not call it again for the same request. - Never retry a signed request: a retry replays the signature nonce and the request is then judged
spoofed(bad). ipmust be the trusted client IP, not an address a client can set. With FastAPI run uvicorn with--proxy-headersand--forwarded-allow-ipsset to your proxy's address (not*), then userequest.client.host.- Limits: 3,000 checks per minute per client IP and 1,200 per minute per project (429 beyond that).
bot_check_headersalso forwards any header named in the request'ssignature-input, so signed agents verify; Cookie and Authorization are never forwarded.
parse_webhook_event returns signals_version (1 before server 0.48.0, 2 since) and bot (BotSignals: result may be good, types, info as BotInfo or None). search_events also filters by bot_result, bot_category, bot_identity and bot_id.
Sealed results
result = unseal_result(identify_response["sealedResult"], [os.environ["FPC_SEALED_KEY"]])
Pass every active key (active and previous during rotation). Raises SealedResultError with .code of bad_format, unknown_key, decrypt_failed, invalid_key or unsupported_version.
Webhooks
# raw_body must be the exact bytes received, not a re-serialised object.
verify_webhook_signature(request.body, request.headers["FPC-Signature"], os.environ["FPC_WEBHOOK_SECRET"])
Raises WebhookSignatureError with .code of malformed_header, bad_signature, expired or future. tolerance_sec (default 300) must be a finite number >= 0, else TypeError / ValueError.
License
Apache-2.0
Metadata
Release files for fingerprintchecker 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| fingerprintchecker-0.4.0.tar.gz | 24.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fingerprintchecker-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 43.9 kB
Release files / fingerprintchecker-0.4.0.tar.gz
| Download URL | fingerprintchecker-0.4.0.tar.gz |
|---|---|
| Size | 24.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a6f292d24fd14c84949324940fe49e68ebe6347ceaafb18d6b942697c84b9d38
|
|
BLAKE2b-256 checksum How to use checksums |
7aedc7711700ce632a995558f76a1071b7696acb4faa709f19503a0e49785561
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.8
|
Release files / fingerprintchecker-0.4.0-py3-none-any.whl
| Download URL | fingerprintchecker-0.4.0-py3-none-any.whl |
|---|---|
| Size | 18.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
108384f1f19ae67404288fe9c9af2cf281cc33ef3fb59fe5b0f9069f2ef2d0ce
|
|
BLAKE2b-256 checksum How to use checksums |
c2a61118366b2ee7779c9a310e9acb49dcad99264350f7aeb77dd8f69b705aa2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.8
|
Security for fingerprintchecker 0.4.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.