VeriSec Python SDK
The official Python library for the VeriSec API: identity (KYC) and Financial checks, with typed answers, images straight from files, safe retries and webhook verification.
pip install verisec
Python 3.9 or later. No runtime dependencies: only the standard library.
Configure
import os
from verisec import VeriSec
client = VeriSec(api_key=os.environ["VERISEC_API_KEY"]) # sk_test_… or ak_live_…
Create keys in the console under API Keys. A test key (sk_test_…) is used as it is. A live key (ak_live_…)
also needs the client id and secret issued with it; the library exchanges them at POST /v2/oauth/token for a
short-lived token, caches it, and renews it for you:
client = VeriSec(
api_key=os.environ["VERISEC_API_KEY"], # ak_live_…
client_id=os.environ["VERISEC_CLIENT_ID"],
client_secret=os.environ["VERISEC_CLIENT_SECRET"],
)
With no arguments, VeriSec() reads VERISEC_API_KEY, VERISEC_CLIENT_ID, VERISEC_CLIENT_SECRET and
VERISEC_BASE_URL.
| Option | Default | |
|---|---|---|
base_url |
https://verisec.mmose.me |
|
timeout |
300 |
Seconds to wait for an answer, per attempt. A deep cheque read can take about 250 s. |
max_retries |
2 |
Retries where nothing was charged (see Retries). |
default_headers |
none | Headers sent with every request. |
A client is safe to share between threads; they share one token exchange.
KYC: images from files
Every API is a method, grouped as the API groups them: client.face, client.documents, client.mrz,
client.phone.otp, client.screening, … (all 69, with prices, are in ../spec/README.md). Methods take the API's
fields as keyword arguments, spelled exactly as the API spells them (image_front, detectAll, minAge).
An image can be a file path (str or pathlib.Path), the file's bytes, an open binary file, a data: URI, or
base64 you already have. The library sends it the way the API expects.
result = client.face.verify(image1="selfie.jpg", image2=id_photo_bytes, threshold=0.8)
print(result.match, result.similarity)
document = client.documents.read(image_front="id-front.jpg", image_back=Path("id-back.jpg"))
# lists take the images themselves; the index and ids are filled in for you
match = client.face.match(images=["selfie.jpg", "passport.jpg"])
found = client.face.search(probe="selfie.jpg", gallery=["alice.jpg", {"image": "bob.jpg", "id": "bob"}])
screening = client.screening.sanctions(name="Jane Example", countries=["RW"])
A KYC call returns the answer's data. The rest of the response is on last_response:
print(result.last_response.request_id) # quote it to support
print(result.last_response.warnings, result.last_response.latency_ms, result.last_response.status)
Answers are dicts whose keys also read as attributes: result.similarity and result["similarity"] are the same.
A key that is a Python keyword or a dict method (pass, items) is read with brackets. Each answer has a type in
verisec.types (FaceVerify, DocumentMrz, …) for your editor and type checker.
The APIs that return the engine's full result (face.match, face.analyze, documents.read,
screening.sanctions, contact.dossier and the other "open" ones) give it as an open dict, with every field the
engine returns:
for pair in match["results"]:
print(pair["similarity"], pair.get("match"))
Financial: cheques, cards, BIN, banks
Cheque and card photos are uploaded as files: a path, bytes, an open binary file, or (filename, bytes).
cheque = client.cheques.read(image="cheque.jpg")
print(cheque.status, cheque.micr.zones_detected)
if cheque.vision:
print(cheque.vision.payee, cheque.vision.amount)
line = client.cheques.micr_line(image=cheque_bytes, format="e13b")
card = client.cards.check(number="4242 4242 4242 4242", expiry="09/29")
bin_record = client.bin.lookup("424242")
bank = client.banks.lookup(name="Harbor Point Bank", country="US")
A Financial call returns the answer object as it is (cheque.object == "cheque_read"), with the same
last_response.
Credits
Each call costs its API's price in credits, charged only when it succeeds. Refused calls (no credits, no permission, rate limited) and calls that could not run are free.
Errors
from verisec import APIConnectionError, APIError, InsufficientCreditsError, InvalidRequestError, RateLimitError
try:
client.face.verify(image1="selfie.jpg", image2="id-photo.jpg")
except InsufficientCreditsError:
print("Not enough credits: top up in the console.")
except InvalidRequestError as e:
print(e.code, e.param, e.message) # e.g. image_unreadable image2 …
except RateLimitError as e:
print(f"Wait {e.retry_after} s.")
except APIError as e:
print(e.status, e.code, e.request_id)
except APIConnectionError as e:
print(e.message) # says whether the call may have run
| Class | When |
|---|---|
InvalidRequestError |
400, 413, 415, 422: a field is missing or not valid, or the file is too large or not an image |
AuthenticationError |
401: no key, or the key (or the client secret) is not valid |
InsufficientCreditsError |
402: not enough credits, or the key reached its spend cap. Nothing was charged. |
PermissionError |
403: the key may not call this API (verisec.PermissionError, not the built-in) |
NotFoundError |
404 |
RateLimitError |
429, with retry_after in seconds |
APIServerError |
5xx: the check could not run. Nothing was charged. |
APIConnectionError, APITimeoutError |
no answer |
All of them inherit from VeriSecError and carry status, code, message, param, request_id, retry_after
and headers. An image that cannot be read (a path that does not exist) raises VeriSecError naming the field,
before anything is sent.
Retries and timeouts
The library retries rate limits (429), server errors (5xx) and connections that never opened, up to max_retries
times with backoff (or the Retry-After the API sends): none of those were charged. It never sends a call again
after a timeout, or after the connection dropped once the request was sent, because the check may already have run
and been charged; you get an APITimeoutError or APIConnectionError saying so.
Every method also takes timeout, max_retries and extra_headers for that call:
client.cheques.read(image="cheque.jpg", timeout=280, max_retries=0, extra_headers={"X-Trace": "t-42"})
Webhooks
Deliveries are signed as Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature). Pass the raw
body, the headers and your endpoint's signing secret (whsec_…):
from flask import Flask, request
from verisec import SignatureVerificationError, Webhook
app = Flask(__name__)
@app.post("/webhooks/verisec")
def verisec_webhook():
try:
event = Webhook.construct_event(request.get_data(), request.headers, os.environ["VERISEC_WEBHOOK_SECRET"])
except SignatureVerificationError:
return "", 400
if event.type == "verification.completed":
print(event.data)
return "", 200
Webhook.verify_signature(...) returns True or False instead. Signatures older than 300 seconds are refused
(pass tolerance= to change it). Webhook.generate_test_headers(body, secret) signs a body for your own tests. The
same helpers are on client.webhooks.
A call the library does not wrap yet
result = client.http.request("POST", "/v1/some/new-api", body={"field": "value"}, envelope="data")
Development
python -m venv .venv
.venv/Scripts/python -m pip install -e ".[dev]" # Windows; .venv/bin/python elsewhere
.venv/Scripts/python scripts/generate.py # after the spec changes
.venv/Scripts/python -m pytest # unit + integration tests
scripts/generate.py writes src/verisec/_resources.py and src/verisec/types/ from ../spec/openapi.json
(standard library only; --check tells whether they are up to date). Everything else in src/verisec/ is written by
hand: the transport, authentication, errors, images and uploads, webhooks and the answer objects.
The integration tests start the shared test server (node ../test-server/server.mjs --port 4021), call every
operation through the library, and stop it when they end.
Metadata
Release files for verisec 1.0.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 | |
|---|---|---|---|
| verisec-1.0.0.tar.gz | 97.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| verisec-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 172.5 kB
Release files / verisec-1.0.0.tar.gz
| Download URL | verisec-1.0.0.tar.gz |
|---|---|
| Size | 97.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ddf3abd86ecf75ab8bf18edcc8bed8e10ef6388d33030b440b96721c62c6f7f7
|
|
BLAKE2b-256 checksum How to use checksums |
a28ac95bf0b96ee5b92c6f4b1e0519a5f5ec30565f932480519f768865ab80a9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.0
|
Release files / verisec-1.0.0-py3-none-any.whl
| Download URL | verisec-1.0.0-py3-none-any.whl |
|---|---|
| Size | 74.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4d2772d1995251199d7c0dd3b38e32c0de7a0c1b8b99fcb4bd7bdc74221a3e2c
|
|
BLAKE2b-256 checksum How to use checksums |
83109d9bd24f232258f6c050acbac87e9a773abc8d9b82f726ebc733fad19e9b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.0
|