Skip to main content

Official Boost.space API SDK (Python)

Project description

boostspace-sdk

Official Boost.space API SDK for Python — fully typed (pydantic v2 + httpx), generated from the OpenAPI spec. Sync and async.

Install

pip install boostspace-sdk

Requires Python ≥ 3.11.

Quickstart (sync)

from boostspace import BoostSpace

# Per-tenant base URL: https://<system>.boost.space/api
with BoostSpace(token="...", system="acme") as bs:
    space = bs.spaces.get(1)             # typed pydantic model

    for s in bs.spaces.list(limit=50):   # transparent pagination
        print(s.name)

    page = bs.spaces.list().page()       # single page + total
    print(page.found_records)

Quickstart (async)

import asyncio
from boostspace import AsyncBoostSpace

async def main() -> None:
    async with AsyncBoostSpace(token="...", system="acme") as bs:
        record = await bs.records.ref(1).get()
        labels = await bs.records.ref(1).labels().all()   # async pagination
        async for r in bs.records.list(limit=50):
            print(r.id)

asyncio.run(main())

OAuth2 / MCP tokens

Besides a static API token, the client accepts a callable token provider, resolved per request. OAuth2Session implements the Boost.space OAuth2 flow (MCP clients included — their tokens are ordinary OAuth2 access tokens) and is itself callable, refreshing proactively before expiry:

from boostspace.runtime import OAuth2Session

session = OAuth2Session("client-id", "client-secret", system="acme",
                        refresh_token=stored_refresh_token)
# or bootstrap from an authorization code:
# session.exchange_code(code, redirect_uri="https://app.example.com/cb")

bs = BoostSpace(session, system="acme")   # token stays fresh automatically

Relationship navigation

creator = bs.records.created_by(record)     # belongsTo → related record (User)
for label in bs.records.labels(record.id):   # hasMany → related collection
    ...

expanded = bs.records.expand(record, ["space", "created_by"])   # eager-load several at once
print(expanded.space.name if expanded.space else None)

Filtering

from boostspace import Filter

page = bs.spaces.list(
    filter=str(
        Filter.equals("color", "#4A90D9")
        .and_(Filter.greater_than_or_equal("id", 3))
        .and_(Filter.any_of(Filter.equals("name", "Acme"), Filter.equals("name", "Globex")))
    )
).page()
# color=#4A90D9;id>=3;(name=Acme|name=Globex)

Operators: eq ne gt gte lt lte like not_like in_ is_null, combined with and_/or_ (OR nested in AND is parenthesised automatically).

Upsert

Create-or-update by a match filter in a single call:

from boostspace import Filter, models

saved = bs.spaces.upsert(
    Filter.equals("name", "VIP"),
    models.Space(name="VIP", color="#ff0000"),
)

upsert lists by the filter, then updates the single match or creates one when none exists. More than one match raises AmbiguousMatchError. Available on resources that expose list + create + update over the same model.

Delta sync

Pull only the records changed since your last sync — pass updated_since (a Unix timestamp) to list, and pair it with list_deleted to learn which records were removed since the same point:

since = "1704067200"                                  # Unix timestamp of your last sync
for record in bs.records.list(updated_since=since):   # streams changed records
    reindex(record)
for deleted_id in bs.records.list_deleted(updated_since=since):
    drop(deleted_id)

Bulk with chunking

Split a large bulk update/delete into chunks and collect per-chunk failures instead of failing all-or-nothing:

report = bs.records.update_chunked(batch, chunk_size=100)
if not report.ok:
    for f in report.failed:  # BulkChunkError(index, size, error)
        print(f.index, f.error)
print(len(report.results))

Resolve a boostId

Map a Boost.space boostId (e.g. "Space5") to its typed record across entities, without knowing which resource it belongs to:

record = bs.resolve("Space5")   # a typed model, or None
if record is not None:
    print(record.boostId)

Returns None for an unknown prefix, a missing record, or a UUID-style id. The result is verified against the input boostId, so it is never a wrong-type record.

Field values

Read a record's Boost.space field values keyed by field name (self-contained from the record — no extra request):

values = bs.records.field_values(record)  # {"Rating": "5", "Tier": "gold"}

Available on resources whose model carries custom field values. Writing field values by name is planned for a later release.

Timeouts & observability

from boostspace import Hooks

bs = BoostSpace(
    token="...", system="acme",
    timeout=10.0,   # per-request timeout in seconds (default 30)
    hooks=Hooks(
        on_request=lambda m, u, h: h.__setitem__("X-Trace-Id", trace_id),
        on_response=lambda m, u, s, ms: metrics.record(s, ms),
    ),
)

Errors

from boostspace import NotFoundError

try:
    bs.spaces.get(999)
except NotFoundError as err:
    print(err.http_status, err.code)  # internal Boost.space error code

Requests retry with jittered backoff on 429/5xx, only for idempotent methods.

Testing without a live API

Inject an httpx client backed by a mock transport to unit-test your code:

import httpx
from boostspace import BoostSpace

def handler(request: httpx.Request) -> httpx.Response:
    return httpx.Response(200, json={"id": 1, "name": "Mock Inc"})

bs = BoostSpace("token", system="acme",
                http_client=httpx.Client(transport=httpx.MockTransport(handler)))
assert bs.spaces.get(1).name == "Mock Inc"  # no network

License

MIT

Guides

In-depth how-to guides live in docs/guides/: filtering · ordering & pagination · authentication · record handles (ref) · relations & expand · delta sync & bulk · errors & retries · testing & mocking

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

boostspace_sdk-0.1.2.tar.gz (92.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

boostspace_sdk-0.1.2-py3-none-any.whl (136.2 kB view details)

Uploaded Python 3

File details

Details for the file boostspace_sdk-0.1.2.tar.gz.

File metadata

  • Download URL: boostspace_sdk-0.1.2.tar.gz
  • Upload date:
  • Size: 92.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for boostspace_sdk-0.1.2.tar.gz
Algorithm Hash digest
SHA256 04b56005d847ca9ed59b5a52f91bada6a79a0eec9072f28c77842a564bc557df
MD5 e462bcf0fb7fc75cd1fb92aa212c11e4
BLAKE2b-256 4c75de294afa6ad712d47abef0ed8fc9321804a355d9dc038ba0aa6394d6cf49

See more details on using hashes here.

Provenance

The following attestation bundles were made for boostspace_sdk-0.1.2.tar.gz:

Publisher: ci.yml on boostspace/sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file boostspace_sdk-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: boostspace_sdk-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 136.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for boostspace_sdk-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 e134188af2c646eec7eb3c15eb30a4de89e56cd5cbed9e68d195ccd664b04773
MD5 70841bffeaa4b5736209dd4a8672704b
BLAKE2b-256 40cf75a647708c7701d256d58590ce57c565ffa603c15c2493c23ad67ee1353e

See more details on using hashes here.

Provenance

The following attestation bundles were made for boostspace_sdk-0.1.2-py3-none-any.whl:

Publisher: ci.yml on boostspace/sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page