Python SDK
A small client you can copy into your backend. This is a documented implementation, not a separately published package. It sends the same API envelope for every service through one call method.
Python 3.10+; standard library only. Async calls run blocking HTTP I/O in a worker thread.
Response contract
| Field | Meaning |
|---|---|
data | Operation result; absent on API errors |
error | API error object, or null/nil on success; message is the stable code |
meta | Response metadata, including pagination when returned |
exec_time | Gateway execution time in seconds, when returned |
API errors return in Result.error. Network, invalid JSON, and transport failures raise an exception or return a Go error. These small clients do not manage sessions, auto-refresh tokens, or retry requests. Pass a current user bearer token to the constructor/client only when acting as that user; omit it for trusted server/agent calls. Keep keys on the server.
Client implementation
Save as singlebase.py. Download the source.
python
import asyncio
import json
from dataclasses import dataclass, field
from typing import Any
from urllib.error import HTTPError
from urllib.request import Request, urlopen
@dataclass
class Result:
data: Any = None
error: dict | None = None
meta: dict = field(default_factory=dict)
exec_time: float | None = None
class singlebaseClient:
def __init__(self, api_key: str, api_url: str, bearer: str | None = None):
self.api_key, self.api_url, self.bearer = api_key, api_url, bearer
async def call(self, operation: str, payload: dict, options: dict | None = None) -> Result:
return await asyncio.to_thread(self._call, operation, payload, options)
def _call(self, operation, payload, options):
headers = {"Content-Type": "application/json", "X-API-KEY": self.api_key}
if self.bearer:
headers["Authorization"] = "Bearer " + self.bearer
body = {"operation": operation, "payload": payload}
if options is not None:
body["options"] = options
request = Request(self.api_url, data=json.dumps(body).encode(), headers=headers, method="POST")
try:
response = urlopen(request, timeout=30)
except HTTPError as exc:
response = exc
with response:
result = json.load(response)
if response.status >= 400 and not result.get("error"):
raise RuntimeError(f"HTTP {response.status} without an API error envelope")
return Result(result.get("data"), result.get("error"), result.get("meta") or {}, result.get("exec_time"))Example
Set SINGLEBASE_API_URL to the complete console endpoint and SINGLEBASE_API_KEY to your server or appropriately scoped agent key. The notes collection must exist.
python
import asyncio
import os
from singlebase import singlebaseClient, Result
async def main():
sbc = singlebaseClient(api_key=os.environ["SINGLEBASE_API_KEY"], api_url=os.environ["SINGLEBASE_API_URL"])
resp: Result = await sbc.call(operation="data.query", payload={"collection": "notes", "limit": 5}, options={})
if resp.error:
raise RuntimeError(resp.error["message"])
print(resp.data, resp.meta, resp.exec_time)
asyncio.run(main())Use the same method with llm.summarize for generation or chat.ask for an answer, with the operation's verified payload. The options argument goes in the request envelope, separate from payload. An empty options object is valid; omit it when unused. See API overview for request access and errors, and API reference for the operation groups.