Skip to content

Home

The pyc8y (Cumulocity Python SDK) module is a pure Python implementation of Cumulocity IoT's REST interface based on asyncio.

→ Getting started with the Cumulocity Python SDK

REST API Connector

The SDK module provides a convenience wrapper around the standard Cumulocity REST API (see also the OpenAPI documentation).

The CumulocityRestClient class provides the fundamental wrapping around authentication and basic get, post, put, delete commands. The CumulocityClient class is your entrypoint into higher level funct ions, grouped by contexts like inventory, users, and measurements. Each of these contexts is documented in detail within the main-api-classes section.

The DeviceRegistryClient class provides an additional entry point for devices, wrapping the entire bootstrap mechanism. See also the Device integration documentation at Cumulocity.

Application Helpers

The SDK is designed to be particularly useful for developing Cumulocity microservices. For this, the module provides two helper classes that take care of microservice specific authentication.

The SimpleCumulocityApp class should be used for single tenant microservices. It automatically reads the microservice's environment to determines the microservice access credentials.

The MultiTenantCumulocityApp class should be used for multi-tenant microservices which need to handle requests for arbitrary Cumulocity tenants. It reads the microservice's environment to determine the necessary bootstrap credentials and provides additional functions to dynamically obtain CumulocityClient instances for specific tenants.

For interactive use (e.g. in Jupyter Notebooks) the get_client function can be used to obtain a ready to use CumulocityClient instance. It honors from standard Cumulocity environment variables for not explicity provided connection details, and falls back to interactive prompts for anything still missing.

Main API Classes

The pyc8y module's main API classes provide access to various contexts within the Cumulocity REST API. Use it to read existing data or modify in bulk.

See also the Object model section for object creation and object-oriented access in general.

Querying the database

The main API classes can be used to read individual objects by their respective key, usually the Cumulocity object ID:

some_object = await c8y.inventory.get("123456")
some_user = await c8y.users.get("mini_me")
some_id = await c8y.identity.get("9876543210", "c8y_Serial")

To access entire collections most APIs provide convenient query functions which reflect the respective API's capabilities:

objects = await c8y.inventory.get_all(name="My*", limit=10)

The get_all function provides the query results as a list, the feature-equivalent select function works as a generator for streaming access. Both functions perform automatic paging if necessary.

The returned objects are instances of the Object model and the functions are properly typed, so the results can conveniently used directly as-is:

async for event in c8y.events.select(source="123456", max_age="1d", limit=100):
    print(f"Event: #{event.id} {event.time}, Type {event.type}")

Flexible query parameters

While all query functions are functionally equivalent, each API provides a specialized version which reflects the respective REST API's capabilities. For example, the Inventory API provides OData-like queries which for example are not supported for the Event API or Operation API.

objects = await c8y.inventory.get_all(query="name eq 'My*'", limit=10)

All potential filters available through code completion. They match the REST API's naming and function - however, the names have been renamed to match Python's snake_case naming convention (e.g. date_from instead of dateFrom). The original names can always be used as a fallback, though. Additionally, convenient alternatives are provided for date-like query parameters:

c8y.events.get_all(source="123456", dateFrom="2025-01-01")  # original
c8y.events.get_all(source="123456", date_from="2025-01-01")  # Pythonic
c8y.events.get_all(source="123456", after="2025-01-01")  # alternative
c8y.events.get_all(source="123456", max_age="1d")  # convenience helper
All date-like parameters accept timezone-aware datetime objects, ISO date/time strings as well as "now" as a convenience alternative.

As a fallback, all query functions support the direct definition of a REST API expression:

# knowing it better
c8y.inventory.get_all("q=$filter=name eq 'M01' $orderby=lastUpdated")

High-performance throughput

The SDK features an async first design with IO concurrency and high throughput in mind. Hence, all query functions can be executed in concurrent mode via the workers parameter:

all_devices = await c8y.device_inventory.get_all(limit=None, page_size=100, workers=20)

This will internally fetch pages concurrently and return the complete set. Results will be returned in random order (by completion), unless an ordering parameter is specified:

all_events = await c8y.events.get_all(
    source="12345",
    ascending=True,  # or: revert=True
    limit=None,
    page_size=100,
    workers=20)

Especially when working on large data sets, the full result object's JSON is usually obsolete. To directly forfeit the JSON data and only process relevant fields, the as_values parameter can be used on all query functions:

async for values in c8y.inventory.select(
        type="MyDevice",
        as_values=("id", ("name", "undefined"), "lastUpdated")):
    print(f"#{values[0]} '{values[1]}', last updated: {values[2]}")

Object manipulation and bulk processing

The main API classes can also be used to directly manipulate the database without the need to pull object instances:

# add a new fragment to a known set of inventory objects:
new_fragment_json = {"my_AdditionalFragment": {"essential": True}}
await c8y.inventory.update(new_fragment_json, "123456", "1238957", "12399192")

Similar functions exist to create and delete objects in bulk. Each API class additionally provides access to API specific functionality, for example to assign group memberships, define child devices, reset user passwords, ...

Like the query functions, all bulk operations feature a workers parameter to enable concurrency and maximize throughput:

many_ids = [...]
await c8y.inventory.delete(*many_ids, workers=20)

Because all these functions are defined as async, the bulk operations can easily be combined using standard asyncio means:

# clean up my data
object_events = await c8y.events.get_all(source=my_object.id, limit=None, workers=20, as_values="id")
object_alarms = await c8y.alarms.get_all(source=my_object.id, limit=None, workers=20, as_values="id")
await asyncio.gather(
    c8y.events.delete( *object_events, workers=20),
    c8y.alarms.delete(*object_alarms, workers=20),
)
await c8y.inventory.delete(my_object.id)

→ Inventory
→ DeviceInventory
→ Identity
→ Binaries
→ Measurements
→ Events
→ Alarms
→ Users
→ UserGroups
→ InventoryRoles
→ Subscriptions
→ Tokens
→ Operations
→ BulkOperations
→ Applications
→ TenantOptions
→ AuditRecords

Object Models

The Cumulocity Python SDK's object model provides object-oriented access to the Cumulocity REST API. Use it to create and modify single objects within the Database.

Object-oriented database access

The object model define functions for direct, object-oriented database access:

# Create an object in the database and use it
obj = ManagedObject(
    connection,
    name="MyObject",
    type="my_CustomType"
)
obj = await obj.create()

# Update the object and write changes to the database
obj.type = "my_ChangedType"  
await obj.update()

# Remove the previously created object from the database
await obj.delete()

Note: These objects can also be used directly within the Main API classes to modify data in bulk.

Direct JSON data access

The object model instances are essentially enriched JSON-like objects and can be used like standard nested dictionaries:

the_value = obj["my_Fragment"]["value"]  # read nested value
obj["my_Fragment"]["value"] = new_value  # set nested value

For convenience the SDK supports dot-notation for the [] operator:

the_value = obj["my_Fragment.value"]  # read nested value
obj["my_Fragment.value"] = new_Value  # set nested value

Likewise, the .get and .set functions:

the_value = obj.get("my_Fragment.value", default=42)  # read nested value
obj.set("my_Fragment.value", 42)  # set a nested value

which also protect against missing keys along the path.

Many Cumulocity objects define standard properties, e.g. id, type, name and creationTime. These fields can additionally also read (and set if allowed) through explicit class properties:

# Most objects have an id, name, and type
print(f"Object details: #{obj.id}, '{obj.name}' ({obj.type})")

# Date-like properties can be read as strings (actual JSON value) or datetime (parsed)
total_minutes = int((datetime.now(timezone.utc) - obj.creation_datetime).total_seconds() / 60)
print(f"Object creation: {obj.creation_time} ({total_minutes} minutes ago)")

These properties use Pythonic snake_case naming to integrate nicely into Python code. The original data is always available through direct access, e.g. obj["creationTime"].

→ ManagedObject
→ Device
→ DeviceGroup
→ ExternalId
→ Binary
→ Measurement
→ Event
→ Alarm
→ Series
→ Subscription
→ Availability
→ User
→ CurrentUser
→ TfaSettings
→ UserGroup
→ Permission
→ ReadPermission
→ WritePermission
→ AnyPermission
→ Operation
→ BulkOperation
→ Application
→ ApplicationSubscription
→ TenantOption
→ AuditRecord
→ Change

Measurement Additions

The Cumulocity Python SDK's measurements API (see also classes Measurements and Measurement) includes the following additions to allow easy creation of standard measurement values including units.

Effectively, each of the value classes represent a value fragment, e.g. Celsius:

{"unit": "°C", "value": 22.8}

These values can easily be combined, e.g. when constructing a measurement:

m = Measurement(
    type='cx_LevelMeasurement', source=device_id, time='now',
    cx_Levels={
        'oil': Liters(8.4),
        'gas': Liters(223.18),
        'h2o': Liters(1.2),
        'bat': Percentage(85)
    })

→ Units
→ Celsius
→ Centimeters
→ Count
→ CubicMeters
→ Grams
→ Kelvin
→ Kilograms
→ Liters
→ Meters
→ Percent
→ Value