Handling Binary Responses
Some Saner SDK endpoints return binary data instead of JSON responses. These responses typically represent downloadable artifacts such as:
- agent installer
- export file
- report
- archive
- scan outputs
The SDK automatically detects binary responses and returns them as bytes, allowing you to save them directly to disk.
Example: Downloading a Binary File
Example usage from the Agent download endpoint:
from saner.cvem import CvemClient
client = CvemClient(
api_key="<YOUR_API_KEY>",
accountid="<YOUR_ACCOUNT_ID>",
base_url="https://saner.secpod.com"
)
response = client.Agent.download(
accountid="DemoAccount",
type="exe",
architecture="x64"
)
if isinstance(response, bytes):
with open("Saner-Agent.zip", "wb") as f:
f.write(response)
else:
print(response)Equivalent using the universal client (Agent is a cvem resource, so it's reached through the .cvem namespace):
from saner import SanerClient
client = SanerClient(
api_key="<YOUR_API_KEY>",
accountid="<YOUR_ACCOUNT_ID>",
base_url="https://saner.secpod.com"
)
response = client.cvem.Agent.download(
accountid="DemoAccount",
type="exe",
architecture="x64"
)This script:
- Requests a downloadable agent package
- Detects whether the response is binary
- Saves the file locally if binary data is returned
- Prints the response if an error occurs instead
How Binary Response Detection Works
The SDK automatically determines the response type based on the server content type.
Behavior:
JSON response → returned as dict
Binary response → returned as bytesThis allows your script to safely handle both scenarios without additional parsing logic.
Which Endpoints Return Binary Data
| Product | Resource.Method | Typical file type |
|---|---|---|
| platform | Report.getPdf | ZIP containing a PDF report |
| platform | Report.getDevicePdf | ZIP containing a PDF report (per-device) |
| cvem | Agent.download | Agent installer (.exe/.zip depending on type/architecture) |
| cvem | Agent.getActivationConfig | ZIP containing the agent activation configuration |
| cvem | AD.downloadAgent | Active Directory scan agent package |
| cvem | Device.getDetails | ZIP containing a device details report |
| cvem | Device.getReport | ZIP containing a device report |
These seven are annotated bytes | dict rather than plain bytes, because the server answers with JSON instead of a file on some failures. Every other resource method returns parsed JSON: a dict, except Device.getVulnerabilities, whose response has an array at the top level and so is typed list[dict]. Always check isinstance(response, bytes) rather than hardcoding this list, since it's the SDK's own detection (based on the server's Content-Type header) that actually decides the return type per request, not a fixed list on the client side.
Recommended Pattern for Binary Handling
Always check whether the response is bytes before writing it to a file:
if isinstance(response, bytes):
with open("output.zip", "wb") as f:
f.write(response)This prevents accidental file corruption when the server returns an error message instead of a binary file.
Memory Considerations for Large Downloads
The SDK reads the entire response body into memory before returning it. There is no streaming/chunked-download mode, and your code always gets a complete bytes object back rather than a file-like object to read incrementally. For the artifacts these endpoints typically return (agent installers, single reports), this is rarely an issue. If you're downloading unusually large files in a memory-constrained environment (a container with a tight memory limit, or many downloads running concurrently), account for the full file size being held in memory at once between the call returning and your write() call completing.
The max_response_bytes Cap
max_response_bytes CapBy default, the client refuses any single response body larger than 256 MB (max_response_bytes, configurable; see the "Client Configuration" guide), raising SanerResponseError instead of downloading it. This is checked twice: first against the server's declared Content-Length, before any of the body is downloaded (this is what actually protects memory), and again against the real size after the body is read, which is what catches a response that never declared a length at all.
For the endpoints in this guide, that default is comfortably above a typical agent installer or single report. If you're downloading something that legitimately exceeds 256 MB (a very large device report, for example), raise the limit or remove it entirely on the client used for that call:
from saner import SanerClient
client = SanerClient(
api_key="YOUR_API_KEY",
accountid="YOUR_ACCOUNT_ID",
max_response_bytes=None, # or a larger explicit number of bytes
)A response rejected for exceeding the cap surfaces as SanerResponseError, distinguishable from other response errors by its message ("... exceeds the N byte limit ..."). See the "Error Handling Model" guide.
Handling Errors During Binary Downloads
If the API returns an error instead of binary content, the SDK returns a structured response instead of bytes.
Example:
if isinstance(response, bytes):
save_file(response)
else:
print("Download failed:", response)This ensures your automation scripts remain safe and predictable.
Example Workflow for Automation Pipelines
Typical binary download workflow:
Step 1 → call SDK download method
Step 2 → check response type
Step 3 → save file if bytes
Step 4 → handle errors otherwiseThis pattern works across all endpoints that return files.
Binary Download Logging Support
When SDK logging is enabled, binary responses such as agent installers, archives, and exported files are recorded safely in structured logs without storing the raw file contents. Instead of writing binary data directly to the log file, the SDK records the size of the downloaded payload in bytes, ensuring logs remain readable and storage-efficient.
Example log entry:
{
"timestamp": "2026-04-03 14:28:14",
"method": "downloadAgent",
"url": "https://saner.secpod.com/...",
"payload": {...},
"status": 200,
"response": "<binary 5242880 bytes>",
"success": true,
"attempt": 1,
"time_ms": 842.11,
"accountid": "Default"
}The response field records the binary size instead of the file content:
<binary 5242880 bytes>This approach provides several benefits:
- confirms successful file download without exposing binary data
- keeps log files compact and structured
- avoids accidental storage of large artifacts inside logs
- preserves observability for automation workflows
- allows verification of expected download size during debugging
Combined with the attempt and time_ms fields, binary response logging gives clear visibility into download performance and retry behavior during file retrieval operations.
SDK Examples Available in API Reference
Each API endpoint in the Saner documentation includes a ready-to-use SDK example demonstrating:
- required parameters
- optional parameters
- expected response format
- binary handling steps (when applicable)
The example shown above is taken directly from the Agent download API reference and reflects the recommended usage pattern.
When working with any file-producing endpoint, refer to the SDK snippet provided alongside that API documentation for the most accurate implementation guidance.
Best Practices for Binary Responses
Recommended practices when working with binary downloads:
- always verify response type using
isinstance(response, bytes) - use correct file extensions
- store files in dedicated output directories
- enable logging when debugging download issues
- avoid assuming binary responses without checking type first
- use the client class matching the resource's product for single-endpoint scripts (
CvemClientforAgent/AD,PlatformClientforReport; see the "Client Types" guide)
Following this approach ensures reliable file handling across automation scripts and integration pipelines.
Updated 4 days ago
