Python SDK Reference
The attestd package provides sync and async clients, typed response models, named exceptions, and testing utilities. Requires Python 3.10+.
pip install attestdattestd.Client
Synchronous client. Thread-safe; a single instance can be shared across threads.
| Parameter | Description |
|---|---|
api_key | Required. Your Attestd API key. |
base_url | Base URL override. Defaults to "https://api.attestd.io". |
timeout | Request timeout in seconds. Default: 10.0. |
max_retries | Retry attempts on 5xx errors. Default: 2. Set to 0 to disable. |
transport | Optional httpx transport override — used in tests (see attestd.testing). |
import attestd
client = attestd.Client(api_key="YOUR_API_KEY")
result = client.check("nginx", "1.24.0")
print(result.risk_state) # "high"
print(result.actively_exploited) # False
print(result.cve_ids) # ["CVE-2021-23017"]
client.close()with attestd.Client(api_key="YOUR_API_KEY") as client:
result = client.check("log4j", "2.14.1")
if result.risk_state == "critical":
raise SystemExit("Deployment blocked")attestd.AsyncClient
Async client with identical parameters to Client. Use in FastAPI, async agents, or any asyncio environment. Call await client.check().
import attestd
import asyncio
async def check_deps():
async with attestd.AsyncClient(api_key="YOUR_API_KEY") as client:
result = await client.check("redis", "6.0.9")
return result
result = asyncio.run(check_deps())attestd.RiskResult
Frozen dataclass returned by client.check(). All fields are read-only. See the Response Field Reference for field semantics.
@dataclass(frozen=True, slots=True)
class RiskResult:
product: str
version: str
risk_state: RiskState # "critical" | "high" | "elevated" | "low" | "none"
risk_factors: list[str]
actively_exploited: bool
remote_exploitable: bool
authentication_required: bool
patch_available: bool
fixed_version: str | None
confidence: float
cve_ids: list[str]
last_updated: datetimeError types
| Exception | When raised |
|---|---|
AttestdAuthError | HTTP 401 — invalid or missing API key |
AttestdRateLimitError | HTTP 429 — rate limit exceeded. Has retry_after attribute (seconds). |
AttestdUnsupportedProductError | supported: false — product is outside coverage |
AttestdAPIError | HTTP 5xx, connection errors, or unexpected responses after all retries |
AttestdError | Base class for all above — catch this for a single broad handler |
Note on retry behaviour: 401 and 429 errors are never retried. Only transient 5xx errors and connection failures are retried (up to max_retries).
import attestd
client = attestd.Client(api_key="YOUR_API_KEY")
try:
result = client.check("nginx", "1.24.0")
except attestd.AttestdAuthError:
print("Invalid API key")
except attestd.AttestdRateLimitError as e:
print(f"Rate limited — retry in {e.retry_after}s")
except attestd.AttestdUnsupportedProductError as e:
# Product is outside coverage — not a safety clearance.
# Make an explicit policy decision: block, warn, or skip.
print(f"{e.product} is outside attestd coverage")
raise # or handle according to your policy
except attestd.AttestdAPIError as e:
print(f"API error: {e}")attestd.testing
The attestd.testing module provides httpx transport classes for injecting mock API responses without making real network calls. Use these to test your security branching logic against controlled, reproducible responses.
MockTransport / MockAsyncTransport
Returns the same response for every request.
import attestd
from attestd.testing import MockTransport, NGINX_VULNERABLE, NGINX_SAFE
def test_deployment_blocked_on_high_risk():
transport = MockTransport(200, NGINX_VULNERABLE)
client = attestd.Client(api_key="test", transport=transport)
result = client.check("nginx", "1.20.0")
assert result.risk_state == "high"
def test_deployment_allowed_when_safe():
transport = MockTransport(200, NGINX_SAFE)
client = attestd.Client(api_key="test", transport=transport)
result = client.check("nginx", "1.27.4")
assert result.risk_state == "none"SequentialMockTransport / SequentialMockAsyncTransport
Returns responses from a pre-defined sequence. Use for testing retry logic where the first one or two requests fail before a success. This pattern is especially useful for testing AI agent tools that must handle transient failures gracefully.
from attestd.testing import SequentialMockTransport, NGINX_SAFE
def test_retries_on_503():
transport = SequentialMockTransport([
(503, {}), # first attempt fails
(503, {}), # second attempt fails
(200, NGINX_SAFE), # third attempt succeeds
])
client = attestd.Client(api_key="test", transport=transport, max_retries=2)
result = client.check("nginx", "1.27.4")
assert result.risk_state == "none"
assert transport.call_count == 3Canned response bodies
Ready-made response dicts for common test scenarios. All are plain dicts and can be merged with | to override individual fields.
from attestd.testing import (
NGINX_SAFE, # risk_state: "none"
NGINX_VULNERABLE, # risk_state: "high"
LOG4J_CRITICAL, # risk_state: "critical", actively_exploited: true
UNSUPPORTED, # supported: false
)
# Override individual fields
transport = MockTransport(200, NGINX_VULNERABLE | {"risk_state": "critical"})