Install
There is deliberately no install command on this page: printing one that resolves to v1 would be worse than printing none. This page updates automatically when publication evidence is recorded.
Authenticate
Construct the client with your API key. It exchanges the key for a short-lived access token automatically. Organization and account arguments are unnecessary; collection methods resolve their workspace on the server. See Authentication for personal-key limits and current access.
Quickstart
"""The Python quickstart, typechecked against the generated client.
apps/docs/scripts/check-examples.ts runs mypy over this file against a freshly
generated SDK, so a contract change that invalidates the documented calls fails
the docs build instead of shipping a snippet that cannot run.
"""
import asyncio
import os
from nebula import NebulaClient, models
async def main() -> None:
client = NebulaClient(
api_key=os.environ["NEBULA_API_KEY"],
)
workspace_id = os.environ["NEBULA_WORKSPACE"]
async with client:
# 1. Create a collection. `collection_id` is omitted, so the client
# generates a UUID; pass your own to make a retry idempotent.
collection = await client.collections.create(
workspace_id=workspace_id,
body={"name": "Notes"},
)
# The response models `collection_id` as a UUID while the resource
# methods take a str, so it is converted once here. (TypeScript types
# the same field as a string -- an SDK asymmetry, not a docs choice.)
collection_id = str(collection.collection_id)
# 2. Store a memory. Returns an operation, not the finished memory.
accepted = await client.memory.store(
collection_id=collection_id,
body={
"content": {"kind": "text", "text": "We batch exports nightly."},
"metadata": {"source": "meeting-notes"},
},
)
# 3. Wait until search can observe it: `search_ready`, not `state`.
# `state` is an enum, so compare against models.OperationState
# rather than a string literal. Stop on `cancelled` as well as
# `failed`: both are terminal and neither becomes searchable, so
# waiting only for `failed` polls forever.
operation_id = str(accepted.operation_id)
operation = await client.operations.get(
collection_id=collection_id,
operation_id=operation_id,
)
terminal = {models.OperationState.failed, models.OperationState.cancelled}
while not operation.search_ready and operation.state not in terminal:
await asyncio.sleep(0.5)
operation = await client.operations.get(
collection_id=collection_id,
operation_id=operation_id,
)
if not operation.search_ready:
raise RuntimeError(f"operation {operation_id} ended {operation.state}")
# 4. Search.
results = await client.memory.search(
collection_id=collection_id,
body={"query": "how do we handle exports"},
)
for hit in results.data:
print(f"{hit.relevance:.2f} {hit.summary} - {hit.citation.label}")
asyncio.run(main())The file above is typechecked against a freshly generated client by
pnpm --dir apps/docs check-examples, so it cannot drift from the contract.
What it covers
The client is generated from the same OpenAPI contract this site documents, so every operation in the API reference is available with the same field names.
Errors surface as typed exceptions carrying the machine-readable code from
error codes; authentication failures carry a
problem code instead.