This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Reason given by maintainers: placeholder upload to reserve package name
Saner SDK for Python
Official Python SDK for the SecPod Saner CVEM platform — continuous vulnerability and exposure management, patching, compliance, and endpoint management.
📖 Full API reference: apidocs-cvem.readme.io
This README is a quickstart. Every endpoint, parameter, and response shape is documented on the developer portal.
Installation
pip install secpod-saner-sdk
Requires Python 3.9+. The only runtime dependency is requests.
Quickstart
from saner import SanerClient
client = SanerClient(
api_key="<YOUR_API_KEY>",
accountid="<YOUR_ACCOUNT>",
base_url="https://saner.secpod.com",
)
orgs = client.platform.Organization.get()
print(orgs)
client.close()
The client owns pooled HTTP connections, so close it when you're done — or let a
with block do it:
with SanerClient(api_key="...", accountid="...") as client:
devices = client.cvem.Device.get(ip="192.168.1.10")
Configuration
api_key, accountid, base_url, and verify_ssl all fall back to
environment variables when omitted, so SanerClient() works unmodified across
dev, CI, and containers — only the environment differs. An explicit argument
always wins over the environment, which keeps the
multi-client pattern (several clients, several accounts, one
script) isolated even when the variables are set.
# with SANER_API_KEY / SANER_ACCOUNT_ID set in the environment
client = SanerClient()
| Argument | Default | Description |
|---|---|---|
api_key |
required — or SANER_API_KEY |
Saner API key, sent as the Authorization header. |
accountid |
required — or SANER_ACCOUNT_ID |
Account the client operates against. |
base_url |
SANER_BASE_URL, else https://saner.secpod.com |
Saner server URL. Use your regional or on-prem host. |
verify_ssl |
SANER_VERIFY_SSL ("true"/"false"), else True |
Verify TLS certificates. Only disable against a lab server or an internal host with a self-signed cert. |
timeout |
30 |
Per-request timeout, in seconds. |
max_retries |
3 |
Retries after the first attempt. 0 disables retrying. |
retry_backoff |
1.0 |
Base for exponential backoff, in seconds. |
retry_on_write |
False |
Also retry requests that may mutate server state. See below. |
enable_logging |
False |
Write a JSONL request/response log. |
log_dir |
"logs" |
Directory for the JSONL log. |
Resources
Resources are split into two products, each with its own client if you only need one of them:
| Resources | Client | |
|---|---|---|
| platform — shared across every Saner product | Organization, Account, User, Group, ServicePrivision, Report, MFA |
from saner.platform import PlatformClient → flat: client.Organization |
| cvem — vulnerability management, patching, compliance, endpoints | Agent, Device, Configuration, CyberHygiene, Vulnerability, Compliance, Patch, RiskPrioritization, PostureAnomaly, AssetExposure, Endpoint, NetworkScanner, AD |
from saner.cvem import CvemClient → flat: client.Patch |
SanerClient (the universal client) nests both under client.platform.* /
client.cvem.* instead. Prefer it when your process touches both products —
each client instance opens its own pair of pooled sessions, so creating a
PlatformClient and a CvemClient side by side doubles connection overhead
for no benefit over one SanerClient. Reach for PlatformClient/CvemClient
only when you exclusively work within one product and want flatter attribute
access.
Methods return the parsed JSON response as a dict. Endpoints that produce a
file (Report.getPdf, AD.downloadAgent) return bytes instead:
with SanerClient(api_key="...", accountid="...") as client:
report = client.platform.Report.getPdf(accountid="MyAccount", reportname="Asset Report")
if isinstance(report, bytes):
open("report.zip", "wb").write(report)
Error handling
Every SDK error derives from SanerError and carries structured context, so you
can branch on it without parsing message strings.
from saner import SanerClient, SanerAuthError, SanerRateLimitError, SanerError
try:
client.cvem.Device.get(ip="192.168.1.10")
except SanerAuthError:
... # 401 / 403 — bad or expired key
except SanerRateLimitError as e:
print("retry after", e.retry_after) # 429, exhausted retries
except SanerError as e:
print(e.status_code, e.method, e.url, e.attempts, e.response_body)
| Exception | Raised when |
|---|---|
SanerAuthError |
HTTP 401 / 403. |
SanerRateLimitError |
HTTP 429 after retries are exhausted. Exposes retry_after. |
SanerRequestError |
Network failure, SSL error, or a non-retryable HTTP error. |
SanerResponseError |
Server replied successfully but the body was not valid JSON. |
SanerError |
Base class — catch this to handle everything. |
Invalid arguments raise plain ValueError / TypeError before any network call
is made.
Retries
The SDK retries HTTP 429 and 500/502/503/504, plus connection failures, using
exponential backoff with jitter capped at 60s. A Retry-After header always
takes precedence over the computed delay.
Writes are not retried by default. Replaying a request that already reached the server can duplicate it — creating the same remediation job twice, for example. So retries apply to read-only calls only. Two exceptions are always safe and always retried: HTTP 429 (the server refused to process the request) and connect timeouts (the request never arrived).
Set retry_on_write=True if your workload is idempotent and you'd rather have
the retries.
Logging
With enable_logging=True the SDK writes one JSON object per request to
<log_dir>/secpod-saner-sdk__<account>__<hash>.jsonl, rotating weekly and keeping 8
backups.
Credentials (password, client_secret, token, ssl, …) are replaced with
***REDACTED*** and long values are truncated. Response bodies are not
redacted, and in a CVEM product those contain asset inventories and CVE
exposure data — treat the log directory as sensitive.
For application-level logging, the SDK also emits retry warnings through the
standard logging module under the saner.* logger namespace.
Thread safety
None of the three client classes (SanerClient, PlatformClient, CvemClient)
are thread-safe — each wraps requests.Session, which does not guarantee
thread safety. Create one client per thread.
Support
- API reference: https://apidocs-cvem.readme.io/docs/installation
- Support: support@secpod.com
License
Proprietary — see LICENSE. Use is governed by your SecPod license agreement.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file secpod_saner_sdk-0.1.0.dev0.tar.gz.
File metadata
- Download URL: secpod_saner_sdk-0.1.0.dev0.tar.gz
- Upload date:
- Size: 85.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17453a449a58b44e40a08a33508b86d2bc85c150893339d0a156f362711fe054
|
|
| MD5 |
03a90b91415ceabffb676c9ac81f43d0
|
|
| BLAKE2b-256 |
882399e71950aced8a0c37117f679d845195ea4596c44606f9ef2c7e2603d7d5
|
File details
Details for the file secpod_saner_sdk-0.1.0.dev0-py3-none-any.whl.
File metadata
- Download URL: secpod_saner_sdk-0.1.0.dev0-py3-none-any.whl
- Upload date:
- Size: 105.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c9cb11f04e0e450e2d27bc78862aee81203fc88328232c5375d8cee0c9100b5
|
|
| MD5 |
6d91ccb4579be100ea86298c489a367e
|
|
| BLAKE2b-256 |
2644579af0c606ee66d4a3f76a1829eddc7f069f69d9b3ccbcf1b0f08952ea4b
|