Skip to main content

Python SDK

The official PayKore Python SDK. Supports both synchronous and async/await usage.

Package: paykore

danger

All API calls must be made server-side. Never embed your API key in client-facing code or any environment where it can be read by end users.


Installation

pip install paykore

Initialisation

import os
from paykore import PayKoreApi

client = PayKoreApi(
token=os.environ["PAYKORE_API_KEY"],
base_url="https://api.paykore.com", # or https://sandbox.paykore.com
)

For sandbox:

client = PayKoreApi(
token=os.environ["PAYKORE_SANDBOX_KEY"],
base_url="https://sandbox.paykore.com",
)

The token parameter is your API key (sk_live_* or sk_test_*), sent as Authorization: Bearer <key>. The parameter name accepts your secret key, not a JWT.

For async usage, use AsyncPayKoreApi with identical parameters, every method below has a 1:1 async equivalent:

from paykore import AsyncPayKoreApi

client = AsyncPayKoreApi(
token=os.environ["PAYKORE_API_KEY"],
base_url="https://api.paykore.com",
)

Wallets

# Create a wallet
wallet = client.wallets.create_wallet(
user_ref="user_123",
currency="NGN",
metadata={"plan": "premium"},
)
# wallet.status == "pending" initially
# wallet.account_number is empty until wallet.activated webhook fires

# Get a wallet and its current balance
result = client.wallets.get_wallet(id="wlt-uuid-here")
# result.wallet — the Wallet object
# result.balance_kobo — current posted balance in kobo

# List a wallet's transactions
page = client.wallets.list_wallet_transactions(
id="wlt-uuid-here",
limit=20,
cursor=None, # pass cursor from previous response to paginate
)
MethodReturns
client.wallets.create_wallet(user_ref, ...)CreateWalletResponse
client.wallets.get_wallet(id)GetWalletResponse
client.wallets.list_wallet_transactions(id, ...)ListWalletTransactionsResponse
note

create_wallet returns a 202 Accepted response — the wallet exists immediately in pending state, but account_number is empty until the MFB provisions it asynchronously. Listen for the wallet.activated webhook before showing the NUBAN to your user.


Transfers

import uuid

# P2P transfer — instant, no NIP fee, synchronous
transfer = client.transfers.p2p_transfer(
source_wallet="source-wallet-uuid",
dest_wallet="dest-wallet-uuid",
amount=500000, # kobo — ₦5,000
reference=str(uuid.uuid4()),
description="Payment for order #789",
)
# transfer.status == "completed"

# Bank transfer — async NIP, returns 202, settles via webhook
payout = client.transfers.bank_transfer(
source_wallet="source-wallet-uuid",
dest_account={
"bank_code": "058",
"account_number": "0123456789",
"account_name": "Ada Okafor",
},
amount=1000000, # kobo — ₦10,000
reference=str(uuid.uuid4()),
)
# payout.status == "processing"
# Final status arrives via transaction.completed or transaction.failed webhook
MethodReturns
client.transfers.p2p_transfer(source_wallet, dest_wallet, amount, reference, ...)Transaction
client.transfers.bank_transfer(source_wallet, dest_account, amount, reference, ...)Transaction

Payments

import uuid

# Create a QR payment request (merchant side)
qr = client.payments.create_qr_payment(
wallet_id="merchant-wallet-uuid",
amount=500000,
reference="order_789",
expires_in_seconds=300, # default 5 minutes
)
# qr.qr_code — base64-encoded QR image or payload string
# qr.expires_at — show countdown to user

# Pay a QR request (payer side)
payment = client.payments.pay_qr_payment(
qr_reference=qr.reference,
source_wallet="payer-wallet-uuid",
)

# Initiate a USSD payment
ussd = client.payments.initiate_ussd_payment(
wallet_id="wallet-uuid",
amount=500000,
reference="order_789",
phone="+2348012345678", # E.164 format
)
# ussd.ussd_code — display to user e.g. "*123*456#"
# ussd.session_id — use to poll status if needed
# ussd.expires_at — 3-minute TTL

# Split payment across multiple wallets
splits = client.payments.split_payment(
source_wallet="source-wallet-uuid",
reference="order_456",
splits=[
{"wallet_id": "seller-wallet-uuid", "amount": 824000},
{"wallet_id": "platform-wallet-uuid", "amount": 176000},
],
)
# splits — list of Transaction objects, one per destination
MethodReturns
client.payments.create_qr_payment(wallet_id, amount, reference, ...)CreateQRPaymentResponse
client.payments.pay_qr_payment(qr_reference, source_wallet)Transaction
client.payments.initiate_ussd_payment(wallet_id, amount, reference, phone)InitiateUSSDPaymentResponse
client.payments.split_payment(source_wallet, reference, splits)list[Transaction]

KYC

# Submit BVN — async, result arrives via kyc.verified or kyc.failed webhook
bvn_result = client.kyc.submit_bvn(
user_ref="user_123",
bvn="22190000000",
)
# bvn_result.status == "pending"

# Submit NIN
nin_result = client.kyc.submit_nin(
user_ref="user_123",
nin="12345678901",
)

# Get current KYC status for a user
status = client.kyc.get_kyc_status("user_123")
for verification in status.verifications:
print(f"type={verification.type} status={verification.status}")
MethodReturns
client.kyc.submit_bvn(user_ref, bvn)SubmitVerificationResponse
client.kyc.submit_nin(user_ref, nin)SubmitVerificationResponse
client.kyc.get_kyc_status(user_ref)KYCStatusResponse

Webhooks

# Register an endpoint
endpoint = client.webhooks.create_webhook(
url="https://yourapp.com/webhooks/paykore",
events=[
"transaction.completed",
"transaction.failed",
"wallet.activated",
"kyc.verified",
"kyc.failed",
],
)

# List registered endpoints
endpoints = client.webhooks.list_webhooks()

# Delete an endpoint
client.webhooks.delete_webhook(id="webhook-uuid")
MethodReturns
client.webhooks.create_webhook(url, events)WebhookEndpoint
client.webhooks.list_webhooks()list[WebhookEndpoint]
client.webhooks.delete_webhook(id)None

Error handling

All methods raise a subclass of ApiError on failure. Import it from the SDK's core module and branch on err.body["error"]:

from paykore.core import ApiError

try:
transfer = client.transfers.p2p_transfer(
source_wallet="source-wallet-uuid",
dest_wallet="dest-wallet-uuid",
amount=500000,
reference=str(uuid.uuid4()),
)
except ApiError as err:
match err.body.get("error"):
case "INSUFFICIENT_FUNDS":
# show user their balance
pass
case "VALIDATION_ERROR":
print(f"Bad request: {err.body.get('message')}")
case _:
# unexpected — log for investigation
print(f"PayKore error {err.status_code}: {err.body}")
raise
AttributeTypeDescription
err.status_codeintHTTP status code (e.g. 422).
err.body["error"]strMachine-readable code. Branch on this — it's stable across SDK versions.
err.body["message"]strHuman-readable description. For logging only.

Async usage

Every method has an identical async counterpart via AsyncPayKoreApi:

import asyncio
from paykore import AsyncPayKoreApi

client = AsyncPayKoreApi(
token=os.environ["PAYKORE_API_KEY"],
base_url="https://api.paykore.com",
)

async def main() -> None:
wallet = await client.wallets.create_wallet(user_ref="user_123")
print(wallet.status) # "pending"

transfer = await client.transfers.p2p_transfer(
source_wallet="source-wallet-uuid",
dest_wallet="dest-wallet-uuid",
amount=500000,
reference=str(uuid.uuid4()),
)
print(transfer.status) # "completed"

asyncio.run(main())

The async client is a drop-in replacement — all method signatures are identical, every call just needs to be await-ed.


Raw response access

To read response headers (e.g. Retry-After on a 429), use .with_raw_response:

raw = client.wallets.with_raw_response.create_wallet(user_ref="user_123")
retry_after = raw.headers.get("Retry-After")
wallet = raw.data # CreateWalletResponse

Next steps