Client Configuration
The SDK ships three client classes: SanerClient, PlatformClient, and CvemClient (see the "Client Types" guide for the difference). All three manage authentication, connection settings, retries, logging behavior, and request execution controls identically, sharing the exact same constructor, shown below as SanerClient for concreteness, but every parameter and every environment variable fallback on this page applies unchanged to PlatformClient(...) and CvemClient(...) too.
This page explains each configuration parameter in detail along with practical usage examples. If you haven't yet decided which of the three classes to use, start with the Client Types guide instead. This page assumes you already know.
Constructor Signature
SanerClient(
api_key: str | None = None,
accountid: str | None = None,
base_url: str | None = None,
verify_ssl: bool | None = None,
enable_logging: bool = False,
log_dir: str = "logs",
timeout: int = 30,
max_retries: int = 3,
retry_backoff: float = 1.0,
retry_on_write: bool = False,
max_response_bytes: int | None = 268435456 # 256 MB
)Note:
timeout,max_retries,retry_backoff, andmax_response_bytesare validated eagerly: the constructor raisesValueErrorimmediately for a non-positivetimeout, a negativemax_retriesorretry_backoff, or a non-positivemax_response_bytes. This catches a typo likemax_retries=-1at construction time instead of it silently disabling the retry loop and returningNonefrom every call.
Note:
PlatformClientandCvemClientaccept exactly this same signature; the only difference between the three classes is which resources end up attached to the instance, not how the instance itself is configured.
Environment Variable Fallbacks
api_key, accountid, base_url, and verify_ssl can each be omitted from the constructor and supplied via an environment variable instead:
| Argument | Environment Variable |
|---|---|
api_key | SANER_API_KEY |
accountid | SANER_ACCOUNT_ID |
base_url | SANER_BASE_URL |
verify_ssl | SANER_VERIFY_SSL |
This means the same client construction works unmodified across local development, CI, and containers. Only the environment differs:
from saner import SanerClient
client = SanerClient() # reads SANER_API_KEY, SANER_ACCOUNT_ID, etc.Precedence: an explicit constructor argument always takes priority over the environment variable. The environment is only consulted when the argument is omitted (None). This matters if you're running multiple clients in one script against different accounts. Each client should still be given its own explicit api_key and accountid, otherwise every client that omits them will resolve to the same environment values.
SANER_VERIFY_SSL accepts True or False (case-insensitive). Any other value raises a ValueError naming the variable, rather than being silently misinterpreted.
If api_key or accountid is missing after checking both the argument and the environment variable, SanerClient() raises a ValueError that names the specific environment variable that would resolve it.
Loading Values From a .env File
The SDK reads these variables from the process environment. It does not read a .env file itself, so a .env sitting next to your script has no effect until something loads it. python-dotenv is the usual way to do that:
pip install python-dotenvCreate a .env file alongside your script:
SANER_API_KEY=YOUR_API_KEY
SANER_ACCOUNT_ID=YOUR_ACCOUNT
SANER_BASE_URL=https://eu.saner.secpod.com
SANER_VERIFY_SSL=TrueThen load it before constructing the client:
from dotenv import load_dotenv
from saner import SanerClient
load_dotenv() # must run before the client is constructed
client = SanerClient() # resolves everything from the loaded environment
response = client.platform.Organization.get()
print(response)A few things worth knowing:
- Order matters.
load_dotenv()has to run beforeSanerClient(), since the constructor resolves the environment once at construction time. Loading the file afterwards leaves the client already built with whatever was (or wasn't) set. - Real environment variables win. By default
load_dotenv()does not overwrite a variable that is already set in the process environment, so a leftover local.envcannot silently override the credentials your CI system or container injects. Passload_dotenv(override=True)if you want the opposite. - python-dotenv is not an SDK dependency. The SDK's only runtime dependency stays
requests. Installing python-dotenv is your choice, and any other loader (direnv,docker compose --env-file, your orchestrator's secret injection) works exactly as well, because all the SDK ever sees is the resulting environment. - Keep
.envout of version control. It holds a live API key. Add it to.gitignorebefore the first commit.
Required Parameters
These parameters must be provided, either as a constructor argument or via their environment variable.
api_key
Type: str
Required: Yes, or set SANER_API_KEY
The API key authenticates requests to the Saner platform. It ensures secure access to resources associated with your account.
Use Case
Use this parameter whenever performing authenticated operations such as retrieving organization details, managing users, or automating workflows.
Example
from saner import SanerClient
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="YOUR_ACCOUNT"
)Or, with SANER_API_KEY set in the environment:
client = SanerClient(accountid="YOUR_ACCOUNT")accountid
Type: str
Required: Yes, or set SANER_ACCOUNT_ID
Identifies the account context in which API operations are executed.
Use Case
Useful when working across multiple environments such as:
- managed service provider (MSP) accounts
- multi-tenant automation scripts
Example
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="DEMO_ACCOUNT"
)Optional Parameters
These parameters allow customization of SDK behavior for production-ready integrations.
base_url
Type: str
Default: SANER_BASE_URL, else https://saner.secpod.com
Specifies the Saner platform server endpoint. This is typically required when working with region-specific deployments or private infrastructure.
Use Case
Use this parameter when:
- accessing EU region servers
- switching between staging and production
- testing against internal environments
Example
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="YOUR_ACCOUNT",
base_url="https://eu.saner.secpod.com"
)verify_ssl
Type: bool
Default: SANER_VERIFY_SSL (True/False), else True
Controls whether SSL certificate verification is enforced for HTTPS requests.
Use Case
Disable SSL verification only when working against a lab server or an internal host using a self-signed certificate.
⚠️ Not recommended for production systems.
Example
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="YOUR_ACCOUNT",
verify_ssl=False
)Or, with SANER_VERIFY_SSL=false set in the environment:
client = SanerClient(api_key="YOUR_API_KEY", accountid="YOUR_ACCOUNT")enable_logging
Type: bool
Default: False
Enables SDK-level logging for debugging and monitoring API interactions.
When enabled, request activity and execution details are stored locally. Sensitive values (such as passwords and tokens) are automatically redacted before being written. See the Logging guide for details.
Use Case
Recommended for:
- debugging integration issues
- monitoring automation workflows
- troubleshooting intermittent API failures
- auditing request execution history
Example
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="YOUR_ACCOUNT",
enable_logging=True
)log_dir
Type: str
Default: "logs"
Defines the directory where SDK log files are stored when logging is enabled.
If the directory does not exist, it is created automatically.
Note:
log_diris one of the inputs used to derive the log file's unique name (alongsideaccountidandbase_url). Using a differentlog_dirfor the same account and server produces a different log filename. See the Logging guide.
Use Case
Useful when:
- storing logs in centralized monitoring folders
- separating logs per project
- integrating with log collectors
Example
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="YOUR_ACCOUNT",
enable_logging=True,
log_dir="saner_sdk_logs"
)timeout
Type: int
Default: 30
Specifies the maximum number of seconds the SDK waits for a server response before terminating the request.
Use Case
Increase timeout when:
- running large queries
- retrieving extensive asset data
- operating in high-latency environments
Reduce timeout when:
- building fast-fail automation pipelines
- running CI/CD checks
Example
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="YOUR_ACCOUNT",
timeout=60
)max_retries
Type: int
Default: 3
Controls how many retry attempts the SDK performs when requests fail due to temporary issues such as network instability, server throttling, or transient server errors.
Retries use exponential backoff automatically, and are only applied to failures considered safe to retry. See the Retry and Timeout Behaviour guide for the full eligibility rules.
Use Case
Increase retry attempts when:
- running scheduled automation jobs
- operating across unstable networks
- executing large batch workflows
Example
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="YOUR_ACCOUNT",
max_retries=5
)retry_backoff
Type: float
Default: 1.0
Defines the exponential delay multiplier between retry attempts.
Each retry waits progressively longer before the next attempt, up to a fixed cap of 60 seconds per attempt.
Example retry timing pattern (approximate):
Attempt 1 → immediate
Attempt 2 → 1s delay
Attempt 3 → 2s delay
Attempt 4 → 4s delay
Use Case
Increase backoff when:
- avoiding rate limits
- interacting with busy servers
- running large automation loops
Example
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="YOUR_ACCOUNT",
retry_backoff=2.0
)retry_on_write
Type: bool
Default: False
Controls whether the SDK retries requests that may mutate server state (creating, updating, or deleting data), in addition to the read-only and connection-level failures it always retries.
By default, a write request is only retried automatically when the SDK can tell the failure occurred before the request reached the server (so nothing could have been applied yet). A write that times out or fails with a 500-series error after being sent is not retried by default, because the first attempt may have already succeeded server-side, and retrying it could duplicate the action (for example, creating the same remediation job twice).
HTTP 429 (rate limiting) is always retried regardless of this setting, since the server did not process the request at all.
Use Case
Enable this only when you have confirmed the operations you call are safe to repeat, for example when the server de-duplicates by name or by an idempotency key.
Example
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="YOUR_ACCOUNT",
retry_on_write=True
)See the "Retry and Timeout Behaviour" guide for the complete retry-eligibility table.
max_response_bytes
Type: int | None
Default: 268435456 (256 MB)
Caps the size of a single response body the client will accept. A server that declares a larger Content-Length is rejected with SanerResponseError before the body is downloaded; a response that never declares a length (chunked) is checked again once it's been read, so an oversized one still can't be held in memory afterward.
Use Case
Lower it when:
- running in a memory-constrained environment (small container, serverless function) and want a tighter guarantee
- you want a fast, explicit failure if an endpoint ever starts returning unexpectedly large payloads
Raise it (or set it to None to remove the cap entirely) when:
- downloading a binary artifact you know is legitimately larger than 256 MB (see the Handling Binary Responses guide)
Example
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="YOUR_ACCOUNT",
max_response_bytes=None, # no cap
)Additional Client Behavior
A few behaviors are built into every SanerClient instance and aren't constructor parameters, but are worth knowing about.
Closing the Client
Each SanerClient owns two pooled HTTP connections (one for RPC calls, one for REST calls). Close them when you're done, or use the client as a context manager:
from saner import SanerClient
with SanerClient(api_key="YOUR_API_KEY", accountid="YOUR_ACCOUNT") as client:
response = client.platform.Organization.get()
print(response)
# connections are closed automatically hereCalling client.close() directly is equally valid and safe to call more than once.
User-Agent Identification
Every request sent by the SDK includes a User-Agent header identifying the SDK version and Python runtime, for example:
secpod-saner-sdk/0.1.0 python/3.11.4 requests/2.32.5This is informational only and has no effect on request behavior or authentication. The same value is sent regardless of which of the three client classes made the request.
Example: Full Configuration Sample
Below is a production-ready configuration example combining multiple parameters:
from saner import SanerClient
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="YOUR_ACCOUNT",
base_url="https://eu.saner.secpod.com",
verify_ssl=True,
enable_logging=True,
log_dir="logs",
timeout=45,
max_retries=5,
retry_backoff=2.0,
retry_on_write=False,
max_response_bytes=268435456,
)This configuration enables secure communication, structured logging, retry resilience without risking duplicated writes, extended timeout handling, and a response-size guardrail, all defaults suited to enterprise-grade automation workflows.
Equivalently, with every value except timeout/max_retries/retry_backoff supplied through the environment (SANER_API_KEY, SANER_ACCOUNT_ID, SANER_BASE_URL, SANER_VERIFY_SSL):
client = SanerClient(
timeout=45,
max_retries=5,
retry_backoff=2.0,
)Updated about 9 hours ago
