bachs is the official Bachs library for Python servers. It sends authenticated requests, checks the responses, retries reads safely, and verifies webhook signatures. It has a sync client, Bachs, and an async client, AsyncBachs, with the same methods.
It runs on your server only. Keep your secret API key out of browser and mobile code.
Install
You need Python 3.10 or later.httpx and ships type hints for mypy and Pyright.
Create a client
Create one client and reuse it. Pass your secret key and the environment it belongs to.environment is required. "sandbox" sends requests to https://sandbox-api.bachs.io and "production" sends them to https://api.bachs.io. Use a sandbox key with "sandbox" and a production key with "production". See Authentication.
Creating a client sends no request, so a wrong key shows up on your first call.
To release connections when you are done, use the client as a context manager (
with Bachs(...) as bachs: or async with AsyncBachs(...) as bachs:), or call bachs.close() or await bachs.aclose().
Send requests
Each resource on the client maps to a group of API endpoints. Methods follow one pattern:- Create:
create(params, *, idempotency_key=None, timeout=None) - Retrieve:
get(id, *, timeout=None, max_retries=None) - List:
list(params=None, *, timeout=None, max_retries=None)
AsyncBachs, every method is a coroutine: await bachs.products.create(...).
Requests and responses are dictionaries with the same field names as the API, in snake_case, so the API reference describes them exactly. Money amounts are decimal strings, such as "12.00", never numbers. Leaving a key out and sending None are different, as in the API.
Resources
subscriptions.cancel(id, params=None, ...) takes {"cancel_at_period_end": True} to cancel at the end of the paid period, or False to cancel now. See Manage subscriptions.
Not in the SDK yet
Use the API directly for these. You can call it alongside the SDK with the same key.- Connect: connected accounts, onboarding, the
X-Account-Idheader and split payments - Customers, refunds, disputes and conversions
- Updating, archiving or unarchiving products, and changing a subscription’s seats or plan
- Virtual accounts, uploads, product groups and account settings
Lists and pagination
List methods return one page, in the same shape as the API response. The SDK does not fetch further pages for you. Products page by cursor. Passpagination["next_cursor"] as cursor to get the next page:
limit and offset. Payout destinations are in destinations; the others are in items.
Request options
Every method takes these as keyword arguments after its other arguments.
To cancel an
AsyncBachs call, cancel its task, for example with asyncio.timeout() or when your framework drops a disconnected request. The cancellation exception reaches your code as usual. Cancelling stops waiting; it cannot undo a write Bachs already received.
Retries and uncertain writes
Reads are retried up tomax_retries times after a network failure or an HTTP 429, 500, 502, 503 or 504. The SDK waits about half a second, then a second, and follows any Retry-After header. It stops when the next wait would pass timeout.
Writes are sent once, even with an idempotency key. A write can reach Bachs and still fail on the way back, for example when the connection drops or the API returns a 5xx. In those cases the error has outcome_unknown set to True: the payment, payout or product may exist.
When a write’s outcome is unknown, do not send it again with a new key. Look it up first, for example in your payout history or by retrieving the checkout session. If it does not exist, repeat the write with the same idempotency key and the same request.
AsyncBachs stops exactly at timeout. The sync client can run past it in one case: when the server sends part of a response and then stops, the last network read waits for its own full timeout, so a call can take up to about twice timeout. If you need a hard limit, use AsyncBachs or set timeout to half of your limit.Errors
Every SDK error extendsBachsError.
error_code is the error_code from the response body, and field_errors lists any problems with individual fields. See Errors. Include request_id when you contact support.
Error bodies and customer portal URLs can contain personal data or credentials. Do not log them in full.
Verify webhooks
webhooks.construct_event checks the signature on a delivery and returns the event. Pass the raw request body, before any JSON parsing, the request headers, and your endpoint’s signing secret. The signing secret is not your API key.
X-Bachs-Signature-V2, accepts any matching signature while you rotate a secret, and rejects deliveries more than 300 seconds old or ahead. Change the window with tolerance=, in seconds. You can call it without a client, or as bachs.webhooks.construct_event.
Flask support for
request.headers needs bachs 1.0.1 or later. On 1.0.0, pass dict(request.headers).5xx when handling fails, so Bachs retries. See Webhooks.
Types
Request and response types areTypedDicts in bachs.types:
Source code
The SDK is open source under the MIT license.- GitHub repository: source, issues and the full README
- PyPI package
- Changelog

