Guides
Validation Caching
Reduce API calls and latency by caching validation results locally.
Why Cache Validation Results?
- Reduce latency — cached lookups are instant vs. network round-trips
- Reduce API calls — stay well within rate limits
- Partial offline support — serve cached results when the network is unavailable
- Better UX — no loading states for license checks in hot paths
1. SDK Built-in Caching
Both SDKs support TTL-based in-memory caching out of the box. Set the cache_ttl (Python) or cacheTtl (TypeScript) option when creating the client.
Python
from licentric import Licentric
# The SDK has built-in TTL-based caching
client = Licentric(
api_key="lk_live_your_key_here",
cache_ttl=600 # Cache validation results for 10 minutes
)
# First call hits the API
result = client.validate(
key="DSK-XXXX-XXXX-XXXX-XXXX",
fingerprint=client.fingerprint()
)
# Subsequent calls within 10 minutes use the cache
result = client.validate(
key="DSK-XXXX-XXXX-XXXX-XXXX",
fingerprint=client.fingerprint()
) # No API call — served from cacheTypeScript
import { Licentric } from "@licentric/sdk";
const client = new Licentric({
apiKey: "lk_live_your_key_here",
cacheTtl: 600, // Cache for 10 minutes
});
// First call hits the API
const result = await client.validate({
key: "DSK-XXXX-XXXX-XXXX-XXXX",
fingerprint: client.fingerprint(),
});
// Subsequent calls within 10 minutes use the cache
const cached = await client.validate({
key: "DSK-XXXX-XXXX-XXXX-XXXX",
fingerprint: client.fingerprint(),
}); // No API call — served from cache2. Cache Storage Options
Choose a caching strategy based on your application type.
| Strategy | Storage | Persistence | Best For | TTL |
|---|---|---|---|---|
| In-memory | Process memory | Lost on restart | Long-running servers, desktop apps | 5-15 minutes |
| File-based | Local filesystem | Survives restarts | CLI tools, desktop apps, containers | 15-60 minutes |
| Database | Redis, SQLite, etc. | Survives restarts | Multi-instance servers, microservices | 5-15 minutes |
3. File-Based Caching
For CLI tools and desktop apps that restart frequently, persist the cache to disk so it survives process restarts.
import json
import time
from pathlib import Path
class FileCache:
"""Persistent file-based cache for validation results."""
def __init__(self, cache_dir: str, ttl: int = 600):
self.cache_dir = Path(cache_dir)
self.cache_dir.mkdir(parents=True, exist_ok=True)
self.ttl = ttl
def get(self, key: str) -> dict | None:
path = self.cache_dir / f"{key}.json"
if not path.exists():
return None
data = json.loads(path.read_text())
if time.time() - data["cached_at"] > self.ttl:
path.unlink() # Expired
return None
return data["result"]
def set(self, key: str, result: dict):
path = self.cache_dir / f"{key}.json"
path.write_text(json.dumps({
"result": result,
"cached_at": time.time()
}))
# Usage
cache = FileCache("~/.myapp/license_cache", ttl=900)4. Event-Driven Invalidation
For the most up-to-date validation results, use webhooks to invalidate the cache when a license changes. This gives you the speed of caching with near-real-time accuracy.
# Event-driven cache invalidation via webhooks
# When a license changes, clear its cached validation
def handle_webhook(event):
event_type = event["type"]
# These events mean the cached validation is stale
invalidation_events = [
"license.suspended",
"license.revoked",
"license.expired",
"license.updated",
"entitlement.attached",
]
if event_type in invalidation_events:
license_id = event["data"]["id"]
cache.invalidate(license_id)5. Fallback Behavior
When the API is unreachable and the cache contains a valid (non-expired) result, allow access based on the cached validation. This provides graceful degradation without requiring full offline license files.
from licentric import Licentric, ApiError
client = Licentric(
api_key="lk_live_your_key_here",
cache_ttl=600
)
def validate_with_fallback(license_key: str) -> bool:
"""Validate with graceful degradation on API failure."""
try:
result = client.validate(
key=license_key,
fingerprint=client.fingerprint()
)
return result.valid
except ApiError:
# API unreachable — check cache
cached = client.get_cached_result(license_key)
if cached is not None:
return cached.valid
# No cache — check offline license file
return validate_offline_file("license.lic")