Python SDK
The official Settlra Python SDK provides a typed, idiomatic Python client. It supports both synchronous and async (asyncio) usage patterns.
Installation
bash
"color:#ff7b72">pip install settlra-"color:#ff7b72">python "color:#8b949e"># or with Poetry poetry add settlra-"color:#ff7b72">python
Setup
settlra_client.pypython
"color:#ff7b72">import os "color:#ff7b72">from settlra "color:#ff7b72">import SettlraClient client = SettlraClient( api_key=os.environ['SETTLRA_API_KEY'], "color:#8b949e"># Optional: use sandbox base_url='https://api-sandbox.settlra.com/v1', )
Async setup
python
"color:#ff7b72">import os "color:#ff7b72">from settlra.async_client "color:#ff7b72">import AsyncSettlraClient client = AsyncSettlraClient(api_key=os.environ['SETTLRA_API_KEY'])
Quotes
Create a quote
python
"color:#ff7b72">import time quote = client.quotes.create( source_amount_usdc=500.0, target_currency='UGX', idempotency_key=f'quote-{int(time.time())}', ) print(quote.quote_id) "color:#8b949e"># "q_01j3ab4cd..." print(quote.target_amount) "color:#8b949e"># 1871250 print(quote.exchange_rate) "color:#8b949e"># 3742.5 print(quote.expires_at) "color:#8b949e"># datetime object
Get a quote
python
quote = client.quotes.get('q_01j3ab4cd5ef6gh7ij8kl9mn0p') "color:#ff7b72">if quote.status == 'EXPIRED': "color:#8b949e"># Create a new quote pass
Payouts
Create a payout
python
"color:#ff7b72">import time payout = client.payouts.create( quote_id=quote.quote_id, recipient_phone='+256700123456', recipient_name='Jane Nakato', network='MTN_UG', idempotency_key=f'payout-{int(time.time())}', ) print(payout.payout_id) "color:#8b949e"># "pyt_01j3pq8rs..." print(payout.deposit_address) "color:#8b949e"># "0xABCD1234..." print(payout.status) "color:#8b949e"># "AWAITING_FUNDS"
Get payout status
python
payout = client.payouts.get('pyt_01j3pq8rs9tu0vw1xy2za3bc4d') print(payout.status) "color:#8b949e"># "SETTLED" print(payout.settled_at) "color:#8b949e"># datetime object
List payouts
python
result = client.payouts.list(status='SETTLED', limit=20, offset=0) "color:#ff7b72">for payout in result.data: print(payout.payout_id, payout.status) print(f"Total results in page: {result.meta.count}")
Bulk payouts
python
"color:#8b949e"># Create quotes "color:#ff7b72">for all recipients first quotes = [ client.quotes.create( source_amount_usdc=r['amount'], target_currency=r['currency'], idempotency_key=f'batch-quote-{batch_id}-{i}', ) "color:#ff7b72">for i, r in enumerate(recipients) ] "color:#8b949e"># Submit batch batch = client.payouts.create_bulk( payouts=[ { 'quote_id': quotes[i].quote_id, 'recipient_phone': r['phone'], 'recipient_name': r['name'], 'network': r['network'], 'idempotency_key': f'batch-{batch_id}-{i}', } "color:#ff7b72">for i, r in enumerate(recipients) ] ) print(batch.batch_id) "color:#8b949e"># "batch_01j3rs4tu..." print(batch.total_count) "color:#8b949e"># 50
Async usage
python
"color:#ff7b72">import asyncio "color:#ff7b72">import os "color:#ff7b72">from settlra.async_client "color:#ff7b72">import AsyncSettlraClient "color:#ff7b72">async "color:#ff7b72">def run_payout(): client = AsyncSettlraClient(api_key=os.environ['SETTLRA_API_KEY']) quote = "color:#ff7b72">await client.quotes.create( source_amount_usdc=100.0, target_currency='KES', idempotency_key='async-quote-001', ) payout = "color:#ff7b72">await client.payouts.create( quote_id=quote.quote_id, recipient_phone='+254700123456', network='MPESA_KE', idempotency_key='async-payout-001', ) "color:#ff7b72">return payout payout = asyncio.run(run_payout())
Webhook verification
webhook_handler.pypython
"color:#ff7b72">import hmac "color:#ff7b72">import hashlib "color:#ff7b72">import json "color:#ff7b72">import os "color:#ff7b72">from flask "color:#ff7b72">import Flask, request, jsonify "color:#ff7b72">from settlra.webhook "color:#ff7b72">import verify_signature app = Flask(__name__) @app.route('/webhooks/settlra', methods=['POST']) "color:#ff7b72">def webhook(): raw_body = request.get_data() signature = request.headers.get('X-Settlra-Signature', '') "color:#ff7b72">try: event = verify_signature( payload=raw_body, signature=signature, secret=os.environ['SETTLRA_WEBHOOK_SECRET'], ) "color:#ff7b72">except ValueError "color:#ff7b72">as e: "color:#ff7b72">return jsonify({'error': str(e)}), 401 "color:#ff7b72">if event['type'] == 'payout.settled': payout_id = event['data']['payout_id'] print(f'Payout {payout_id} settled successfully') "color:#8b949e"># Update your database here "color:#ff7b72">elif event['type'] == 'payout.failed': payout_id = event['data']['payout_id'] reason = event['data']['failure_reason'] print(f'Payout {payout_id} failed: {reason}') "color:#ff7b72">return jsonify({'received': "color:#79c0ff">True}), 200
Error handling
python
"color:#ff7b72">from settlra.exceptions "color:#ff7b72">import SettlraApiError, SettlraNetworkError "color:#ff7b72">try: payout = client.payouts.create(...) "color:#ff7b72">except SettlraApiError "color:#ff7b72">as e: print(e.code) "color:#8b949e"># e.g. "QUOTE_EXPIRED" print(e.message) "color:#8b949e"># Human-readable message print(e.status) "color:#8b949e"># HTTP status code "color:#ff7b72">if e.code == 'QUOTE_EXPIRED': "color:#8b949e"># Create a new quote and retry pass "color:#ff7b72">if e.code == 'RATE_LIMIT_EXCEEDED': "color:#ff7b72">import time time.sleep(60) "color:#8b949e"># Retry "color:#ff7b72">except SettlraNetworkError "color:#ff7b72">as e: "color:#8b949e"># Connection timeout, DNS failure, etc. print(f'Network error: {e}')
Type hints
python
"color:#ff7b72">from settlra.types "color:#ff7b72">import ( Quote, Payout, PayoutBatch, WebhookEvent, MobileMoneyNetwork, FiatCurrency, PayoutStatus, ) "color:#8b949e"># MobileMoneyNetwork: Literal['MTN_UG', 'AIRTEL_UG', 'MPESA_KE', 'MTN_RW', 'AIRTEL_TZ'] "color:#8b949e"># FiatCurrency: Literal['UGX', 'KES', 'NGN', 'GHS', 'TZS', 'ZMW'] "color:#ff7b72">def process_payout(payout: Payout) -> "color:#79c0ff">None: "color:#ff7b72">if payout.status == 'SETTLED': print(f"Delivered {payout.target_amount_fiat} {payout.target_currency}")