Type-safe Python client for the FlexPrice API: billing, metering, and subscription management for SaaS and usage-based products.
- Python 3.10+
pip install flexpriceWith uv or poetry:
uv add flexprice
# or
poetry add flexpriceRunnable samples are in the examples/ directory.
| Variable | Required | Description |
|---|---|---|
FLEXPRICE_API_KEY |
Yes | API key |
FLEXPRICE_API_HOST |
Optional | Full base URL including https:// and /v1 (default: https://us.api.flexprice.io/v1). No trailing slash. |
Integration tests in api/tests/python/test_sdk.py use a different env shape; see api/tests/README.md.
Initialize the client, create a customer, ingest an event:
import os
from flexprice import Flexprice
api_key = os.getenv("FLEXPRICE_API_KEY", "YOUR_API_KEY")
server_url = os.getenv(
"FLEXPRICE_API_HOST", "https://us.api.flexprice.io/v1"
)
with Flexprice(server_url=server_url, api_key_auth=api_key) as client:
external_id = "customer-123"
client.customers.create_customer(
external_id=external_id,
email="user@example.com",
name="Example Customer",
)
result = client.events.ingest_event(
request={
"event_name": "Sample Event",
"external_customer_id": external_id,
"properties": {"source": "python_app", "environment": "test"},
"source": "python_app",
}
)
print(result)The same client supports async when used as an async context manager:
import asyncio
import os
from flexprice import Flexprice
async def main():
server_url = os.getenv(
"FLEXPRICE_API_HOST", "https://us.api.flexprice.io/v1"
)
async with Flexprice(
server_url=server_url,
api_key_auth=os.getenv("FLEXPRICE_API_KEY", "YOUR_API_KEY"),
) as client:
result = await client.events.ingest_event_async(
request={
"event_name": "Sample Event",
"external_customer_id": "customer-123",
"properties": {"source": "python_async", "environment": "test"},
"source": "python_async",
}
)
print(result)
asyncio.run(main())- Pass your API key as
api_key_authwhen creating the client. The SDK sends it in thex-api-keyheader. - Set
FLEXPRICE_API_HOSTto a full URL (see Environment) or use the defaulthttps://us.api.flexprice.io/v1. - Prefer environment variables; get keys from your FlexPrice dashboard or docs.
API errors are raised as exceptions. Catch them and inspect the response as needed:
try:
with Flexprice(server_url="...", api_key_auth="...") as flexprice:
result = flexprice.events.ingest_event(request={...})
except Exception as e:
print(f"Error: {e}")
# Inspect status code and body if available on the exceptionSee the API docs for error formats and status codes.
- Full API coverage (customers, plans, events, invoices, payments, entitlements, etc.)
- Sync and async support
- Type-safe request/response models (Pydantic)
- Built-in retries and error handling
For a full list of operations, see the API reference and the examples in this repo.
- Missing or invalid API key: Ensure
api_key_authis set (or setFLEXPRICE_API_KEYand pass it in). Keys are for server-side use only. - Wrong server URL: Use a full URL such as
https://us.api.flexprice.io/v1(include/v1; no trailing slash). - 4xx/5xx on ingest: Event ingest returns 202 Accepted; for errors, check request fields (
event_name,external_customer_id,properties,source) against the API docs.
Flexprice sends webhook events to your server for async updates on payments, invoices, subscriptions, wallets, and more.
Flow:
- Register your endpoint URL in the Flexprice dashboard
- Receive
POSTwith raw JSON body - Read
event_typeto route - Parse payload into typed model
- Handle business logic idempotently
- Return
200quickly
import json
from flexprice.models import (
WebhookDtoPaymentWebhookPayload,
WebhookDtoSubscriptionWebhookPayload,
WebhookDtoInvoiceWebhookPayload,
)
def handle_webhook(raw_body: str) -> None:
event = json.loads(raw_body)
match event.get("event_type"):
case "payment.success" | "payment.failed" | "payment.updated":
payload = WebhookDtoPaymentWebhookPayload.model_validate(event)
if payload.payment:
print(f"payment {payload.payment.id}")
# TODO: update payment record
case "subscription.activated" | "subscription.cancelled" | "subscription.updated":
payload = WebhookDtoSubscriptionWebhookPayload.model_validate(event)
if payload.subscription:
print(f"subscription {payload.subscription.id}")
case "invoice.update.finalized" | "invoice.payment.overdue":
payload = WebhookDtoInvoiceWebhookPayload.model_validate(event)
if payload.invoice:
print(f"invoice {payload.invoice.id}")
case _:
print(f"unhandled event: {event.get('event_type')}")| Category | Events |
|---|---|
| Payment | payment.created · payment.updated · payment.success · payment.failed · payment.pending |
| Invoice | invoice.create.drafted · invoice.update · invoice.update.finalized · invoice.update.payment · invoice.update.voided · invoice.payment.overdue · invoice.communication.triggered |
| Subscription | subscription.created · subscription.draft.created · subscription.activated · subscription.updated · subscription.paused · subscription.resumed · subscription.cancelled · subscription.renewal.due |
| Subscription Phase | subscription.phase.created · subscription.phase.updated · subscription.phase.deleted |
| Customer | customer.created · customer.updated · customer.deleted |
| Wallet | wallet.created · wallet.updated · wallet.terminated · wallet.transaction.created · wallet.credit_balance.dropped · wallet.credit_balance.recovered · wallet.ongoing_balance.dropped · wallet.ongoing_balance.recovered |
| Feature / Entitlement | feature.created · feature.updated · feature.deleted · feature.wallet_balance.alert · entitlement.created · entitlement.updated · entitlement.deleted |
| Credit Note | credit_note.created · credit_note.updated |
Production rules:
- Keep handlers idempotent — Flexprice retries on non-
2xx - Return
200for unknown event types — prevents unnecessary retries - Do heavy processing async — respond fast, queue the work
- FlexPrice API documentation
- Python SDK examples in this repo
- SDK integration tests — different
FLEXPRICE_API_HOSTshape for automated tests