Skip to content

Python: tuppence

The server-side SDK for Python 3.10+, sync and async: pydantic models, an idempotency key on every write, safe retries and webhook verification.

Terminal window
pip install tuppence
import os
from tuppence import Tuppence
tuppence = Tuppence(os.environ["TUPPENCE_SECRET_KEY"])
customer = tuppence.customers.create(name="Ada Lovelace", email="ada@example.com")
payment = tuppence.payments.create(amount=2000, currency="gbp", customer=customer.id)
print(payment.id, payment.status, payment.approval_url)
# Iterate every item in a list. Pages are fetched as you reach them.
for c in tuppence.customers.list_all(limit=100):
print(c.id, c.email)
break
import os
from tuppence import Tuppence
tuppence = Tuppence(os.environ["TUPPENCE_SECRET_KEY"])
customer = tuppence.customers.create(name="Ada Lovelace", email="ada@example.com")
payment = tuppence.payments.create(amount=2000, currency="usd", customer=customer.id)
print(payment.id, payment.status, payment.approval_url)
# Iterate every item in a list. Pages are fetched as you reach them.
for c in tuppence.customers.list_all(limit=100):
print(c.id, c.email)
break
import os
from tuppence import Tuppence
tuppence = Tuppence(os.environ["TUPPENCE_SECRET_KEY"])
customer = tuppence.customers.create(name="Ada Lovelace", email="ada@example.com")
payment = tuppence.payments.create(amount=2000, currency="eur", customer=customer.id)
print(payment.id, payment.status, payment.approval_url)
# Iterate every item in a list. Pages are fetched as you reach them.
for c in tuppence.customers.list_all(limit=100):
print(c.id, c.email)
break
TaskCall
Create a payment linktuppence.payment_links.create(name=…, amount=…, currency=…)
Create a paymenttuppence.payments.create(amount=…, currency=…, customer=…)
Confirm one (test mode)tuppence.payments.confirm(id, payment_method="pm_card_gb")tuppence.payments.confirm(id, payment_method="pm_card_us")tuppence.payments.confirm(id, payment_method="pm_card_de")
Capture, canceltuppence.payments.capture(id), tuppence.payments.cancel(id)
Refundtuppence.refunds.create(payment=…, amount=…)
Customerstuppence.customers.create / retrieve / update / list / list_all
Balancetuppence.balance.retrieve()
Pay outtuppence.payouts.create(), tuppence.payouts.settings()
Webhook endpointstuppence.webhook_endpoints.create / list / retrieve / update / delete
Verify a webhooktuppence.webhooks.construct_event(raw_body, signature_header, secret) returns a dict
Act as a connected accounttuppence.as_account("acct_…") returns a client that sends Tuppence-Account
Anything elsetuppence.request("GET", "/v1/…", body=…, params=…) returns a dict

Every list has list(**params) for one page (.data, .has_more) and list_all(**params), an iterator over all pages.

from tuppence import AsyncTuppence
async with AsyncTuppence(os.environ["TUPPENCE_SECRET_KEY"]) as tuppence:
payment = await tuppence.payments.create(amount=2000, currency="gbp")
async for refund in tuppence.refunds.list_all(payment=payment.id):
...
from tuppence import AsyncTuppence
async with AsyncTuppence(os.environ["TUPPENCE_SECRET_KEY"]) as tuppence:
payment = await tuppence.payments.create(amount=2000, currency="usd")
async for refund in tuppence.refunds.list_all(payment=payment.id):
...
from tuppence import AsyncTuppence
async with AsyncTuppence(os.environ["TUPPENCE_SECRET_KEY"]) as tuppence:
payment = await tuppence.payments.create(amount=2000, currency="eur")
async for refund in tuppence.refunds.list_all(payment=payment.id):
...
pydantic modelsFor every object, generated from the API’s contract. Money is always an int of minor units. Fields the API adds later are kept (model_extra)
Idempotency-KeyOn every POST, reused across that call’s retries; pass idempotency_key= to choose it
RetriesTwice by default, with jittered backoff, honouring Retry-After. Retries network errors, timeouts, 5xx, 429 and a request still in flight; never another 4xx
Typed errorsCardError, InvalidRequestError, AuthenticationError, PermissionError, RateLimitError, IdempotencyError, APIError, ConnectionError. All extend TuppenceError, with code, param, decline_code and request_id
from tuppence import CardError
try:
tuppence.payments.confirm(payment_id, payment_method="pm_card_chargeDeclined")
except CardError as err:
print(err.code, err.decline_code, err.request_id)

Get a quote first. It says what arrives and when, or why not and where to fix it. The bank is the default destination. The card balance (destination="card_balance", UK only) is described before it can be used. See Instant payouts.

quote = tuppence.payouts.instant_quote(amount=5872)
if quote.available:
# £58.13 arrives within 30 minutes. The £0.59 fee comes out of the £58.72.
tuppence.payouts.create(method="instant", amount=quote.total_amount)
else:
print(quote.reason, quote.message, quote.fix_url, quote.available_from)
settings = tuppence.payouts.settings()
print(settings.instant_payouts.destinations.card_balance.message)
quote = tuppence.payouts.instant_quote(amount=5872)
if quote.available:
# $58.13 arrives within 30 minutes. The $0.59 fee comes out of the $58.72.
tuppence.payouts.create(method="instant", amount=quote.total_amount)
else:
print(quote.reason, quote.message, quote.fix_url, quote.available_from)
settings = tuppence.payouts.settings()
print(settings.instant_payouts.destinations.card_balance.message)
quote = tuppence.payouts.instant_quote(amount=5872)
if quote.available:
# €58.13 arrives within 30 minutes. The €0.59 fee comes out of the €58.72.
tuppence.payouts.create(method="instant", amount=quote.total_amount)
else:
print(quote.reason, quote.message, quote.fix_url, quote.available_from)
settings = tuppence.payouts.settings()
print(settings.instant_payouts.destinations.card_balance.message)

payouts.set_next_day switches next-day payouts (UK) on and off. Until next_day.available, the call is refused with the reason. account.retrieve_pricing() returns every price the account pays now (list price or one agreed with Tuppence) with its dates.

settings = tuppence.payouts.settings()
if settings.next_day.available:
tuppence.payouts.set_next_day(enabled=True)
for p in tuppence.account.retrieve_pricing().prices:
print(p.type, p.bps, p.fixed, p.source, p.ends_at)
Tuppence(key, api_version="2026-09-20", max_retries=4, timeout=10)
tuppence.balance.retrieve(max_retries=0, timeout=2) # per request

base_url= (or TUPPENCE_API_URL in the environment) points it at another API.

Docs assistant

Open as a page
Loading the assistant…