Skip to main content

BillKit Python SDK

Async + sync client for BillKit, the Stripe-Billing-shape multi-tenant SaaS API on Mollie.

Install

pip install billkit-eu
# or
uv add billkit-eu

Requires Python 3.11+. The distribution is billkit-eu because the bare billkit name on PyPI belongs to an unrelated project. The import name is unaffected:

import billkit

Quick start

from billkit import BillKit

client = BillKit(api_key="sk_test_...")

customer = client.customers.create(email="ada@example.com", name="Ada Lovelace")
product = client.products.create(
    name="Pro",
    description="Hosted billing for SaaS",
    marketing_features=["Checkout", "Subscriptions"],
)
price = client.prices.create(
    product_id=product["id"],
    amount_cents=999,
    currency="EUR",
    interval="month",
    trial_days=14,
    payment_methods=["creditcard", "directdebit"],
)
session = client.checkout_sessions.create(
    customer_id=customer["id"],
    price_id=price["id"],
    success_url="https://app.example.com/success",
    cancel_url="https://app.example.com/cancel",
)
print(session["url"])  # redirect the user here

One-shot (mandate-less) payments

A one-shot is a single charge with no subscription, mandate or renewals: the Stripe PaymentIntent shape, mapped onto Mollie. Create it, redirect to redirect_url, and settle terminal state via the one_shot_payment.succeeded / .failed webhook events.

payment = client.one_shot_payments.create(
    customer_id=customer["id"],
    amount_cents=2500,
    currency="EUR",
    method="ideal",
    success_url="https://shop.example.com/thanks",
    refund_window_days=14,  # optional; 0 disables refunds, default is 30
)
print(payment["redirect_url"])  # redirect the payer here

# Later, refund it within its window (omit amount_cents for a full refund):
client.refunds.create(one_shot_payment_id=payment["id"])
# ...or refund part of it. A charge can carry several partials:
client.refunds.create(one_shot_payment_id=payment["id"], amount_cents=500)

Async

from billkit import AsyncBillKit

async with AsyncBillKit(api_key="sk_test_...") as client:
    customer = await client.customers.create(email="ada@example.com")

Configuration

from billkit import BillKit, RetryPolicy

client = BillKit(
    api_key="sk_test_...",                  # or set BILLKIT_API_KEY
    base_url="https://api.billkit.eu",   # override for self-hosted
    timeout=30.0,                          # seconds, or pass httpx.Timeout
    retry_policy=RetryPolicy(
        max_attempts=5,
        max_retry_after_seconds=10.0,       # cap 429 Retry-After sleeps
    ),
)

The SDK auto-generates an Idempotency-Key for every mutating call, so 5xx and short Retry-After 429 retries are safe: the server replays the original response when an earlier attempt completed. Pass idempotency_key= to coalesce retries across process restarts.

Errors

from billkit import BillKit, ResourceMissingError, RateLimitError, BillKitError

client = BillKit(api_key="sk_test_...")
try:
    customer = client.customers.retrieve("cus_doesnt_exist")
except ResourceMissingError:
    print("Customer is gone")
except RateLimitError as exc:
    print(f"Rate limited; retry in {exc.retry_after}s")
except BillKitError as exc:
    print(f"BillKit error {exc.status_code}: {exc.message}")

All errors inherit from BillKitError. Subclasses: APIConnectionError, APIError, ServerError, AuthenticationError, PermissionError, ResourceMissingError, InvalidRequestError, ConflictError, RateLimitError.

Logging

The SDK is silent by default. It owns one logger, logging.getLogger("billkit"), with a NullHandler attached, and it never calls basicConfig, never sets a level, and never adds a handler to a logger it doesn't own. Your logging config is yours.

Turn it on from your application:

import logging

logging.basicConfig()
logging.getLogger("billkit").setLevel(logging.DEBUG)
DEBUG:billkit:BillKit request POST https://api.billkit.eu/v1/customers (attempt 1/3)
DEBUG:billkit:BillKit response POST https://api.billkit.eu/v1/customers -> 503 in 84ms (request_id=req_9f2a)
WARNING:billkit:BillKit retrying POST https://api.billkit.eu/v1/customers after HTTP 503 (attempt 1) in 500ms
DEBUG:billkit:BillKit response POST https://api.billkit.eu/v1/customers -> 200 in 91ms (request_id=req_9f2b)
  • DEBUG: one line per attempt, one per response (status, elapsed ms, X-Request-Id; quote that id to support).
  • WARNING: one line per retry, with the reason and the delay before the next attempt.

Never logged: your API key or the Authorization header; request and response bodies (they carry customer PII); the query string (list filters carry values like email=); only the path is logged. The final failure isn't logged either: it's raised as a typed BillKitError carrying the status, request id and retry-after, and logging it here too would hand you a duplicate you can't suppress.

The logger object is exported if you'd rather wire it up directly:

from billkit import logger

logger.addHandler(my_handler)

Webhook verification

from billkit import WebhookSignature, WebhookVerificationError

# In your FastAPI / Flask / Django handler:
try:
    event = WebhookSignature.verify(
        payload=request.body,
        signature_header=request.headers.get("BillKit-Signature"),
        secret=os.environ["BILLKIT_WEBHOOK_SECRET"],
    )
except WebhookVerificationError:
    return Response(status_code=400)

if event["type"] == "subscription.created":
    handle_new_subscription(event["data"])

The verifier enforces a 5-minute timestamp tolerance (replay protection) and constant-time HMAC compare. Pass tolerance_seconds= to customise.

Development

uv sync --all-extras --dev
uv run pytest
uv run ruff check
uv run mypy src

License

Proprietary.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

billkit_eu-0.1.1.tar.gz (74.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

billkit_eu-0.1.1-py3-none-any.whl (35.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: billkit_eu-0.1.1.tar.gz
  • Upload date:
  • Size: 74.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for billkit_eu-0.1.1.tar.gz
Algorithm Hash digest
SHA256 fe784dde9d6c3de5e2931ae838df40ccf5f600167b1cb59a2d734c66a37b138d
MD5 df2401b7fcd715b767642f748a94e5cc
BLAKE2b-256 1b6a9c162ba65eea99cce8cf4777bf4850e884648984d1245c543a36160f5d91

See more details on using hashes here.

Provenance

The following attestation bundles were made for billkit_eu-0.1.1.tar.gz:

Publisher: publish.yml on billkit-eu/billkit-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file billkit_eu-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: billkit_eu-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 35.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for billkit_eu-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 20315d660986b984ca2d39ec0ad4584299d3b3a39d51301ce229f97368efaf60
MD5 f61a3e8d1c7745ec3b8880a966e7f253
BLAKE2b-256 dd0579140701b18ac9d0153468af0471c5d78bef5f4befb364a3a42ed702b9ff

See more details on using hashes here.

Provenance

The following attestation bundles were made for billkit_eu-0.1.1-py3-none-any.whl:

Publisher: publish.yml on billkit-eu/billkit-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.1

2 files

0.2.0

2 files

This release

0.1.1 This release

2 files

0.1.0

2 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