airwallex-python
Unofficial Python SDK for the Airwallex API — payouts, FX, balances, global accounts, beneficiaries, deposits, and webhooks.
[!IMPORTANT] This is an unofficial, community-maintained library. It is not affiliated with, endorsed by, or supported by Airwallex Pty Ltd — "Airwallex" is their trademark, used here only to describe compatibility. The SDK is in beta: the public interface may change before v1.0, so pin your version. For vendor-supported tooling, use the official Node.js SDK.
Airwallex's only official server-side SDK is Node.js. This library brings the same developer experience to Python:
- Sync and async clients (
Airwallex/AsyncAirwallex) built on httpx - Automatic authentication — token fetched on first use and refreshed before expiry; no manual login calls
- Idempotent by default —
request_idis auto-generated for money-moving calls, so retries never double-pay - Automatic retries with full-jitter exponential backoff on 408/429/5xx/network failures (honours
Retry-After; 409 business conflicts are never retried) - Typed responses — Pydantic v2 models that are immutable and forward-compatible (unknown fields preserved, never dropped)
- Auto-pagination — iterate every page with one loop
- Webhook signature verification with replay protection
- Typed errors —
RateLimitError,AuthenticationError, … each carrying the Airwallex errorcode,source, andrequest_id
Installation
Available on PyPI:
pip install airwallex
Requires Python 3.9+. Releases follow semantic versioning; see the changelog.
Quickstart
Create API credentials in the Airwallex web app under Developer → API keys, then:
from airwallex import Airwallex
client = Airwallex(
client_id="your_client_id", # or set AIRWALLEX_CLIENT_ID
api_key="your_api_key", # or set AIRWALLEX_API_KEY
environment="demo", # "production" (default) or "demo" sandbox
)
# Current wallet balances
for balance in client.balances.current():
print(balance.currency, balance.available_amount)
Send a payout
Payouts use
/api/v1/transfers, which requires API version 2024-01-31 or later. If your account default is older, passapi_version="2024-01-31"(or newer) to the client.
transfer = client.transfers.create(
beneficiary_id="ben_abc123",
source_currency="USD",
transfer_currency="PHP",
transfer_amount=5000,
transfer_method="LOCAL",
reference="Invoice 42",
reason="professional_service_fees",
)
print(transfer.id, transfer.status)
request_id is generated for you (pass your own to control idempotency). Airwallex will never execute the same request_id twice — including across the SDK's automatic retries.
FX: quote and convert
rate = client.rates.current(buy_currency="USD", sell_currency="SGD", buy_amount=1000)
print(rate.client_rate)
conversion = client.conversions.create(
buy_currency="USD",
sell_currency="SGD",
buy_amount=1000,
term_agreement=True,
)
Accept a payment
intent = client.payment_intents.create(
amount=25.00,
currency="USD",
merchant_order_id="order_42",
)
confirmed = client.payment_intents.confirm(intent.id, payment_method={"type": "card", "card": {...}})
refund = client.refunds.create(payment_intent_id=intent.id, amount=5.00)
Issue a card
cardholder = client.issuing_cardholders.create(
email="employee@example.com",
individual={"name": {"first_name": "Ada", "last_name": "Lovelace"}},
type="INDIVIDUAL",
)
card = client.issuing_cards.create(
cardholder_id=cardholder.cardholder_id,
form_factor="VIRTUAL",
created_by="Ada Lovelace",
program={"purpose": "COMMERCIAL"},
)
Test flows in the sandbox
client.simulation.create_deposit(amount=1000, currency="USD") # demo environment only
client.simulation.transition_transfer("tra_123", next_status="PAID")
Auto-pagination
# Iterates page by page under the hood
for beneficiary in client.beneficiaries.list(page_size=100).auto_paging_iter():
print(beneficiary.beneficiary_id, beneficiary.nickname)
Async
import asyncio
from airwallex import AsyncAirwallex
async def main() -> None:
async with AsyncAirwallex(environment="demo") as client:
page = await client.transfers.list(status="PAID")
async for transfer in page.auto_paging_iter():
print(transfer.id)
asyncio.run(main())
Webhooks
Verify and parse incoming notifications (get the secret when you create the webhook endpoint):
from airwallex import webhooks, WebhookSignatureError
def handle(request): # any web framework
try:
event = webhooks.construct_event(
payload=request.body, # raw bytes — do not re-serialise
timestamp=request.headers["x-timestamp"],
signature=request.headers["x-signature"],
secret=WEBHOOK_SECRET,
)
except WebhookSignatureError:
return 400
if event.name == "transfer.settled":
...
return 200
Error handling
from airwallex import Airwallex, RateLimitError, APIStatusError
try:
client.transfers.retrieve("tra_missing")
except RateLimitError:
... # already retried automatically; back off further
except APIStatusError as err:
print(err.status_code, err.code, err.message, err.request_id)
All API errors inherit from airwallex.APIError; network failures raise airwallex.APIConnectionError.
Calling endpoints the SDK doesn't wrap yet
Every list method accepts extra query params as keyword arguments, and the client exposes a raw escape hatch with auth, retries, and error mapping intact:
page = client.transfers.list(short_reference_id="REF123") # extra filter
disputes = client.request("GET", "/api/v1/pa/payment_disputes", params={"status": "OPEN"})
Typed responses
Response models live in airwallex.types and preserve unknown fields for forward compatibility:
from airwallex.types import Transfer, Balance
def settle(transfer: Transfer) -> None: ...
Bring your own httpx client
import httpx
client = Airwallex(http_client=httpx.Client(proxy="http://proxy:3128"))
The SDK applies the base URL and default headers per request, so proxies and custom TLS work without extra configuration; the SDK will not close a client you own.
Connected accounts (platforms)
client = Airwallex(on_behalf_of="acct_connected_account_id") # sets x-on-behalf-of
Pinning an API version
client = Airwallex(api_version="2024-01-31") # sets x-api-version on every request
Two quirks observed against the live demo API, worth knowing:
- Airwallex versions endpoint groups independently — e.g.
conversions.listrequires2024-01-31on some accounts whilefx/rates/currentrejects it. If you hitincorrect_versionerrors, use a second client pinned differently for that endpoint group. - Several list endpoints enforce a minimum
page_sizeof 10 and returninvalid_argumentbelow it. - Payment-acceptance lifecycle actions (
payment_intents.confirm/capture/cancel,customers.update) require arequest_id— the SDK auto-generates one, like it does for creates. webhook_endpoints.createrequires aversionfield (e.g.version="2024-01-31").
Resources covered
| Resource | Methods |
|---|---|
client.balances |
current, history |
client.transfers |
create, retrieve, list, cancel, validate, confirm_funding |
client.batch_transfers |
create, retrieve, list, add_items, delete_items, items, quote, submit, delete |
client.wallet_transfers |
create, retrieve, list |
client.payers |
create, retrieve, update, delete, list, validate |
client.beneficiaries |
create, retrieve, update, delete, list, validate |
client.conversions |
create, retrieve, list |
client.rates |
current |
client.fx_quotes |
create, retrieve |
client.conversion_amendments |
create, quote, retrieve, list |
client.payment_intents |
create, retrieve, list, confirm, confirm_continue, capture, cancel |
client.customers |
create, retrieve, update, list, generate_client_secret |
client.refunds |
create, retrieve, list |
client.issuing_cardholders |
create, retrieve, update, delete, list |
client.issuing_cards |
create, retrieve, update, activate, limits, list |
client.issuing_transactions |
retrieve, list |
client.issuing_authorizations |
retrieve, list |
client.accounts |
retrieve |
client.financial_transactions |
retrieve, list |
client.settlements |
retrieve, list |
client.simulation |
demo-only: deposit create/settle/reject/reverse, transfer/payment transitions |
client.global_accounts |
create, retrieve, update, close, list, transactions |
client.deposits |
list |
client.reference |
supported_currencies, settlement_accounts, invalid_conversion_dates |
client.webhook_endpoints |
create, retrieve, update, delete, list |
airwallex.webhooks |
verify_signature, construct_event |
Coverage now matches (and in webhooks/pagination/simulation, exceeds) the official Node.js SDK's main surfaces — contributions welcome for the remaining areas (disputes, payment consents, linked accounts, scale/platform APIs).
Status
This SDK is beta software:
- The wrapped endpoints are grounded in Airwallex's published API spec and covered by tests, but they have not yet been exercised against every account configuration.
- Semantic versioning applies: breaking changes only in minor versions while
0.x, and patch releases never change behavior. - Response models tolerate unknown fields, so new Airwallex API versions won't break parsing.
- Test in the
demoenvironment before pointing at production, and pin the version in your dependency file (e.g.airwallex==0.2.0).
Development
uv sync # install dependencies
uv run pytest # run tests
uv run ruff check . # lint
uv run mypy # type-check
Disclaimer
This project is an independent, unofficial SDK maintained by the community. It is not affiliated with, endorsed by, sponsored by, or supported by Airwallex Pty Ltd. "Airwallex" and related marks are trademarks of Airwallex Pty Ltd; they are used here solely to indicate API compatibility. This software is provided "as is" under the MIT license — review the SECURITY policy and test against the demo environment before moving real money. If you need vendor support or SLAs, use the official Node.js SDK.
License
Metadata
Release files for airwallex 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| airwallex-0.2.1.tar.gz | 119.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| airwallex-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 173.1 kB
Release files / airwallex-0.2.1.tar.gz
| Download URL | airwallex-0.2.1.tar.gz |
|---|---|
| Size | 119.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7a4e109d40d088bc94f163a0822e4959e222a79f1cc58566f154a95c8c520922
|
|
BLAKE2b-256 checksum How to use checksums |
1c39906e0c2cd773c8beba97fb5eea854e91acde3c1fb6d6769538fffdd34aec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 2, 2026.
Transparency logRelease files / airwallex-0.2.1-py3-none-any.whl
| Download URL | airwallex-0.2.1-py3-none-any.whl |
|---|---|
| Size | 53.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0944f28b3d84069f7c28175a5a408a589ad7cfcd628e72ea60fbf83f9f89f633
|
|
BLAKE2b-256 checksum How to use checksums |
38e754e26b4674ed66485b5d38d13b331f4208dc283678ab0e8e43d3789cb19f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 2, 2026.
Transparency log