stripe review
stripe is Stripe's official Python SDK, with generated services and resource types for payments, Checkout, billing, Connect, issuing, tax, and the rest of the public API. StripeClient is the current entry point; request methods also have async variants, lists can auto-page, transient network retries can add idempotency keys, and webhook helpers verify signatures. Version 15.5.1 clarifies StripeObject.to_dict behavior. The preceding 15.5.0 adds async v2 auto-pagination, event parsing helpers for already-verified payloads, a webhook signature-header generator for tests, and a major_api_version constant. Our Python 3.12 install imported successfully and included py.typed.
Use stripe for a real Stripe integration; signature verification, pagination, idempotent retries, and generated API coverage outweigh the 17 MB measured install in most payment backends. Scope credentials through StripeClient, pin upgrades deliberately, and never infer payment success from a browser redirect.
We installed it
| Install | ✓ · 0.4s | 7 packages on disk · 17 MB |
| Import | ✓ | import stripe in 0.51s · pure Python · py.typed · requires Python >=3.9 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does stripe install cleanly?
Yes. In a fresh container with an empty cache, pip install stripe finished in 0.4s, leaving 7 packages and 17 MB on disk. pip-audit reported no known vulnerabilities.
What does stripe need to run?
Python >=3.9, and nothing compiled: it is pure Python. In our run import stripe succeeded in 0.51s, and the package ships py.typed for type checkers.
stripe or dj-stripe: which should you use?
dj-stripe: Use it in Django when Stripe objects should be mirrored into database models through webhook-driven synchronization. Use stripe for a real Stripe integration; signature verification, pagination, idempotent retries, and generated API coverage outweigh the 17 MB measured install in most payment backends.
When should you not use stripe?
The service uses another payment processor. Braintree or Adyen SDKs follow their own APIs and account models
Use it if
- A Python backend calls Stripe and needs generated methods, resource objects, pagination, retries, and typed request parameters
- Webhook requests must be authenticated from their raw bytes before business code handles an event
- An async service wants _async methods and explicit HTTP client selection rather than wrapping blocking payment calls
- Connect calls need a per-request connected-account header without sharing mutable global API-key state
- The service uses another payment processor. Braintree or Adyen SDKs follow their own APIs and account models
- Only a couple of stable endpoints are called from a size-constrained job. A carefully tested HTTPX client can be smaller than the full generated Stripe surface
- Dependency updates cannot receive regular API and typing review. Stripe SDK majors move with API versions, and minor releases may change type-checker results
- The codebase insists on the legacy stripe.api_key and module-resource style. New development should use an explicit StripeClient to keep credentials and options scoped
- Default telemetry about request latency and feature use is unacceptable and the deployment cannot explicitly disable it
Setup reality
We installed stripe 15.5.1 in a fresh Python 3.12 Bookworm container. The install finished in 0.4 seconds and left 7 packages using 17 MB. It declares 3 direct dependencies, requires Python 3.9 or newer, and is pure Python. The distribution includes py.typed and reports the MIT License. Importing stripe took 0.51 seconds. pip-audit found no known vulnerabilities.
Create one StripeClient with a restricted secret key from a secret store. Do not put sk_live credentials in source, logs, browser code, or snippets. StripeClient keeps key, account, retry, proxy, and HTTP transport choices together. The older global stripe.api_key path still appears in many examples, but mixing it with explicit clients makes it harder to tell which account and retry policy a call uses. Request telemetry is enabled by default and can be turned off with stripe.enable_telemetry.
Synchronous and asynchronous calls can use different HTTP backends. Install the async extra and construct the chosen client explicitly when an application mixes frameworks or strict dependency pins. max_network_retries handles connection failures and selected responses; its generated idempotency keys cover retries performed inside the SDK. A queue or application retry must provide its own stable idempotency key based on the business operation.
Webhook verification needs the exact raw request body, the Stripe-Signature header, and the endpoint's whsec secret. JSON middleware that parses and serializes before verification breaks the signature. Types describe the API version bundled with the SDK. Overriding stripe_version or receiving older-version webhook payloads can make runtime fields differ from annotations. Pin a minor range when type-check failures must not arrive through an unattended update.
Patterns
Create an explicit Stripe client client-setup
import os
from stripe import StripeClient
client = StripeClient(
os.environ["STRIPE_SECRET_KEY"],
max_network_retries=2,
)
customers = client.v1.customers.list(params={"limit": 3})Keep credentials and retry settings on the client. Internal retries are disabled until max_network_retries is set.
Create a PaymentIntent create-payment-intent
intent = client.v1.payment_intents.create(
params={
"amount": 2000,
"currency": "usd",
"automatic_payment_methods": {"enabled": True},
},
)
# send intent.client_secret to your frontend to confirm
return {"clientSecret": intent.client_secret}Amounts use the currency's smallest unit. StripeClient services accept request data through params.
Start a hosted Checkout session checkout-session
session = client.v1.checkout.sessions.create(
params={
"mode": "payment",
"line_items": [{"price": "price_1ABC...", "quantity": 1}],
"success_url": "https://example.com/thanks?session_id={CHECKOUT_SESSION_ID}",
"cancel_url": "https://example.com/cart",
},
)
return redirect(session.url, code=303)A success redirect is not payment proof. Fulfill the order only after a verified webhook reports completion.
Verify a webhook event verify-webhook-signature
import stripe
from flask import request
@app.post("/stripe/webhook")
def webhook():
payload = request.get_data() # raw bytes, not request.json
sig = request.headers.get("Stripe-Signature", "")
try:
event = client.construct_event(payload, sig, WEBHOOK_SECRET)
except stripe.SignatureVerificationError:
return "bad signature", 400
if event.type == "checkout.session.completed":
fulfill(event.data.object)
return "", 200Pass the untouched request bytes and the endpoint whsec secret; an API key cannot verify the signature.
Iterate all pages lazily auto-pagination
for customer in client.v1.customers.list(
params={"limit": 100},
).auto_paging_iter():
process(customer)limit controls page size rather than total records. Converting the iterator to a list can issue many requests and retain every object.
Supply an application idempotency key idempotency-key
intent = client.v1.payment_intents.create(
params={"amount": 2000, "currency": "usd"},
options={"idempotency_key": f"order-{order.id}-charge"},
)SDK-generated keys protect its internal retries only. Reuse a business-operation key when a job may run again.
Classify Stripe failures error-handling
import stripe
try:
intent = client.v1.payment_intents.create(params={...})
except stripe.CardError as e:
show_user(e.user_message) # safe to display
except stripe.RateLimitError:
retry_later()
except stripe.InvalidRequestError as e:
log.error("bad params: %s (param=%s)", e, e.param)
raise
except stripe.StripeError as e:
log.error("stripe failure, request_id=%s", e.request_id)
raiseShow only user-safe messages and log request_id so Stripe support can trace the API call.
Override the API version deliberately pin-api-version
client = StripeClient(
os.environ["STRIPE_SECRET_KEY"],
stripe_version="2019-02-19", # or per request:
)
client.v1.customers.list(
options={"stripe_version": "2019-02-19"},
)The SDK annotations follow its bundled API version, so an override can make runtime objects disagree with static types.
Call Stripe asynchronously async-usage
import stripe
from stripe import StripeClient
client = StripeClient(
os.environ["STRIPE_SECRET_KEY"],
http_client=stripe.HTTPXClient(),
)
async def get_customer(cid: str):
return await client.v1.customers.retrieve_async(cid)
async def all_customers():
page = await client.v1.customers.list_async(params={"limit": 100})
async for c in page.auto_paging_iter():
yield cUse the _async method and an explicit async HTTP client; avoid accidental blocking calls in an event loop.
Scope a Connect request connect-account
account_intent = client.v1.payment_intents.create(
params={"amount": 2000, "currency": "usd"},
options={"stripe_account": "acct_1ABC..."},
)A per-request stripe_account option avoids turning the platform client into a connected-account client.
Expand a nested resource expand-nested-objects
sub = client.v1.subscriptions.retrieve(
"sub_1ABC...",
params={"expand": ["latest_invoice.payment_intent", "customer"]},
)
status = sub.latest_invoice.payment_intent.statusUnexpanded fields may be identifier strings. Expand only the objects the response handler actually reads.
Call an endpoint missing from generated services raw-request
response = client.raw_request(
"post",
"/v1/beta_endpoint",
param=123,
stripe_version="2022-11-15; feature_beta=v3",
)
obj = client.deserialize(response, api_mode="V1")raw_request trades typed parameters and resources for early access; deserialize returns a dynamic StripeObject.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| dj-stripe | PyPI | Use it in Django when Stripe objects should be mirrored into database models through webhook-driven synchronization. |
| braintree | PyPI | Use it when the payment account and checkout flow run on Braintree rather than Stripe. |
| Adyen | PyPI | Use it when an Adyen merchant account and its payment APIs are the system of record. |
| httpx | PyPI | Use it to own a narrow REST wrapper when only a few Stripe endpoints justify code generation and SDK upgrades. |
More web backend guides
urllib3 · requests · ws · anyio · httpx · undici · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

