Official Python SDK for ProStack NG — Africa's unified API gateway
Project description
prostackng (Python)
Official Python SDK for ProStack NG — Africa's unified API gateway.
One API key. Five services. Billed in Naira.
Installation
pip install prostackng
Quick Start
from prostackng import ProStack
client = ProStack(api_key="psk_live_...")
# Send an SMS
sms = client.notifications.send_sms(
to="08012345678",
message="Your OTP is 4821. Valid for 5 minutes.",
)
print(sms.status) # "sent"
# Verify a BVN
bvn = client.identity.verify_bvn(
bvn="12345678901",
first_name="John",
last_name="Doe",
)
if bvn.status == "verified":
print(f"Verified: {bvn.first_name} {bvn.last_name}")
# Initiate a payment
payment = client.payments.initiate(amount=5000, email="user@app.com")
# Redirect user to:
print(payment.authorization_url)
Get your API key at prostack-api-dashboard.vercel.app.
Async Support (FastAPI, aiohttp)
from prostackng import AsyncProStack
client = AsyncProStack(api_key="psk_live_...")
# All methods are awaitable
sms = await client.notifications.send_sms(to="08012345678", message="Hello")
bvn = await client.identity.verify_bvn(bvn="12345678901")
payment = await client.payments.initiate(amount=5000, email="user@app.com")
FastAPI example:
from fastapi import FastAPI
from prostackng import AsyncProStack
app = FastAPI()
prostack = AsyncProStack(api_key="psk_live_...")
@app.post("/send-otp")
async def send_otp(phone: str, otp: str):
result = await prostack.notifications.send_sms(
to=phone,
message=f"Your OTP is {otp}. Valid for 5 minutes.",
)
return {"status": result.status, "cost": result.cost}
Services
Notifications
# Single SMS
result = client.notifications.send_sms(
to="08012345678",
message="Hello!",
sender_id="MyApp", # optional
)
# Email
result = client.notifications.send_email(
to="user@example.com",
subject="Welcome to MyApp",
body="Thanks for signing up.",
html="<p>Thanks for signing up.</p>", # optional
)
# Bulk SMS (up to 10,000 recipients — async processing)
job = client.notifications.send_bulk_sms(
recipients=[
{"to": "08012345678", "message": "Hi John!"},
{"to": "08098765432", "message": "Hi Jane!"},
],
callback_url="https://yourapp.com/webhooks/bulk", # optional
)
print(f"Job {job.job_id}: {job.total} recipients queued")
# Poll job status
status = client.notifications.get_bulk_job_status(job.job_id)
print(f"{status.sent}/{status.total} sent")
Payments
# Initiate
payment = client.payments.initiate(
amount=5000, # ₦5,000 — kobo conversion handled automatically
email="user@example.com",
provider="paystack", # or "flutterwave"
reference="MY-REF-001", # optional
metadata={"order_id": "99"}, # optional
)
# Redirect to payment.authorization_url
# Verify
result = client.payments.verify("MY-REF-001")
if result.status == "success":
pass # fulfill order
# List transactions
transactions = client.payments.list(limit=50)
Identity / KYC
BVN and NIN verification require a LIVE API key.
# BVN
result = client.identity.verify_bvn(
bvn="12345678901",
first_name="John", # optional cross-validation
last_name="Doe",
date_of_birth="1990-01-15", # YYYY-MM-DD
)
# NIN
result = client.identity.verify_nin(nin="12345678901", first_name="Jane")
# Phone
result = client.identity.verify_phone(phone="08012345678", network="mtn")
print(result.status) # "verified"
print(result.first_name) # "John"
print(result.cost) # 100.0
VTU / Utility Bills
# Airtime
client.vtu.buy_airtime(phone="08012345678", amount=500, network="mtn")
# Data — list plans first
plans = client.vtu.list_data_plans("airtel")
client.vtu.buy_data(phone="08012345678", plan_id=plans[0].id, network="airtel")
# Electricity
result = client.vtu.pay_electricity(
meter_number="12345678901",
disco="phed",
meter_type="prepaid",
amount=5000,
)
print(result.token) # "1234-5678-9012-3456" (prepaid token)
# Cable TV
client.vtu.pay_cable_tv(decoder_number="1234567890", provider="dstv", package="CONFAM")
Reports
# Generate (async — poll for completion)
report = client.reports.generate(
title="April 2026 Sales",
output_format="pdf", # "pdf" | "excel" | "both"
data=[
{"date": "2026-04-01", "product": "Widget A", "amount": 15000},
{"date": "2026-04-02", "product": "Widget B", "amount": 7500},
],
)
# Poll until complete
import time
while report.status in ("pending", "processing"):
time.sleep(3)
report = client.reports.get_by_id(report.id)
print(report.output_url) # download URL
Webhooks
wh = client.webhooks.create(
url="https://yourapp.com/webhooks/prostack",
events=["payment.success", "identity.verified", "vtu.success"],
)
webhooks = client.webhooks.list()
client.webhooks.delete(wh.id)
Webhook Verification
from prostackng import ProStack, verify_webhook, parse_webhook_payload
# Flask
from flask import Flask, request
app = Flask(__name__)
@app.route("/webhooks/prostack", methods=["POST"])
def prostack_webhook():
is_valid = verify_webhook(
payload=request.get_data(), # raw bytes — do NOT parse first
signature=request.headers.get("X-ProStack-Signature", ""),
secret=os.environ["PROSTACK_WEBHOOK_SECRET"],
)
if not is_valid:
return "Invalid signature", 400
event = parse_webhook_payload(request.get_data())
if event["event"] == "payment.success":
payment_id = event["data"]["id"]
# fulfill order...
return "OK", 200
# Django
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def prostack_webhook(request):
is_valid = verify_webhook(
payload=request.body,
signature=request.headers.get("X-Prostack-Signature", ""),
secret=settings.PROSTACK_WEBHOOK_SECRET,
)
if not is_valid:
return HttpResponse(status=400)
event = parse_webhook_payload(request.body)
return HttpResponse(status=200)
Error Handling
from prostackng import (
ProStack,
ProStackError,
AuthenticationError,
ValidationError,
InsufficientBalanceError,
RateLimitError,
NetworkError,
ServerError,
)
import time
try:
result = client.identity.verify_bvn(bvn="12345678901")
except InsufficientBalanceError as e:
print(f"Top up needed. Have ₦{e.balance}, need ₦{e.required}")
except RateLimitError as e:
print(f"Rate limited. Retry in {e.retry_after}s")
time.sleep(e.retry_after)
except AuthenticationError:
print("Invalid API key — check your ProStack dashboard")
except ValidationError as e:
print(f"Validation failed: {e.fields}")
except NetworkError as e:
print(f"Connection failed: {e}")
except ProStackError as e:
print(f"API error {e.status_code}: {e.message}")
The SDK automatically retries 429 and 5xx errors with exponential backoff. Auth errors (401/403) and validation errors (400/422) are not retried.
Context Managers
# Sync — auto-closes connection pool
with ProStack(api_key="psk_live_...") as client:
result = client.vtu.buy_airtime(phone="080...", amount=500, network="mtn")
# Async — auto-closes async connection pool
async with AsyncProStack(api_key="psk_live_...") as client:
result = await client.payments.initiate(amount=5000, email="u@e.com")
Requirements
- Python 3.8+
httpx >= 0.27pydantic >= 2.0
License
MIT © ProStack NG Technologies Ltd
Built in Port Harcourt, Nigeria 🇳🇬
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file prostackng-1.0.0.tar.gz.
File metadata
- Download URL: prostackng-1.0.0.tar.gz
- Upload date:
- Size: 22.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
41ee6e618f94917c97d8da928d61ded2e2cc6ea833001a2ebe4701bb27b6a351
|
|
| MD5 |
bed71e576eedbef11275fbc1e1c86c97
|
|
| BLAKE2b-256 |
396d667c8954bf493629695772c9e3896f7df33cd4948f36c40669130b3d8a51
|
File details
Details for the file prostackng-1.0.0-py3-none-any.whl.
File metadata
- Download URL: prostackng-1.0.0-py3-none-any.whl
- Upload date:
- Size: 19.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a837ab8f8fdff3e69152b47a3630e961c6df197a91dc4d46e3535c3b203e4802
|
|
| MD5 |
962ca094aa198664c2e1692c81e03b71
|
|
| BLAKE2b-256 |
678e3a9613f46a873ca1e200cb4ee26d67a42534421689d2d34bb6fe2d38dfaa
|