Python client library for the Koios IoT platform — typed GraphQL & REST wrapper
Project description
Koios Client
Typed Python SDK for the Koios IoT platform. Provides two API layers:
- High-level resource API —
Device,Tag,Modelobjects with intuitive methods like.enable(),.update(),.delete() - Low-level GraphQL client — fully-typed generated client via
client.gqlwith IDE autocomplete
Plus REST helpers for file operations, import/export, backups, and trend data.
Installation
pip install koios-client
Or install from source:
pip install -e ".[dev]"
Quick Start
from koios_client import KoiosClient
client = KoiosClient(
hostname="koios.example.com",
client_id="your-client-uuid",
client_secret="your-client-secret",
)
# High-level resource API — fetch by ID (default) or slug
device = client.device(1)
device.enable()
for tag in device.tags():
print(tag.name, tag.value)
# Low-level GraphQL (fully typed — IDE autocomplete works)
result = client.gql.get_devices()
for device in result.devices.results:
print(device.name, device.status)
client.close()
Use as a context manager for automatic cleanup:
with KoiosClient("koios.example.com", "client-id", "secret") as client:
device = client.device(1)
print(device.name, device.enabled)
High-Level Resource API
The resource layer wraps the generated GraphQL client with intuitive Python objects. All resource classes are importable from the top-level package:
from koios_client import Device, Tag, Model, PaginatedList
Fetching Resources
Every resource can be fetched by ID (default positional arg) or slug (UUID).
# By ID (default — integer database primary key)
device = client.device(1)
tag = client.tag(42)
model = client.model(5)
# By slug (UUID — useful when you already have the identifier)
device = client.device(slug="550e8400-e29b-41d4-a716-446655440000")
# By name — use the list query with filters
from koios_client.types import DeviceFilter, StrFilterLookup
devices = client.devices(filters=DeviceFilter(name=StrFilterLookup(exact="My Device")))
device = devices[0]
# List with filtering, ordering, and pagination
from koios_client.types import OffsetPaginationInput
devices = client.devices(
filters=DeviceFilter(enabled=True),
pagination=OffsetPaginationInput(limit=50),
)
print(f"{devices.total_count} total devices")
for device in devices:
print(device.name, device.slug, device.enabled)
PaginatedList supports iteration, indexing, len(), and bool():
tags = client.tags()
print(len(tags)) # Number of items on this page
print(tags.total_count) # Total matching items on server
first = tags[0] # Index access
CRUD Operations
from koios_client.types import DeviceInput
# Create
device = client.create_device(DeviceInput(
name="New Device",
protocol_id="1",
enabled=True,
))
# Update (returns a fresh object with all fields refreshed)
device = device.update(name="Renamed Device", scan_rate=5.0)
# Enable / Disable (convenience wrappers around update)
device = device.enable()
device = device.disable()
# Duplicate
copy = device.duplicate("Device Copy", description="Cloned from original")
# Delete
device.delete()
Parent-Child Accessors
# Device → Tags
device = client.device(1)
tags = device.tags(
filters=TagFilter(enabled=True),
pagination=OffsetPaginationInput(limit=100),
)
# Model → Bindings
model = client.model(5)
for binding in model.bindings():
print(binding.name, binding.usage, binding.normalization_type)
binding.update(normalization_type="MIN_MAX")
Bulk Operations
# Bulk enable / disable
client.enable_devices(["1", "2", "3"])
client.disable_tags(["10", "11", "12"])
# Bulk delete
client.delete_devices(["1", "2", "3"])
client.delete_tags(["10", "11", "12"])
client.delete_models(["20", "21"])
client.delete_scan_groups(["30", "31"])
client.delete_device_sets(["40", "41"])
Live Data
Fetch real-time values from the Redis cache:
device = client.device(1)
live = device.live()
print(live.get("status"), live.get("error_message"))
tag = client.tag(42)
live = tag.live()
print(live.get("value"), live.get("timestamp"), live.get("quality"))
Import / Export with Resources
# Import with dry_run (default) — returns preview without applying
preview = client.import_devices("devices.csv")
print(preview)
# Import for real
result = client.import_devices("devices.csv", dry_run=False)
# Export a single device/model
csv_bytes = device.export_csv()
zip_bytes = model.export()
All Resource Types
All singular methods accept id (positional, integer) or slug= (UUID). Exactly one must be provided.
| Method | Returns | Notes |
|---|---|---|
client.device(id, slug=) |
Device |
|
client.devices(...) |
PaginatedList[Device] |
filters, order, pagination |
client.create_device(data) |
Device |
|
client.enable_devices(ids) |
list[Device] |
|
client.disable_devices(ids) |
list[Device] |
|
client.delete_devices(ids) |
None |
|
client.import_devices(file) |
dict |
dry_run=True by default |
client.tag(id, slug=) |
Tag |
|
client.tags(...) |
PaginatedList[Tag] |
|
client.create_tag(data) |
Tag |
|
client.enable_tags(ids) |
list[Tag] |
|
client.disable_tags(ids) |
list[Tag] |
|
client.delete_tags(ids) |
None |
|
client.import_tags(file) |
dict |
dry_run=True by default |
client.model(id, slug=) |
Model |
|
client.models(...) |
PaginatedList[Model] |
|
client.create_model(data) |
Model |
|
client.enable_models(ids) |
list[Model] |
|
client.disable_models(ids) |
list[Model] |
|
client.delete_models(ids) |
None |
|
client.import_models(file) |
dict |
dry_run=True by default |
client.scan_group(id, slug=) |
ScanGroup |
|
client.scan_groups(...) |
PaginatedList[ScanGroup] |
|
client.create_scan_group(data) |
ScanGroup |
|
client.enable_scan_groups(ids) |
list[ScanGroup] |
|
client.disable_scan_groups(ids) |
list[ScanGroup] |
|
client.delete_scan_groups(ids) |
None |
|
client.device_set(id, slug=) |
DeviceSet |
|
client.device_sets(...) |
PaginatedList[DeviceSet] |
|
client.create_device_set(data) |
DeviceSet |
|
client.delete_device_sets(ids) |
None |
|
client.protocol(id, slug=) |
Protocol |
read-only |
client.protocols() |
list[Protocol] |
not paginated |
Endpoint Clients
# List endpoint clients
clients = client.endpoint_clients()
# Get a single endpoint client by ID
ec = client.endpoint_client("123")
# Create a new endpoint client (returns one-time secret)
result = client.create_endpoint_client("My Integration", description="Data pipeline")
# Set permissions
client.set_endpoint_client_permissions(
client_id="123",
permission_ids=["1", "2", "3"],
)
Low-Level GraphQL Client
All GraphQL queries and mutations are available via client.gql with fully typed parameters and return values.
Querying Data
from koios_client.types import (
DeviceFilter,
OffsetPaginationInput,
StatusChoices,
)
# Paginated queries
devices = client.gql.get_devices(
pagination=OffsetPaginationInput(limit=50),
)
print(f"Total: {devices.devices.total_count}")
for device in devices.devices.results:
print(device.name, device.status, device.protocol.name)
# Filtered queries
tags = client.gql.get_tags(
filters=TagFilter(enabled=True),
pagination=OffsetPaginationInput(limit=100),
)
# Single item by slug
device = client.gql.get_device(slug="my-device")
tag = client.gql.get_tag(slug="my-tag")
Mutations
Single-item mutations return a union of the entity type or OperationInfo. Use check_operation to unwrap:
from koios_client.types import DeviceInput
raw = client.gql.create_device(data=DeviceInput(
name="New Device",
protocol_id="1",
enabled=True,
))
# Raises OperationError if the server returned validation/permission errors
device = KoiosClient.check_operation(raw.create_device)
print(f"Created: {device.name} ({device.slug})")
Available Enums
All GraphQL enums are available as typed Python enums:
from koios_client.types import (
StatusChoices, # RUNNING, STOPPED, FAILED
UsageChoices, # INPUT, OUTPUT, VIRTUAL
AggregateFunction, # MEAN, SUM, MIN, MAX, FIRST, LAST, COUNT
DeviceErrorCodeChoices,
TagErrorCodeChoices,
ProtocolReferenceCodeChoices, # OPCUA, MODBUS_TCP, ETHERNET_IP, ...
)
REST API
Operations that involve file uploads, background tasks, or binary responses use REST endpoints exposed directly on the client.
Export / Import
# Export devices as CSV
csv_bytes = client.export_devices_csv()
with open("devices.csv", "wb") as f:
f.write(csv_bytes)
# Export specific tags
csv_bytes = client.export_tags_csv(ids=[1, 2, 3])
# Export models as ZIP (includes bindings)
zip_bytes = client.export_models()
# Two-step import: preview then confirm
preview = client.import_tags_preview("tags.csv")
print(f"Will import {preview['result']['total_rows']} rows")
if preview["can_import"]:
result = client.import_tags_confirm(
preview["tmp_storage_name"],
preview["file_name"],
)
print(f"Imported {result['imported_count']} tags")
Model Files
# Upload a model file
result = client.upload_model_file(
"model.onnx",
model_slug="my-model",
version="1.0",
set_active=True,
)
# Download a model file
model_bytes = client.download_model_file("model-file-slug")
# Get model structure (weights stripped for ONNX)
structure = client.get_model_file_structure("model-file-slug")
Backups
# Create a backup
task = client.create_backup(tier="full")
# Poll for completion
import time
while True:
status = client.get_backup_status(task["task_id"])
if status["status"] == "completed":
break
time.sleep(2)
# List and download
backups = client.list_backups()
for backup in backups["backups"]:
print(backup["filename"], backup["size_bytes"])
data = client.download_backup(backups["backups"][0]["filename"])
# Restore from backup
upload = client.upload_restore_file("backup.tar.gz")
task = client.start_restore(upload["restore_file_path"])
Trend Data Export
# Start an export task
task = client.create_trend_export(
tag_ids=[1, 2, 3],
start="2026-01-01T00:00:00Z",
stop="2026-02-01T00:00:00Z",
mode="resampled",
resample_interval="5m",
aggregate_fn="mean",
output_format="csv",
)
# Poll and download
status = client.get_trend_export_status(task["task_id"])
if status["status"] == "completed":
data = client.download_trend_export(status["filename"])
# Estimate before exporting
estimate = client.estimate_trend_export(
tag_ids=[1, 2, 3],
export_all=True,
)
print(f"~{estimate['estimated_size_bytes'] / 1e6:.1f} MB")
Component Libraries
result = client.upload_component_library("my-component.kcl")
print(f"Uploaded: {result['name']} v{result['version']}")
OPC-UA Certificates
# Upload a certificate + key pair
result = client.upload_opcua_certificate(
"cert.der", "key.pem", name="My Certificate"
)
# Download cert or key
cert_bytes = client.download_opcua_certificate("cert-slug")
key_bytes = client.download_opcua_key("cert-slug")
EDS Files
# Upload an EDS file for a device
result = client.upload_eds_file("device.eds")
# Delete the EDS file from a device
client.delete_eds_file(device_id=1)
Log Downloads
log = client.download_device_log("my-device")
log = client.download_model_log("my-model")
log = client.download_scan_group_log("scan-group-1")
log = client.download_component_log("instance-1")
log = client.download_service_log("datacollector")
Connection Options
client = KoiosClient(
hostname="koios.example.com",
client_id="your-uuid",
client_secret="your-secret",
port=443, # Default: 443 (HTTPS) or 80 (HTTP)
ssl=True, # Default: True
verify_ssl=True, # Default: True (set False for self-signed certs)
timeout=30.0, # Default: 30 seconds
)
Error Handling
from koios_client import (
KoiosError, # Base exception
AuthenticationError, # Bad credentials or expired token
NotFoundError, # Resource not found (e.g. client.device(999))
OperationError, # Mutation returned OperationInfo
GraphQLError, # GraphQL response errors
KoiosConnectionError, # Network connectivity issues
KoiosPermissionError, # Insufficient permissions
ValidationError, # Input validation failures
)
try:
device = client.device(1)
device.update(name="New Name")
except NotFoundError:
print("Device not found")
except OperationError as e:
for msg in e.messages:
print(f"{msg.kind}: {msg.field}: {msg.message}")
except AuthenticationError:
print("Check your client credentials")
Development
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
python3 -m pytest tests/ -v
# Regenerate GraphQL client (requires running Koios server)
make codegen
License
Copyright 2024-2026 Ai-OPs, Inc. All rights reserved.
Project details
Release history Release notifications | RSS feed
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 koios_client-1.0.0rc2.tar.gz.
File metadata
- Download URL: koios_client-1.0.0rc2.tar.gz
- Upload date:
- Size: 91.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
752492adbd913eadea3dfe240cbe17adae34b53bba9ae876c600b8f8b35c2f3e
|
|
| MD5 |
6ebdaef990d84215f7f86ca4886a6670
|
|
| BLAKE2b-256 |
aabe1d62d9883708da93c2a94ea65e3c14c37e16c732829202c51f1f596eb61f
|
Provenance
The following attestation bundles were made for koios_client-1.0.0rc2.tar.gz:
Publisher:
release.yml on Ai-Ops-Inc/koios-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
koios_client-1.0.0rc2.tar.gz -
Subject digest:
752492adbd913eadea3dfe240cbe17adae34b53bba9ae876c600b8f8b35c2f3e - Sigstore transparency entry: 1120220416
- Sigstore integration time:
-
Permalink:
Ai-Ops-Inc/koios-client@4f3b1c32d35f676eab62ad096ef491501cc3d642 -
Branch / Tag:
refs/tags/v1.0.0rc2 - Owner: https://github.com/Ai-Ops-Inc
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4f3b1c32d35f676eab62ad096ef491501cc3d642 -
Trigger Event:
push
-
Statement type:
File details
Details for the file koios_client-1.0.0rc2-py3-none-any.whl.
File metadata
- Download URL: koios_client-1.0.0rc2-py3-none-any.whl
- Upload date:
- Size: 140.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
322cc24b76e0180e8a35cc9d67b9d91531fa5c19cbef2ce8598129c5d5799c74
|
|
| MD5 |
5801f3171757af22ddd1bac5f2608956
|
|
| BLAKE2b-256 |
8cc93a332da502b79edcb3c3950d36aca72c6b179f25b8c7d627cb79f2437d31
|
Provenance
The following attestation bundles were made for koios_client-1.0.0rc2-py3-none-any.whl:
Publisher:
release.yml on Ai-Ops-Inc/koios-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
koios_client-1.0.0rc2-py3-none-any.whl -
Subject digest:
322cc24b76e0180e8a35cc9d67b9d91531fa5c19cbef2ce8598129c5d5799c74 - Sigstore transparency entry: 1120220700
- Sigstore integration time:
-
Permalink:
Ai-Ops-Inc/koios-client@4f3b1c32d35f676eab62ad096ef491501cc3d642 -
Branch / Tag:
refs/tags/v1.0.0rc2 - Owner: https://github.com/Ai-Ops-Inc
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4f3b1c32d35f676eab62ad096ef491501cc3d642 -
Trigger Event:
push
-
Statement type: