Skip to content

Python SDK

The novavms-sdk package wraps the NovaVMS REST API (v1) with type-hinted Python clients. It supports CPython 3.10+ and ships both a synchronous NovaVMSClient and an asyncio AsyncNovaVMSClient with identical surface areas.

Install

Terminal window
pip install novavms-sdk

Since v1.0. Requires httpx>=0.27 and pydantic>=2.5 (installed automatically).

Initialize

from novavms import NovaVMSClient
client = NovaVMSClient(
api_key="sk_live_abc123def456",
base_url="https://api.novavms.io",
)

For asyncio applications:

import asyncio
from novavms import AsyncNovaVMSClient
async def main() -> None:
async with AsyncNovaVMSClient(api_key="sk_live_abc123def456") as client:
cameras = await client.cameras.list()
for camera in cameras.data:
print(camera.name, camera.status)
asyncio.run(main())

Client options

OptionTypeDefaultSinceDescription
api_keystrv1.0Org-scoped API key. Required.
base_urlstrhttps://api.novavms.iov1.0Override for self-hosted deployments.
timeoutfloat10.0v1.0Per-request timeout in seconds.
max_retriesint3v1.0Retries on 429 and 5xx with exponential backoff.
http_clienthttpx.Client | Noneautov1.0Inject a pre-configured httpx.Client (for proxies, transports).
user_agentstrnovavms-sdk/<version>v1.0Appended to the default User-Agent.

Available methods

The client exposes one namespace per top-level resource:

NamespacePurpose
client.camerasList, get, create, update, delete cameras; fetch snapshots and live-view tokens.
client.eventsQuery the event feed, fetch a single event, acknowledge, star, add notes.
client.alertsList alert history, acknowledge alerts, change status.
client.rulesCreate, read, update, delete alert rules; enable/disable.
client.sitesCreate, read, update, delete sites; manage site-scoped user access.
client.usersInvite users, list members, change roles, revoke sessions.
client.webhooksCreate, list, update, delete webhook definitions; test delivery. Secret rotation requires the Admin role.

Each namespace exposes list, get, create, update, remove, plus resource-specific verbs (for example cameras.trigger(), events.acknowledge()).

cameras = client.cameras.list(site_id="8f2b3a71-0c4e-4b5f-9d1a-2e7c3a4b5d6f")
event = client.events.get("3c4d5e6f-7a8b-9c0d-1e2f-3a4b5c6d7e8f")
client.rules.update("9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d", enabled=False)

Error types

Errors use a standard Python exception hierarchy rooted at NovaVMSError.

ClassRaised whenSince
NovaVMSErrorAny API error. Has status, code, request_id.v1.0
AuthError401 or 403 — invalid key or missing scope.v1.0
RateLimitError429 — exposes retry_after (seconds, float).v1.0
ValidationError400 — field_errors: dict[str, list[str]].v1.0
NotFoundError404 — resource does not exist in this org.v1.0
import time
from novavms import NovaVMSClient
from novavms.errors import AuthError, RateLimitError, NovaVMSError
client = NovaVMSClient(api_key="sk_live_abc123def456")
try:
cameras = client.cameras.list()
except RateLimitError as err:
time.sleep(err.retry_after)
except AuthError as err:
raise RuntimeError("API key is invalid or revoked") from err
except NovaVMSError as err:
print(f"NovaVMS API error {err.request_id} {err.code}: {err.message}")
raise

Typed responses

Every response is a Pydantic model generated from the OpenAPI spec shipped at /api/v1/openapi.yaml. An SDK version always matches an API version — no hand-written types drift.

from novavms.models import Camera, Event, AlertRule
def render(camera: Camera) -> str:
return f"{camera.name} ({camera.status})"

For the full list of types, see /developer/api/.

Pagination

List endpoints use opaque cursors. The SDK exposes a built-in iterator that handles the cursor walk for you:

for camera in client.cameras.iter():
process(camera)

iter() returns a generator. It fetches the next page only when the current one is exhausted, and stops when the next cursor is None. Since v1.0.

For manual control:

cursor: str | None = None
while True:
page = client.events.list(cursor=cursor, limit=100)
for event in page.data:
process(event)
cursor = page.next_cursor
if cursor is None:
break

The async client exposes async for on aiter():

async for event in client.events.aiter(limit=100):
await process(event)