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:
    contact = bs.contact.get(1)          # typed pydantic model

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

    page = bs.contact.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:
        contact = await bs.contact.get(1)
        labels = await bs.contact.labels(contact).all()   # async pagination
        async for c in bs.contact.list(limit=50):
            print(c.name)

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.contact.created_by(contact)   # belongsTo → related record (User)
for label in bs.contact.labels(contact):    # hasMany → related collection
    ...

nav = bs.contact.of(contact)                        # group an entity's relations
expanded = bs.contact.expand(contact, ["group"])    # eager-load several at once
print(expanded.group.name if expanded.group else None)

Filtering

from boostspace import Filter

page = bs.contact.list(
    filter=str(
        Filter.equals("status", "active")
        .and_(Filter.greater_than_or_equal("created", "2024-01-01"))
        .and_(Filter.any_of(Filter.equals("type", "company"), Filter.equals("type", "person")))
    )
).page()
# status=active;created>=2024-01-01;(type=company|type=person)

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.category.upsert(
    Filter.equals("name", "VIP"),
    models.Category(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

Iterate only the records changed since a point in time (updated >= since), across all pages:

from datetime import datetime

for contact in bs.contact.iter_changes(datetime(2024, 1, 1)):
    ...  # only contacts updated at/after 2024-01-01

Available on resources whose model has an updated timestamp.

Attach / detach relations

Link or unlink a related record (e.g. a label) by id:

bs.note.attach_label(note, label_id)
bs.note.detach_label(note, label_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.contact.update_chunked(contacts, 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. "Contact07") to its typed record across entities, without knowing which resource it belongs to:

record = bs.resolve("Contact07")   # 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.contact.field_values(contact)  # {"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.contact.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", "spaces": [1]})

bs = BoostSpace("token", system="acme",
                http_client=httpx.Client(transport=httpx.MockTransport(handler)))
assert bs.contact.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.1.tar.gz (89.1 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.1-py3-none-any.whl (137.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: boostspace_sdk-0.1.1.tar.gz
  • Upload date:
  • Size: 89.1 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.1.tar.gz
Algorithm Hash digest
SHA256 427c5bca985036829c4c0e34da1978e8e16a457d1349ee0218632de543e5c345
MD5 9f34c58205a602c08902573cd903e297
BLAKE2b-256 f606d7dcde5060f3eb288896a39420965f3cf47380dd663ae93fea8fef6e9ac8

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: boostspace_sdk-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 137.0 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 79d799b7ae42b1f0fcc09669f89f25a1e0d8162dd690e13d685952e584d30f9a
MD5 2f24841e44d348df184adba6f5d3a67a
BLAKE2b-256 4174579537db810358309fdfb26239289f345a7ea528d9117b1fcdae22057e52

See more details on using hashes here.

Provenance

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