Skip to content

Quickstart — Python

Connect your first bank account in sandbox mode with budgetbakers-partner-sdk. You need a partner account in the partner portal — registration provisions sandbox access instantly, no card required.

1. Install

sh
pip install budgetbakers-partner-sdk

Python ≥ 3.10; one dependency (httpx); import name budgetbakers_partner_sdk. Until the PyPI package is published, install the wheel from the downloads page instead: pip install ./budgetbakers_partner_sdk-0.1.0-py3-none-any.whl.

2. Create the client

Your API key selects the mode: bb_test_… keys run sandbox (fictional *_xf banks), bb_live_… keys run live. Keys are server-side only.

python
from budgetbakers_partner_sdk import BudgetBakers

bb = BudgetBakers(api_key="{{BB_API_KEY}}")

config = bb.partner.get_config()
print(config["mode"])  # "sandbox"

3. Create an end-user client

A client is one of your end users — bank connections, accounts and transactions all belong to a client, and the X-Client-Id scoping keeps each user's data isolated. Pass your own user id as externalId and the call becomes an upsert — safe to call on every login, no duplicates, no need to store a BudgetBakers-id mapping on your side:

python
client = bb.clients.create(externalId="user-42")
scope = bb.client(client["id"])  # all user-scoped calls go through this

4. Start the hosted connect flow

Create a connect session and send the user to hostedUrl (web redirect, or the mobile Link SDKs — see the iOS/Android quickstarts).

The returnUrl works like an OAuth redirect URI: the portal (Apps → return URLs) holds your allowlist of patterns — one per platform (web URL, iOS universal link, Android app link, custom scheme) — and each session passes the concrete URL for where this user should land. It must match a registered pattern, otherwise the create fails with validation_error.

python
session = scope.connect_sessions.create("https://app.example.com/bb-callback")
redirect_user_to(session["hostedUrl"])

The redirect back to your returnUrl is a UX signal — the authoritative result is the session state:

python
done = scope.connect_sessions.wait_for_terminal(session["sessionId"])
if done["state"] == "Completed":
    print("connected:", done["connectionId"])

5. Receive webhooks

Configure your webhook URL in the portal (per mode); every delivery is signed. See the webhook verification guide for the full contract.

python
from budgetbakers_partner_sdk import parse_event, verify

@app.post("/webhooks/bb")
def bb_webhook(request):
    result = verify(
        ["{{BB_WEBHOOK_SECRET}}"],              # ALL active secrets (two during rotation)
        request.headers.get("X-BB-Signature", ""),
        request.get_data(),                     # the RAW body bytes
    )
    if result != "valid":
        return "", 401

    event = parse_event(request.get_data())
    if event["kind"] == "event" and event["type"] == "ConnectionCreateSuccess":
        ...  # fetch data, update your records
    return "", 200  # always 2xx for verified deliveries — unknown types included

6. Read accounts and transactions

python
accounts = scope.connections.list_accounts(connection_id)

for tx in scope.accounts.transactions(accounts[0]["id"]):
    # tx["amount"] is a 2-dp decimal.Decimal — money is never a float.
    # Only "id" is guaranteed non-null; treat every other field as nullable.
    ...

Error handling

Branch on error.code only — never parse messages (full code reference):

python
from budgetbakers_partner_sdk import PartnerApiError

try:
    scope.connections.refresh(connection_id)
except PartnerApiError as err:
    if err.code == "refresh_cooldown":
        print("retry after", err.next_refresh_possible_at)
    else:
        raise

The SDK already retries 429/5xx with exponential backoff (honoring Retry-After) and auto-generates Idempotency-Key on creates.

Going live

Complete the go-live checklist in the portal (card verification, business check, terms) — production unlocks automatically and you can mint a bb_live_… key. The code above works unchanged; only the key differs.