Getting Started with the ZENTRA Cloud Python SDK
zentracloud is a Python client for the ZENTRA Cloud v5 API
Install and authenticate the zentracloud Python package, list your devices, and download readings as typed objects you can drop
straight into your application — synchronous or asynchronous.
zentracloud is a Python client for the ZENTRA Cloud v5 API. It handles authentication, pagination, rate-limit pacing, and response parsing, and returns fully typed objects with first-class editor autocomplete. It is built for developers integrating ZENTRA Cloud data into their own software — backend services, web apps, and data pipelines. (For interactive analysis in R, see the R Client (zentraR) articles.)
This article covers the full workflow: installing the package, authenticating, discovering your devices, downloading readings (sync and async), handling errors, and understanding rate limits.
Installation
Prerequisites
You need Python 3.9 or newer. Download it from python.org if you don’t already have it, and confirm your version:
python --versionWe recommend installing into a virtual environment so the package and its dependencies stay isolated from the rest of your system:
python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS/Linux: source .venv/bin/activateInstall from PyPI
pip install zentracloudThat’s it — pip also installs the two runtime dependencies (httpx and pydantic) automatically. Verify the install:
python -c "import zentracloud; print(zentracloud.__version__)"Authenticate
Every request needs your ZENTRA Cloud API key. Generate one from your account (see the API Token article for step-by-step instructions), then provide it to the client in one of two ways.
Pass it directly when you create the client:
from zentracloud import ZentraClient
client = ZentraClient(api_key="your-api-key")Or set the ZENTRACLOUD_API_KEY environment variable and let the client pick it up automatically — the recommended approach for servers and CI, where the key lives in your secret manager rather than your code:
export ZENTRACLOUD_API_KEY="your-api-key" # Windows PowerShell: $env:ZENTRACLOUD_API_KEY = "your-api-key"from zentracloud import ZentraClient
client = ZentraClient() # reads ZENTRACLOUD_API_KEYThe client
The SDK is organized by API version and resource: client.v5.devices.<action>(). This keeps the surface tidy as the API grows. Use the client as a context manager so its underlying HTTP connection is cleaned up for you:
from zentracloud import ZentraClient
with ZentraClient(api_key="your-api-key") as client:
devices = client.v5.devices.list()
...There are two clients with an identical shape: ZentraClient (synchronous) and AsyncZentraClient (asynchronous — see Asynchronous usage).
Discover your devices
client.v5.devices.list() returns every device your key can access, as a list of typed Device objects. Pagination is handled for you.
with ZentraClient(api_key="your-api-key") as client:
devices = client.v5.devices.list()
for device in devices:
print(device.device_id, device.name, device.model)Pass expand to attach extra detail to each device — any of "max_min_timestamp" (first/last measurement times), "settings", "hardware", "connectivity", or "subscription":
devices = client.v5.devices.list(expand=["max_min_timestamp", "hardware"])
d = devices[0]
print(d.data_range.first_measurement, d.data_range.last_measurement)
print(d.hardware.firmware_version)If you’d rather stream devices than build the whole list in memory, use client.v5.devices.iter(), which yields devices as pages
arrive.
See the GET List Devices API article for the full parameter reference. This endpoint is rate-limited per user (your API key) — see Rate limits.
Download readings
client.v5.devices.data() fetches a device’s time-series readings and returns them as a list of typed Reading objects — one per (port, measurement, timestamp). All pages are retrieved automatically and the request is paced to respect the rate limit.
with ZentraClient(api_key="your-api-key") as client:
readings = client.v5.devices.data(
device_id="z6-00930",
start="2026-01-01",
end="2026-02-01",
)
for r in readings:
print(r.datetime, r.measurement, r.value, r.unit)Each Reading has: device_id, datetime (timezone-aware UTC), timestamp (epoch seconds), port_num, sensor_name,
measurement, value, unit, and error_code.
Time windows. start and end accept a datetime, a date, epoch seconds (int/float), or an ISO-8601 string. Naive datetimes are treated as UTC. Omit the window to get the API’s default (most recent) range. You can also pass start_timestamp / end_timestamp as epoch seconds instead (but not both forms at once).
Options. direction is "ascending" (default) or "descending"; units is "metric" (default) or "imperial".
Streaming large pulls
A wide time range can return a lot of readings. Instead of materializing them all, stream them and process (or store) as they arrive:
with ZentraClient(api_key="your-api-key") as client:
for reading in client.v5.devices.iter_data(device_id="z6-00930", start="2026-01-01"):
save_to_database(reading) # handled one at a time — memory-safeFor page-level control (for example to persist a next_token between runs), use client.v5.devices.iter_data_pages(...), which yields a ReadingPage with its readings and pagination cursor.
Quality flags
error_code is 0 for a valid reading; any non-zero value flags a device or sensor problem. Every Reading exposes a human-readable
error_label:
for r in readings:
if r.error_code != 0:
print(r.datetime, r.measurement, "->", r.error_label)See the GET Device Readings and Device & Sensor Error Codes API articles for details. The readings endpoint is rate-limited per device — see Rate limits.
Asynchronous usage
Every method has an async twin on AsyncZentraClient, with the same client.v5.devices.* shape. This drops cleanly into asyncio, FastAPI, or any async stack — use async with, await the collect methods, and async for the iterators:
import asyncio
from zentracloud import AsyncZentraClient
async def main():
async with AsyncZentraClient(api_key="your-api-key") as client:
devices = await client.v5.devices.list()
async for reading in client.v5.devices.iter_data(device_id="z6-00930", start="2026-01-01"):
print(reading.datetime, reading.value)
asyncio.run(main())Working with the data
The typed objects work with whatever tools you already use. To load readings into a pandas DataFrame, for example (pandas is not a dependency of the SDK — install it separately if you want it):
import pandas as pd
readings = client.v5.devices.data(device_id="z6-00930", start="2026-01-01")
df = pd.DataFrame([r.model_dump() for r in readings])model_dump() turns any Device or Reading into a plain dictionary, ready for a DataFrame, JSON serialization, or your own storage layer.
Handling errors
Every error the client raises is a subclass of zentracloud.ZentraError, so you can catch one type to handle them all — or catch a specific one:
Exception | Raised when |
| The API key is missing, invalid, or lacks access (HTTP 401 / 403) |
| The request parameters were rejected (HTTP 422) or failed client-side validation |
| The rate limit was exceeded and automatic retries were exhausted (HTTP 429) |
| ZENTRA Cloud could not be reached (network/transport failure) |
| Any other unexpected API response |
Each carries a .status_code and a .detail message from the API when available:
from zentracloud import ZentraClient, ZentraError, ZentraAuthError
try:
with ZentraClient(api_key="your-api-key") as client:
readings = client.v5.devices.data(device_id="z6-00930", start="2026-01-01")
except ZentraAuthError:
print("Check your API key.")
except ZentraError as e:
print(f"Request failed (HTTP {e.status_code}): {e.detail}")Rate limits
The v5 API allows a burst of about 5 requests, then refills at roughly 1 request per minute (a GCRA rate limiter; about 300 seconds of idle time restores the full burst). The budget is scoped differently by endpoint:
- Listing devices (
v5.devices.list()) is limited per user (your API key). - Downloading readings (
v5.devices.data()/iter_data()) is limited per device, so different devices have independent budgets and can be fetched concurrently without competing for a single limit.
You don’t have to manage this yourself: on an HTTP 429 the client reads the API’s “next allowed” time and waits until then before retrying
automatically (up to max_retries, which you can configure on the client). Very large historical backfills will still take time, but routine pulls stay well within the limits. See the Rate Limiting API article for the full policy.
Next steps
- v5 API Overview — how the ZENTRA Cloud v5 API is structured
- API Token — generate and manage your API key
- Rate Limiting — the full rate-limit policy
- Device & Sensor Error Codes — what each
error_codemeans
Prefer R? See the R Client (zentraR) articles for the interactive-analysis toolkit, including tidy data frames and incremental local syncs.
How did we do?