SDKs
Python SDK
Synchronous HTTP client for the Licentric licensing API, powered by httpx and Pydantic.
httpx and pydantic.Installation
pip install licentric
# Or install from source
git clone https://github.com/AbleVarghese/licentric.git
cd licentric/sdks/python
pip install -e .Client Initialization
from licentric import Licentric
# Initialize with your API key
client = Licentric(api_key="lk_live_your_api_key_here")
# With custom options
client = Licentric(
api_key="lk_live_your_api_key_here",
base_url="https://your-instance.licentric.com",
timeout=30.0,
)
# Use as a context manager to auto-close connections
with Licentric(api_key="lk_live_...") as client:
result = client.validate("MYAPP-XXXX-XXXX-XXXX-XXXX")Constructor Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| api_key | str | Required | Your Licentric API key (lk_live_... or lk_test_...) |
| base_url | str | Optional | API base URL. Defaults to https://licentric.com |
| timeout | float | Optional | Request timeout in seconds. Defaults to 30.0 |
Methods
validate()
Validate a license key. This endpoint authenticates via the license key itself — no API key header is required for validation.
| Parameter | Type | Required | Description |
|---|---|---|---|
| key | str | Required | The license key to validate |
| fingerprint | str | None | Optional | Machine fingerprint to check device activation |
| entitlements | list[str] | None | Optional | Entitlement codes the license must satisfy |
Returns a ValidationResult with valid, code, license, machine, and entitlements.
result = client.validate(
key="MYAPP-XXXX-XXXX-XXXX-XXXX",
fingerprint="abc123def456", # optional
entitlements=["export_pdf", "sso"], # optional
)
if result.valid:
print(f"License is valid: {result.code}")
print(f"Entitlements: {result.entitlements}")
else:
print(f"License invalid: {result.code}")activate()
Activate a machine for a license. Authenticates using the license key (License scheme).
| Parameter | Type | Required | Description |
|---|---|---|---|
| key | str | Required | The license key to activate against |
| fingerprint | str | Required | Unique machine fingerprint (min 8 characters) |
| name | str | None | Optional | Human-readable machine name |
| platform | str | None | Optional | Platform: "macos", "windows", "linux", "docker", or "kubernetes" |
Returns a Machine record.
machine = client.activate(
key="MYAPP-XXXX-XXXX-XXXX-XXXX",
fingerprint="abc123def456",
name="Developer Workstation", # optional
platform="macos", # optional: macos, windows, linux, docker, kubernetes
)
print(f"Machine activated: {machine.id}")
print(f"Fingerprint: {machine.fingerprint}")deactivate()
Deactivate (remove) a machine from a license to free up a device slot.
| Parameter | Type | Required | Description |
|---|---|---|---|
| machine_id | str | Required | UUID of the machine to deactivate |
| key | str | Required | The license key the machine belongs to |
# Release the device slot (useful for floating licenses)
client.deactivate(
machine_id="machine-uuid-here",
key="MYAPP-XXXX-XXXX-XXXX-XXXX",
)heartbeat()
Send a heartbeat for an active machine. Required when the policy has requireHeartbeat enabled.
| Parameter | Type | Required | Description |
|---|---|---|---|
| machine_id | str | Required | UUID of the machine to heartbeat |
| key | str | Required | The license key the machine belongs to |
# Send a heartbeat to keep the machine alive
client.heartbeat(
machine_id="machine-uuid-here",
key="MYAPP-XXXX-XXXX-XXXX-XXXX",
)
# Typical heartbeat loop
import time
while True:
client.heartbeat(machine_id=machine.id, key=license_key)
time.sleep(300) # every 5 minutescheckout()
Check out an Ed25519-signed offline license file. Authenticates using the API key (Bearer scheme).
| Parameter | Type | Required | Description |
|---|---|---|---|
| license_id | str | Required | UUID of the license to check out |
| fingerprint | str | Required | Machine fingerprint to bind the file to |
| ttl | int | Optional | Time-to-live in seconds. Defaults to 1209600 (14 days) |
Returns a LicenseFile with the certificate and metadata.
# Download an Ed25519-signed offline license file
license_file = client.checkout(
license_id="license-uuid-here",
fingerprint="abc123def456",
ttl=1209600, # 14 days (default)
)
print(f"Certificate: {license_file.certificate}")
print(f"TTL: {license_file.ttl} seconds")Response Models
| Model | Key Fields | Returned By |
|---|---|---|
| ValidationResult | valid, code, license, machine, entitlements | validate() |
| Machine | id, fingerprint, name, platform, heartbeat_status | activate(), heartbeat() |
| License | id, status, product_id, policy_id, expires_at | Nested in ValidationResult |
| LicenseFile | certificate, license, fingerprint, ttl | checkout() |
Error Handling
All SDK errors inherit from LicentricError. Each HTTP error status maps to a specific exception class.
| Exception | HTTP Status | When |
|---|---|---|
| AuthenticationError | 401 | Invalid or missing API key / license key |
| ValidationError | 400 | Invalid request parameters |
| NotFoundError | 404 | Resource does not exist |
| RateLimitError | 429 | Too many requests (includes retry_after) |
| LicentricError | Other | Base class for all other API errors |
from licentric.exceptions import (
LicentricError,
AuthenticationError,
ValidationError,
NotFoundError,
RateLimitError,
)
try:
result = client.validate("MYAPP-XXXX-XXXX-XXXX-XXXX")
except AuthenticationError as e:
print(f"Auth failed: {e.message}") # HTTP 401
except ValidationError as e:
print(f"Bad request: {e.message}") # HTTP 400
print(f"Details: {e.details}")
except NotFoundError as e:
print(f"Not found: {e.message}") # HTTP 404
except RateLimitError as e:
print(f"Rate limited, retry after {e.retry_after}s") # HTTP 429
except LicentricError as e:
print(f"API error {e.status_code}: {e.message}")
print(f"Code: {e.code}")