SettlraSettlra/Docs
DashboardAPI Playground

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}")