Appearance
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-sdkPython ≥ 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 this4. 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 included6. 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:
raiseThe 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.