Does FastAPI Work With Paddle?
FastAPI and Paddle integrate seamlessly for building SaaS backends with payment processing—FastAPI handles the API layer while Paddle manages billing and tax compliance.
Quick Facts
How FastAPI Works With Paddle
FastAPI and Paddle work together naturally because Paddle provides REST and webhook APIs that integrate cleanly into FastAPI's async request handling. You use FastAPI to expose endpoints that trigger Paddle transactions, handle webhook events for subscription changes, and manage your product catalog. FastAPI's automatic request validation with Pydantic makes processing Paddle's webhook payloads type-safe and straightforward. The developer experience is excellent: define your Paddle API credentials as environment variables, create a Pydantic model for Paddle webhooks, set up a verification endpoint, and you're handling payments. The main architecture consideration is async execution—Paddle API calls should be made asynchronously to avoid blocking your FastAPI request loop, which is why using `httpx` or `aiohttp` is recommended over synchronous `requests`. You'll also want to store Paddle transaction IDs and subscription states in your database to reconcile with webhook events, since webhooks can arrive out of order or be retried.
Best Use Cases
Quick Setup
pip install fastapi uvicorn httpx pydantic python-dotenvfrom fastapi import FastAPI, Request, HTTPException
from pydantic import BaseModel
import httpx
import os
app = FastAPI()
class SubscriptionWebhook(BaseModel):
event_id: str
event_type: str
data: dict
@app.post("/webhooks/paddle")
async def paddle_webhook(request: Request):
body = await request.json()
# Verify signature (simplified - use Paddle's SDK in production)
signature = request.headers.get("X-Paddle-Signature")
if not signature:
raise HTTPException(status_code=401, detail="Missing signature")
# Handle webhook event
event_type = body.get("event_type")
if event_type == "subscription.updated":
user_id = body["data"]["custom_data"]["user_id"]
# Update your database with new subscription state
print(f"Subscription updated for user {user_id}")
return {"status": "received"}
@app.post("/subscribe")
async def create_subscription(user_id: str, plan_id: str):
async with httpx.AsyncClient() as client:
response = await client.post(
"https://api.paddle.com/subscriptions",
headers={"Authorization": f"Bearer {os.getenv('PADDLE_API_KEY')}"},
json={"items": [{"price_id": plan_id}],
"custom_data": {"user_id": user_id}}
)
return response.json()Known Issues & Gotchas
Webhook signature verification is required but easy to miss
Fix: Always verify the X-Paddle-Signature header using Paddle's public key before processing webhook data. Use a dedicated middleware or decorator to enforce this on all webhook endpoints.
Webhooks can be delivered out of order or retried multiple times
Fix: Store webhook IDs in your database and check for duplicates before processing. Use database transactions to ensure idempotent state updates when handling subscription events.
Paddle's sandbox and production environments use different API credentials
Fix: Use environment-based configuration (dev, staging, production) to switch between Paddle vendors and API keys. Test webhook handling in sandbox mode before going live.
Alternatives
- •Stripe + FastAPI: More granular control, larger ecosystem, but handles tax compliance differently
- •Lemonsqueezy + FastAPI: Simpler setup with similar merchant-of-record model, good for indie SaaS
- •Django + Paddle: If you prefer Django's ORM and admin panel over FastAPI's lightweight approach
Resources
Related Compatibility Guides
Explore more compatibility guides