Skip to main content

usesmileid

PyPI version CI status License

Official Smile ID server-side SDK for Python — V3 APIs.

This project is under active development. It is not yet published to PyPI, and the API is not stable. Do not use it in production yet.

The package and the importable module are both named usesmileid. Python 3.8 or later is required.

Install

pip install usesmileid

Getting started

Construct one client with your partner ID and API key. The SDK manages authentication for you: it fetches a short-lived token from the Smile ID API, caches it until it expires, and refreshes it automatically. You never handle tokens yourself.

import os

import usesmileid

smile = usesmileid.Client(
    partner_id="1234",
    api_key=os.environ["SMILE_API_KEY"],
    environment="sandbox",  # the default
)

Partner IDs are displayed zero-padded in the portal (for example 002) but must be passed without leading zeros (2).

Environment selection

The client targets the sandbox by default. Set environment="production" to go live:

  • sandbox → https://testapi.smileidentity.com
  • production → https://api.smileidentity.com

Base URL override

Only sandbox and production are named environments. To reach any other Smile ID environment, pass base_url — it wins over environment:

smile = usesmileid.Client(
    partner_id="2",
    api_key=os.environ["SMILE_API_KEY"],
    base_url="https://your-environment.example.com",
)

The value must be an absolute https URL with no query or fragment — anything else raises usesmileid.errors.ValidationError at construction. There is deliberately no way to turn this off: partner credentials and personal data travel on every request. environment must be "sandbox" or "production"; any other value is rejected at construction.

Callback URLs

Callback URLs must also be https. The SDK validates default_callback_url when you construct the client, and any per-request callback_url before it sends the request.

Other options

Option Default Purpose
default_callback_url unset Used when a call omits callback_url; must be https
timeout 30 seconds Per-request total timeout; each method also accepts a timeout override
max_retries 2 Retries for idempotent operations only (see Retries below)
http_client SDK default Inject your own httpx.Client for testing or proxies

Binary inputs

Every image parameter (selfie_image, liveness_images, document, document_back, comparison_image) accepts a file path (str or os.PathLike), raw bytes, or an open file object.

All verification submissions need a consent record and the user's details. Build consent with the helper; pass user details as a dict or a usesmileid.UserDetails. At least one of email or phone_number is required — the SDK checks this before sending.

from datetime import datetime, timezone

consent = usesmileid.Consent.granted(
    granted_at=datetime.now(timezone.utc),
    notice_language="EN",
    notice_privacy_policy_url="https://example.com/privacy",
)
user_details = {
    "given_names": "Amina Fatou",
    "last_name": "Clearwater",
    "email": "amina.clearwater@example.com",
}

Non-production environments match test identities on given names, last name and email. An identity they do not recognise resolves to block.

The examples below assume smile, consent and user_details are defined as above.

Methods

Enhanced KYC

Verify an ID number against the issuing authority.

accepted = smile.enhanced_kyc.verify(
    country="NG",
    id_type="NIN",
    id_number="12345678901",
    user_details=user_details,
    consent=consent,
)
print(accepted.job_id, accepted.is_accepted)

Document verification

Verify a selfie against a photo of an identity document. id_type is optional; the document type is auto-classified when omitted.

accepted = smile.documents.verify(
    selfie_image="selfie.jpg",
    liveness_images=["live1.jpg", "live2.jpg", "live3.jpg",
                     "live4.jpg", "live5.jpg", "live6.jpg"],
    document="passport_front.jpg",
    country="NG",
    user_details=user_details,
    consent=consent,
)

Enhanced document verification

Same as document verification, but id_type is required and the ID information is also checked against the issuing authority.

accepted = smile.documents.verify_enhanced(
    selfie_image="selfie.jpg",
    liveness_images=["live1.jpg", "live2.jpg", "live3.jpg",
                     "live4.jpg", "live5.jpg", "live6.jpg"],
    document="license_front.jpg",
    document_back="license_back.jpg",
    country="NG",
    id_type="DRIVERS_LICENSE",
    user_details=user_details,
    consent=consent,
)

Residency document verification

Same as document verification, plus the visa endorsed in the passport. visa is required and id_type must be PASSPORT (the default). Results take longer than document verification, so rely on the callback rather than polling.

accepted = smile.documents.verify_residency(
    selfie_image="selfie.jpg",
    liveness_images=["live1.jpg", "live2.jpg", "live3.jpg",
                     "live4.jpg", "live5.jpg", "live6.jpg"],
    document="passport_front.jpg",
    visa="visa_page.jpg",
    country="ZA",
    user_details=user_details,
    consent=consent,
)

Biometric KYC

Verify a selfie against the photo on file with an ID authority.

accepted = smile.biometric_kyc.verify(
    selfie_image="selfie.jpg",
    liveness_images=["live1.jpg", "live2.jpg", "live3.jpg",
                     "live4.jpg", "live5.jpg", "live6.jpg"],
    country="NG",
    id_type="NIN",
    id_number="12345678901",
    user_details=user_details,
    consent=consent,
)

Biometric enrollment

Register a user's selfie for later authentication.

accepted = smile.biometric.enroll(
    selfie_image="selfie.jpg",
    liveness_images=["live1.jpg", "live2.jpg", "live3.jpg",
                     "live4.jpg", "live5.jpg", "live6.jpg"],
    user_details=user_details,
    consent=consent,
    user_id="user_01h8x9y2z3a4b5c6d7e8f9g0h1",
)

Biometric authentication

Authenticate a previously enrolled user. Set use_enrolled_image=True to re-use the enrolled image instead of uploading a new selfie.

accepted = smile.biometric.authenticate(
    user_id="user_01h8x9y2z3a4b5c6d7e8f9g0h1",
    selfie_image="selfie.jpg",
    liveness_images=["live1.jpg", "live2.jpg", "live3.jpg",
                     "live4.jpg", "live5.jpg", "live6.jpg"],
    user_details=user_details,
    consent=consent,
)

Selfie comparison

Compare a selfie against another image (a document photo, ID photo or portrait).

accepted = smile.biometric.compare(
    selfie_image="selfie.jpg",
    comparison_image="id_photo.jpg",
    comparison_image_type="ID_PHOTO",  # DOCUMENT | ID_PHOTO | PORTRAIT
    user_details=user_details,
    consent=consent,
)

Check a verification's status

status = smile.verifications.retrieve("job_01h8x9y2z3a4b5c6d7e8f9g0h1")
print(status.status)  # "processing", "not_found", or the decision

A running job reports status="processing". Once it finishes, status is the decision itself: clear, block, attention or error. message reads "Job completed" on every finished job, so read the decision from status, not from message.

A job that is not found returns a JobStatus with status="not_found" — it does not raise an error, so polling can distinguish "not found yet" cleanly.

Wait for a verification to complete

Polls the status endpoint while the job is processing or not_found, and returns as soon as it reaches a decision. status.is_complete is true for any decision. Raises usesmileid.errors.TimeoutError if the job does not finish in time.

status = smile.verifications.wait_until_complete(
    "job_01h8x9y2z3a4b5c6d7e8f9g0h1",
    interval=2.0,   # seconds between polls
    timeout=60.0,   # give up after this many seconds
)
print(status.status)  # "clear"

By default a not_found status is treated as "not found yet" and polling continues; pass treat_not_found_as_pending=False to return it immediately.

Replay a callback

Re-send the callback for a completed verification.

replayed = smile.verifications.replay(
    "job_01h8x9y2z3a4b5c6d7e8f9g0h1",
    callback_url="https://app.example.com/webhook",  # optional override
)

Replaying a job that is still processing raises usesmileid.errors.ConflictError.

Report user fraud

Flag a user as fraudulent, or clear a previous flag. flag_fraud and clear_fraud are convenience wrappers over report_fraud.

smile.users.flag_fraud(
    "user_01h8x9y2z3a4b5c6d7e8f9g0h1",
    reason="FIRST_PARTY_FRAUD",
    reported_by="risk@example.com",
)

smile.users.clear_fraud(
    "user_01h8x9y2z3a4b5c6d7e8f9g0h1",
    notes="Cleared after review",
    reported_by="risk@example.com",
)

reason is required when flagging; notes is required when clearing or when reason="OTHER". The SDK checks these rules before sending.

List bank codes

No authentication required.

banks = smile.services.bank_codes(country="NG")
for bank in banks.bank_codes:
    print(bank.code, bank.name)

List supported ID types

No authentication required.

id_types = smile.services.supported_id_types(country="NG")
for id_type in id_types.id_types:
    print(id_type.type, id_type.label)

List supported documents

No authentication required.

documents = smile.services.supported_documents(country_code="NG")
for entry in documents.valid_documents:
    print(entry.country.name, [d.code for d in entry.id_types])

Check ID type availability

status = smile.services.id_status(country="NG", id_type="NIN")
print(status.last_known_status, status.last_hour_success_rate)

Responses

Submission endpoints return an AcceptedResponse. Use response.is_accepted rather than comparing the raw status string — the API returns both "Accepted" and "accepted" depending on the endpoint, and is_accepted normalizes the difference.

Error handling

All errors raised by the SDK subclass usesmileid.errors.SmileIDError and expose status_code, status, message, code, request_id and raw_body.

import usesmileid.errors

try:
    accepted = smile.enhanced_kyc.verify(...)
except usesmileid.errors.PaymentRequiredError:
    ...  # top up your wallet
except usesmileid.errors.InvalidRequestError as err:
    print(err.status_code, err.message)
except usesmileid.errors.SmileIDError as err:
    ...  # everything else
Error Raised on
InvalidRequestError HTTP 400, 415
ValidationError Client-side validation, before any request is sent
AuthenticationError HTTP 401 (after one automatic token refresh)
PaymentRequiredError HTTP 402
PermissionError HTTP 403
NotFoundError HTTP 404
ConflictError HTTP 409
PayloadTooLargeError HTTP 413
RateLimitError HTTP 429
APIError HTTP 5xx
UnexpectedResponseError A success response whose body is not a JSON object
ConnectionError Network failure or timeout, no HTTP response
TimeoutError wait_until_complete deadline reached

Retries

The SDK automatically retries idempotent operations only: status and services reads, and the internal token fetch. Retries cover connection errors and HTTP 408, 429 and 5xx, with exponential backoff, and honour the Retry-After header. HTTP 409 is never retried.

Submission calls (verification, enrollment, authentication, compare, replay, fraud reports) are never retried automatically, because a retry could create a duplicate job. A connection failure on these raises usesmileid.errors.ConnectionError and you decide whether to retry.

Telemetry

Every request carries three telemetry headers: SmileID-Source-SDK, SmileID-Source-SDK-Version and User-Agent. They identify the SDK and its version for observability. They are never used for authentication and carry no personal data.

Licence

This project is licensed under the MIT licence. See LICENSE for details.

Security

See SECURITY.md for how to report a vulnerability.

Metadata

Release files for usesmileid 12.1.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 usesmileid 12.1.0
File Size Uploaded
usesmileid-12.1.0.tar.gz 45.1 kB Details

Built distribution (wheel)

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

Total release size: 72.9 kB

Release files / usesmileid-12.1.0.tar.gz

Download URL usesmileid-12.1.0.tar.gz
Size 45.1 kB
Tags Source
SHA-256 checksum
How to use checksums
c7fc5ddc5ec195621328185009b651b26614aaf40f019420ca7f006fad256ff6
BLAKE2b-256 checksum
How to use checksums
e5a3d2c31c9566ed9ffdc1aee927ef72b87c1ff3ef206f104f858f0e844bd6cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / usesmileid-12.1.0-py3-none-any.whl

Download URL usesmileid-12.1.0-py3-none-any.whl
Size 27.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
06ecd4ebb1ee5d45b70c8097f02b257f57e79675f74784ea813203baeaafe988
BLAKE2b-256 checksum
How to use checksums
3e1a2a5290be496e33c0250670677b40082db5da145d7add60badc811c5e0a79
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

12.1.0 This release

2 release files

12.0.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