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, and max_response_bytes are validated eagerly: the constructor raises ValueError immediately for a non-positive timeout, a negative max_retries or retry_backoff, or a non-positive max_response_bytes. This catches a typo like max_retries=-1 at construction time instead of it silently disabling the retry loop and returning None from every call.

Note: PlatformClient and CvemClient accept 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:

ArgumentEnvironment Variable
api_keySANER_API_KEY
accountidSANER_ACCOUNT_ID
base_urlSANER_BASE_URL
verify_sslSANER_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-dotenv

Create 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=True

Then 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 before SanerClient(), 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 .env cannot silently override the credentials your CI system or container injects. Pass load_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 .env out of version control. It holds a live API key. Add it to .gitignore before 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_dir is one of the inputs used to derive the log file's unique name (alongside accountid and base_url). Using a different log_dir for 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:

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 here

Calling 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.5

This 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,
)

Did this page help you?