Python SDK
The certivu Python package provides sync and async clients for signing and verifying AI-generated content. It is designed for AI/ML pipelines where Python is the primary language.
Install
Section titled “Install”pip install certivuRequires Python 3.9+.
Initialize
Section titled “Initialize”from certivu import CertivuClient
client = CertivuClient( api_key="ctv_key_abc123", generator_id="your-generator-uuid", # required for signing)Config can also come from environment variables:
export CERTIVU_API_KEY=ctv_key_abc123export CERTIVU_GENERATOR_ID=your-generator-uuidSign content
Section titled “Sign content”import asynciofrom pathlib import Path
image_bytes = Path("output.jpg").read_bytes()
result = client.sign( content=image_bytes, model="stable-diffusion-xl",)
print(result.token) # ctv_7f3kx9mq2...print(result.record_id) # rec-uuidprint(len(result.watermarked_content)) # signed, watermarked image bytes — use this oneThe API handles watermarking, hashing, and ML-DSA signing server-side. result.watermarked_content is the image with the ctv_ token in XMP metadata and the watermark embedded in the frequency domain.
Verify content
Section titled “Verify content”Verification is always free — no API key required.
result = client.verify(content=image_bytes)
if result.authentic and result.confidence == "high": print(f"Verified — {result.provenance.org} · {result.provenance.signed_at}")elif result.tampered: print("Content has been modified since signing")else: print(f"Not verified: {result.reason}")Pass a token explicitly to skip watermark extraction:
result = client.verify(content=image_bytes, token="ctv_7f3kx9mq2...")Confidence levels
Section titled “Confidence levels”| Level | Signals | Meaning |
|---|---|---|
high | Watermark ✓ + Record ✓ + Signature ✓ | Full chain intact |
medium | Record ✓ + Signature ✓ | Re-uploaded without watermark |
low | Partial | Something is off |
none | Nothing found | Not signed by Certivu |
Token status
Section titled “Token status”Lightweight lookup without re-uploading the image — CDN-cacheable:
status = client.get_token_status("ctv_7f3kx9mq2...")print(status.generator_status) # "active" or "revoked"print(status.signed_at)Batch verify
Section titled “Batch verify”Up to 50 items per call:
results = client.verify_batch([ {"content": image1_bytes}, {"content": image2_bytes, "token": "ctv_..."},])
for r in results: print(r.authentic, r.confidence)Batch sign
Section titled “Batch sign”Up to 50 records per call (HTTP 207 multi-status):
results = client.sign_batch([ {"content": img1, "model": "sdxl"}, {"content": img2, "model": "sdxl"},])
for r in results: if "error" in r: print("Failed:", r["error"]) else: print(r["token"])Audit log
Section titled “Audit log”page = client.get_audit_log(page=1, limit=50)
for event in page.events: print(event.type, event.timestamp)
print(f"Total: {page.total}")Async client
Section titled “Async client”All methods are available on AsyncCertivuClient:
from certivu import AsyncCertivuClient
async def main(): async with AsyncCertivuClient(api_key="ctv_key_abc123") as client: result = await client.verify(content=image_bytes) print(result.confident, result.confidence)
results = await client.verify_batch([ {"content": img} for img in image_list ])
asyncio.run(main())Error handling
Section titled “Error handling”from certivu import AuthError, QuotaError, NotFoundError, CertivuError
try: result = client.sign(content=image_bytes, model="sdxl")except QuotaError as e: print("Quota exceeded. Upgrade at:", e.upgrade_url)except AuthError: print("Invalid API key")except CertivuError as e: print(f"Error {e.status_code}: {e}")Source
Section titled “Source”The Python SDK lives at packages/sdk-python/ in the Certivu repository.