Skip to content
OAOpenAppPhysical Security as a Service
Login

Async usage

The SDK is async-first. AsyncClient is the canonical client; the synchronous Client is implemented as a facade on top.

from openapp_sdk import AsyncClient
async with await AsyncClient.connect(api_key="...") as client:
status = await client.status.get()

The async with block ensures connections and background tasks are cleaned up. If you manage the lifecycle manually, call await client.aclose() yourself.

All sub-client methods are coroutines; fire them concurrently with asyncio.gather, asyncio.TaskGroup, or a semaphore:

import asyncio
async def warm_cache(client, org_ids):
async with asyncio.TaskGroup() as tg:
for oid in org_ids:
tg.create_task(client.orgs.get(oid))

A single AsyncClient is safe to share across coroutines; the underlying transport uses a connection pool.

Cancelling a coroutine cancels the in-flight request: the underlying HTTP call is aborted, and any retries in progress are cancelled too.

task = asyncio.create_task(client.devices.list())
await asyncio.sleep(0.2)
task.cancel()

Per-call timeouts are not currently part of the sub-client API; configure them globally via Client.connect(request_timeout=...) or wrap individual calls with asyncio.wait_for:

try:
result = await asyncio.wait_for(client.devices.list(), timeout=5.0)
except asyncio.TimeoutError:
...

If your application is synchronous, the top-level Client is a thin wrapper that runs coroutines on a private event loop in a background thread. It has the same sub-client surface:

from openapp_sdk import Client
client = Client.connect(api_key="...")
print(client.status.get())
client.close()

Do not mix Client and AsyncClient in the same process for the same token — they each own a runtime and you’d pay that cost twice.

Jupyter runs on an already-running event loop. Prefer the AsyncClient and await directly:

from openapp_sdk import AsyncClient
client = await AsyncClient.connect(api_key="...")
await client.status.get()

If you must use Client from inside an existing loop, nest_asyncio.apply() is the usual workaround, but we recommend the async variant instead.

The SDK’s Python-facing surface uses stdlib asyncio today. Running it under AnyIO/Trio works for request/response calls (via anyio.from_thread.run) but is not first-class; open an issue if you want deeper support.