Skip to main content

bachs-io — Python SDK for the Bachs API

Production-grade Python SDK for the Bachs payments & billing platform — the payments stack for African internet businesses. Define products, run hosted checkouts, collect payments in customers' local currencies, manage subscriptions and trials, issue refunds, respond to disputes, convert balances, withdraw funds, configure webhooks, and run a Connect-style platform with connected accounts, transfers, and split payments.

Generated from the official Bachs OpenAPI spec (v1.0.0, 96 operations).

Installation

pip install bachs-io

Python 3.9+ required. Depends on httpx and pydantic>=2.

Quickstart

import bachs

client = bachs.BachsClient(api_key="sk_sandbox_...")

# Product-based hosted checkout
session = client.checkouts.create_checkout_session(
    customer={"email": "jane@example.com", "name": "Jane Doe"},
    product_cart=[{"product_id": "prod_abc", "quantity": 1}],
    success_url="https://shop.example.com/success",
)
print(session.checkout_url)

# Retrieve a payment
payment = client.payments.get_payment_detail("pay_123")

client.close()

Authentication & environments

Every request is authenticated with Authorization: Bearer sk_....

  • sk_sandbox_... keys automatically target https://sandbox-api.bachs.io
  • sk_live_... keys automatically target https://api.bachs.io
  • Override with base_url=... (useful for mocking/staging)

You can also set the BACHS_API_KEY environment variable and omit api_key.

The API surface

Methods are grouped into typed resources on the client:

Resource Highlights
client.payments list/retrieve payments, charge status, payment methods & rails, supported currencies
client.checkouts create/retrieve checkout sessions and API checkouts
client.customers create/update/retrieve customers, customer portal sessions
client.products create/update/list products, pricing, archive/unarchive
client.product_groups bundles of products for multi-plan checkout
client.uploads upload/retrieve/delete product media
client.subscriptions list/retrieve/update/cancel subscriptions
client.refunds create/list/retrieve refunds
client.disputes list/get disputes, evidence upload/update/submit
client.accounts organization balances
client.organizations your organization, checkout settings, connected accounts
client.connected_accounts Connect-style account lifecycle, Tasks/checklist, account links, uploads
client.transfers move funds between platform and connected accounts
client.conversions quote and execute currency conversions
client.payouts destinations, quotes, bank lookup, withdrawals
client.webhooks endpoints, secrets, events, metrics, replay

Every method is fully typed — parameters map 1:1 to the API request body/query, and every response is validated into a typed model (see bachs.models, also exported from the package root). Unknown fields in API responses are preserved rather than dropped, so the SDK tolerates additive API changes.

Conventions

  • Money is always a decimal string (e.g. "29.00") at the currency's precision — the SDK never converts between units.
  • Timestamps are ISO 8601 UTC strings.
  • IDs (cust_, prod_, sub_, chk_, evt_, ...) are opaque strings.

Sending null to clear a field

Request models are serialized with None fields omitted. If you need to send an explicit null (e.g. clearing billing_address on update_customer), pass an empty representation for that field after reviewing the API docs for the endpoint's accepted values.

Pagination

List endpoints return a typed response carrying items (or items + a pagination block). Use the limit/offset (or cursor) parameters and read pagination.next_cursor / pagination.has_more to walk pages:

page = client.payments.list_payments(limit=100)
while True:
    for payment in page.items:
        ...
    if not page.pagination.has_more:
        break
    page = client.payments.list_payments(limit=100, offset=page.pagination.offset + page.pagination.returned)

Error handling

Every failure is raised as a subclass of bachs.BachsError:

try:
    client.refunds.create_refund(charge_id="pay_1", reference="ref_1")
except bachs.NotFoundError as exc:
    print(exc.error_code, exc.detail, exc.doc_url)
except bachs.RateLimitError as exc:
    print(exc.retry_after)  # seconds, from Retry-After
except bachs.ApiServerError as exc:
    pass  # safe to retry
Exception HTTP
BadRequestError 400
AuthenticationError 401
ForbiddenError 403
NotFoundError 404
ConflictError 409
UnprocessableEntityError 422
PreconditionRequiredError 428
RateLimitError 429 (has .retry_after)
ApiServerError 5xx
APIConnectionError transport-level (DNS/timeouts)

Errors carry status_code, error_code, detail, doc_url, and errors (field-level validation details on 400).

Idempotency

All mutating requests accept an idempotency_key= argument that is sent as the Idempotency-Key header. Reuse the same key across retries to avoid duplicate creates.

Retries

429 and 5xx responses are retried automatically (up to max_retries=2, with exponential backoff and Retry-After honoured for rate limits). Configure via BachsClient(max_retries=..., retry_on_429=...).

Webhook verification

Webhooks are the source of truth for fulfilment. Verify every delivery with the endpoint's signing secret:

from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

@app.post("/webhooks")
async def receive(request: Request):
    raw = await request.body()
    try:
        event = bachs.construct_event(
            raw,
            request.headers.get("X-Bachs-Signature", ""),
            "whsec_...",
            timestamp_header=request.headers.get("X-Bachs-Timestamp"),
        )
    except bachs.SignatureVerificationError:
        raise HTTPException(400, "Bad signature")
    if event.event_type == "collection.succeeded":
        ...  # fulfil the order
    return {"status": "ok"}

verify_header returns a bool; construct_event parses and validates in one step. generate_signature is provided for testing.

Async

AsyncBachsClient mirrors the sync client:

async with bachs.AsyncBachsClient(api_key="sk_sandbox_...") as client:
    session = await client.checkouts.create_checkout_session(
        customer={"email": "jane@example.com", "name": "Jane Doe"},
        product_cart=[{"product_id": "prod_abc"}],
    )

Connect & acting on behalf of accounts

Endpoints that support X-Connected-Account-ID (checkout settings, transfers) expose a connected_account_id= parameter:

client.organizations.get_checkout_settings(connected_account_id="org_ca")
client.transfers.create_transfer(
    destination="org_ca", amount="7000.00", currency="NGN",
    connected_account_id="org_ca",
)

Uploads

Upload endpoints accept a file path, bytes, a file-like object, or an httpx (filename, content, content_type) tuple:

upload = client.uploads.create_upload(file="./cover.jpg", scope="product-media")
client.products.create_product(name="Pro", price={"currency": "USD", "amount": "29.00"},
                               media=[upload.upload_id])

Development

python -m venv .venv && .venv/bin/pip install -e .
.venv/bin/python -m pytest

The typed models and resource clients are generated from the Bachs OpenAPI spec (docs.bachs.io/docs/openapi/openapi.json) and checked in under bachs/_models.py, bachs/_resources.py, and bachs/_async_resources.py.

Release files for bachs-io 0.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bachs-io 0.0.3
File Size Uploaded
bachs_io-0.0.3.tar.gz 59.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bachs-io 0.0.3
File Interpreter ABI Platform
bachs_io-0.0.3-py3-none-any.whl Python 3 none any Details

Total release size: 114.3 kB

Release files / bachs_io-0.0.3.tar.gz

Download URL bachs_io-0.0.3.tar.gz
Size 59.1 kB
Tags Source
SHA-256 checksum
How to use checksums
06ad6e15d487fb069724ffc88da3e78592d5689eeb2e0792fd506d7ae71663c9
BLAKE2b-256 checksum
How to use checksums
a50b8662f5bd2abf68f244a94b8aa8993068813b38473b1cce5e36dae608a7f2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.2

Release files / bachs_io-0.0.3-py3-none-any.whl

Download URL bachs_io-0.0.3-py3-none-any.whl
Size 55.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
779ab814ff8484bd825e4c288a8b15595c54694b89b17e8a97e8b91ad114bed7
BLAKE2b-256 checksum
How to use checksums
7358b61b3d14b8e966547d3561d2455074483843f5a6d1fa07aa09861fdfaaa2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.2

Release history Release notifications | RSS feed

This release

0.0.3 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page