Spendo Data Hub API
Integrate data bundle purchases, AFA SIM registration and order tracking directly into your website or application.
π± Request an API Keyπ Getting Started
Every request to the Spendo API must include your API key in the request header. Without it, all requests will be rejected with a 401 error.
X-API-Key: your_api_key_here
http://spendodatahub.site/v1/
GET /v1/ping before anything else. It confirms your key is active and shows your usage count.
β‘ Typical Integration Flow
Here is the recommended order of API calls for a typical data bundle purchase integration:
For AFA SIM registration, use POST /v1/afa instead of step 4, then track with GET /v1/track?q=AFA-XXXX.
π‘ Endpoints
/v1/ping
Test your API key
Use this to verify your API key is valid and active. Returns your business name and total request count.
{
"success": true,
"message": "API key is valid",
"owner": "Your Business Name",
"requests_made": 42
}
/v1/networks
List all active networks
Returns all currently active networks. Use the id to filter packages by network.
{
"success": true,
"networks": [
{"id": 1, "name": "MTN", "slug": "mtn"},
{"id": 2, "name": "AirtelTigo", "slug": "airteltigo"},
{"id": 3, "name": "Telecel", "slug": "telecel"}
]
}
/v1/packages
List all packages grouped by network
Returns every active data package grouped by network. Use the package id when placing an order.
{
"success": true,
"data": [
{
"network": {"id": 1, "name": "MTN", "slug": "mtn"},
"packages": [
{"id": 1, "size": "1GB", "price": 5},
{"id": 2, "size": "5GB", "price": 20},
{"id": 3, "size": "10GB", "price": 35}
]
}
]
}
To get packages for a single network only:
GET /v1/packages/{network_id}
{
"success": true,
"network": {"id": 1, "name": "MTN"},
"packages": [
{"id": 1, "size": "1GB", "price": 5},
{"id": 2, "size": "5GB", "price": 20}
]
}
/v1/upload
Upload MoMo payment screenshot
Required for MTN MoMo payments. Upload the customer's payment screenshot first, then include the returned URL in your order or AFA request.
file. Do not send JSON for this endpoint.
| Field | Type | Required | Description |
|---|---|---|---|
file | file | β Yes | PNG, JPG, JPEG, GIF, or PDF β max 5MB |
with open("screenshot.jpg", "rb") as f:
resp = requests.post(
f"{BASE_URL}/upload",
headers={"X-API-Key": API_KEY},
files={"file": f}
).json()
screenshot_url = resp["url"] # pass this to /v1/orders
{
"success": true,
"url": "https://res.cloudinary.com/spendo/.../screenshot.jpg",
"message": "File uploaded successfully. Use this URL as momo_screenshot in your order request."
}
/v1/orders
Place a data bundle order
Places a new data bundle order. The order will appear immediately in the Spendo admin dashboard for processing.
| Field | Type | Required | Description |
|---|---|---|---|
package_id | integer | β Yes | Package ID from GET /v1/packages |
recipient_phone | string | β Yes | 10-digit Ghana number that will receive the data e.g. 0244000000 |
payment_method | string | β Yes | MTN MoMo or Paystack |
customer_name | string | No | Customer's name for reference |
customer_phone | string | No | Customer's own contact number |
paystack_reference | string | If Paystack | Reference from your Paystack payment callback |
momo_screenshot | string | If MoMo | URL from POST /v1/upload |
paystack_reference β the reference from your Paystack transaction callback.For MTN MoMo payments, upload the screenshot first via
POST /v1/upload and include the returned URL as momo_screenshot.
{
"package_id": 1,
"recipient_phone": "0244000000",
"payment_method": "Paystack",
"customer_name": "Kofi Mensah",
"customer_phone": "0244000000",
"paystack_reference": "SPD-1234567890"
}
{
"package_id": 1,
"recipient_phone": "0244000000",
"payment_method": "MTN MoMo",
"customer_name": "Kofi Mensah",
"momo_screenshot": "https://res.cloudinary.com/spendo/.../screenshot.jpg"
}
{
"success": true,
"order": {
"order_ref": "ORD-A1B2C3D4E5",
"package": "1GB",
"network": "MTN",
"recipient": "0244000000",
"amount": 5,
"currency": "GHS",
"status": "Pending",
"payment_status": "Paid",
"created_at": "2025-01-15 10:30:00"
}
}
order_ref from the response β use it with GET /v1/track to check status updates.
/v1/track?q={reference}
Track orders and AFA registrations
Track any order or AFA registration by reference number or phone number. Returns both data bundle orders and AFA registrations in separate arrays.
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string (query param) | β Yes | Order ref (e.g. ORD-XXXXXXX), AFA ref (e.g. AFA-XXXXXXX), or phone number |
GET /v1/track?q=ORD-A1B2C3D4E5 GET /v1/track?q=AFA-X1Y2Z3W4V5 GET /v1/track?q=0244000000
{
"success": true,
"query": "ORD-A1B2C3D4E5",
"count": 1,
"orders": [
{
"type": "data_bundle",
"order_ref": "ORD-A1B2C3D4E5",
"package": "1GB",
"network": "MTN",
"recipient": "0244000000",
"amount": 5,
"currency": "GHS",
"status": "Completed",
"payment_status": "Paid",
"payment_method": "Paystack",
"created_at": "2025-01-15 10:30:00"
}
],
"afa_registrations": []
}
Order status values: Pending β Processing β Completed or Failed
AFA status values: Pending β Processing β WIP β Completed or Rejected
/v1/afa
Submit AFA SIM registration
Submit an AFA SIM registration. The registration appears immediately in the Spendo admin AFA list for processing. Registration fee is GHC 12.
| Field | Type | Required | Description |
|---|---|---|---|
surname | string | β Yes | As printed on Ghana Card |
first_name | string | β Yes | As printed on Ghana Card |
date_of_birth | string | β Yes | Format: YYYY-MM-DD e.g. 1990-05-25 |
ghana_card_number | string | β Yes | Format: GHA-123456789-0 |
phone_number | string | β Yes | 10-digit Ghana number e.g. 0244000000 |
payment_method | string | β Yes | MTN MoMo or Paystack |
paystack_reference | string | If Paystack | Reference from Paystack payment callback |
momo_screenshot | string | If MoMo | URL from POST /v1/upload |
{
"surname": "MENSAH",
"first_name": "KWAME",
"date_of_birth": "1990-05-25",
"ghana_card_number": "GHA-123456789-0",
"phone_number": "0244000000",
"payment_method": "Paystack",
"paystack_reference": "SPD-9876543210"
}
{
"success": true,
"registration": {
"reg_ref": "AFA-X1Y2Z3W4V5",
"full_name": "KWAME MENSAH",
"phone": "0244000000",
"status": "Pending",
"payment_status": "Paid",
"created_at": "2025-01-15 10:30:00"
}
}
reg_ref β use it with GET /v1/track?q=AFA-XXXXXXX to check registration status.
β οΈ Error Codes
| HTTP Code | Meaning | Common Cause |
|---|---|---|
200 | OK | Request succeeded |
201 | Created | Order or registration submitted successfully |
400 | Bad Request | Missing required field, wrong format, or invalid value |
401 | Unauthorized | Missing or invalid API key |
403 | Forbidden | API key is suspended or expired |
404 | Not Found | Package ID does not exist or is inactive |
500 | Server Error | Unexpected error β contact support with details |
{
"success": false,
"error": "Description of what went wrong"
}
π» Code Examples
import requests
API_KEY = "your_api_key_here"
BASE_URL = "http://spendodatahub.site/v1"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
# 1. Test key
ping = requests.get(f"{BASE_URL}/ping", headers=HEADERS).json()
print(ping["message"]) # API key is valid
# 2. Get packages
packages = requests.get(f"{BASE_URL}/packages/1", headers=HEADERS).json()
pkg = packages["packages"][0] # first MTN package
print(f"{pkg['size']} β GHC {pkg['price']}")
# 3. Place order (Paystack β reference from your payment callback)
order = requests.post(f"{BASE_URL}/orders", headers=HEADERS, json={
"package_id": pkg["id"],
"recipient_phone": "0244000000",
"payment_method": "Paystack",
"customer_name": "Kofi Mensah",
"paystack_reference": "your-paystack-ref-here"
}).json()
order_ref = order["order"]["order_ref"]
print(f"Order placed: {order_ref}")
# 4. Track status
status = requests.get(
f"{BASE_URL}/track",
headers=HEADERS,
params={"q": order_ref}
).json()
print(status["orders"][0]["status"]) # Pending / Processing / Completed
import requests
API_KEY = "your_api_key_here"
BASE_URL = "http://spendodatahub.site/v1"
HEADERS = {"X-API-Key": API_KEY}
# 1. Upload MoMo screenshot first
with open("payment_screenshot.jpg", "rb") as f:
upload = requests.post(
f"{BASE_URL}/upload",
headers=HEADERS,
files={"file": f}
).json()
screenshot_url = upload["url"]
# 2. Place order with screenshot URL
order = requests.post(
f"{BASE_URL}/orders",
headers={**HEADERS, "Content-Type": "application/json"},
json={
"package_id": 1,
"recipient_phone": "0244000000",
"payment_method": "MTN MoMo",
"customer_name": "Kofi Mensah",
"momo_screenshot": screenshot_url
}
).json()
print(order["order"]["order_ref"])
const API_KEY = "your_api_key_here";
const BASE_URL = "http://spendodatahub.site/v1";
const headers = { "X-API-Key": API_KEY, "Content-Type": "application/json" };
// Place order
const orderRes = await fetch(`${BASE_URL}/orders`, {
method: "POST",
headers: headers,
body: JSON.stringify({
package_id: 1,
recipient_phone: "0244000000",
payment_method: "Paystack",
paystack_reference: "your-paystack-ref"
})
});
const order = await orderRes.json();
const orderRef = order.order.order_ref;
// Track order
const trackRes = await fetch(`${BASE_URL}/track?q=${orderRef}`, { headers });
const track = await trackRes.json();
console.log(track.orders[0].status); // "Pending", "Processing", "Completed"
import requests
API_KEY = "your_api_key_here"
BASE_URL = "http://spendodatahub.site/v1"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
reg = requests.post(f"{BASE_URL}/afa", headers=HEADERS, json={
"surname": "MENSAH",
"first_name": "KWAME",
"date_of_birth": "1990-05-25",
"ghana_card_number": "GHA-123456789-0",
"phone_number": "0244000000",
"payment_method": "Paystack",
"paystack_reference": "your-paystack-ref"
}).json()
reg_ref = reg["registration"]["reg_ref"]
print(f"AFA submitted: {reg_ref}")
# Track AFA status
status = requests.get(
f"{BASE_URL}/track",
headers=HEADERS,
params={"q": reg_ref}
).json()
print(status["afa_registrations"][0]["status"])
π Support
For API key requests, integration help, or technical issues β reach us directly: