Skip to main content

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)

Source distribution for verisec 1.0.0
File Size Uploaded
verisec-1.0.0.tar.gz 97.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for verisec 1.0.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

1.0.0 This release

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