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.3.tar.gz (93.5 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.3-py3-none-any.whl (136.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: boostspace_sdk-0.1.3.tar.gz
  • Upload date:
  • Size: 93.5 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.3.tar.gz
Algorithm Hash digest
SHA256 bf984c4af7be9b9a5d6d8684b4cb385e691aa0e04b4ba48c83ec20d462803c9d
MD5 5c6e10431df30d1cf2e85132a3c7c867
BLAKE2b-256 c818b0589bba900ac1743cd81660e4cf84fa874782faaeb15ec521e1e12912b6

See more details on using hashes here.

Provenance

The following attestation bundles were made for boostspace_sdk-0.1.3.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.3-py3-none-any.whl.

File metadata

  • Download URL: boostspace_sdk-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 136.4 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.3-py3-none-any.whl
Algorithm Hash digest
SHA256 da762f5201d04acb54d8e7a1d368f5d49fcf3e28a9753ab4f12bb900c96e6a68
MD5 dd85410d5e69a919b3c18883870193fb
BLAKE2b-256 01ac1ecd387aef833cee957210a834eb97ffa10719fb5db2f0d6438c6215d772

See more details on using hashes here.

Provenance

The following attestation bundles were made for boostspace_sdk-0.1.3-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