REST API v1

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.

Required Header
X-API-Key: your_api_key_here
Base URL
http://spendodatahub.site/v1/
πŸ” Test your key first β€” call GET /v1/ping before anything else. It confirms your key is active and shows your usage count.
⚠️ Keep your API key private. Never expose it in frontend JavaScript or commit it to a public repository. If compromised, contact us immediately to have it revoked.

⚑ Typical Integration Flow

Here is the recommended order of API calls for a typical data bundle purchase integration:

1
GET /v1/ping β€” verify your key is working
2
GET /v1/packages β€” fetch available bundles to show your customers
3
If MoMo payment: POST /v1/upload β€” upload the payment screenshot, get back a URL
4
POST /v1/orders β€” place the order with the package ID, phone number and payment details
5
GET /v1/track?q=ORD-XXXX β€” poll this endpoint to check order status and update your customer

For AFA SIM registration, use POST /v1/afa instead of step 4, then track with GET /v1/track?q=AFA-XXXX.

πŸ“‘ Endpoints

GET /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.

Response (200)
{
  "success": true,
  "message": "API key is valid",
  "owner":   "Your Business Name",
  "requests_made": 42
}
GET /v1/networks List all active networks

Returns all currently active networks. Use the id to filter packages by network.

Response (200)
{
  "success": true,
  "networks": [
    {"id": 1, "name": "MTN",        "slug": "mtn"},
    {"id": 2, "name": "AirtelTigo", "slug": "airteltigo"},
    {"id": 3, "name": "Telecel",    "slug": "telecel"}
  ]
}
GET /v1/packages List all packages grouped by network

Returns every active data package grouped by network. Use the package id when placing an order.

Response (200)
{
  "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:

Filter by network
GET /v1/packages/{network_id}
Response (200)
{
  "success": true,
  "network":  {"id": 1, "name": "MTN"},
  "packages": [
    {"id": 1, "size": "1GB",  "price": 5},
    {"id": 2, "size": "5GB",  "price": 20}
  ]
}
POST /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.

Send the file as multipart/form-data with the field name file. Do not send JSON for this endpoint.
FieldTypeRequiredDescription
filefileβœ… YesPNG, JPG, JPEG, GIF, or PDF β€” max 5MB
Python Example
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
Response (200)
{
  "success": true,
  "url":     "https://res.cloudinary.com/spendo/.../screenshot.jpg",
  "message": "File uploaded successfully. Use this URL as momo_screenshot in your order request."
}
POST /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.

FieldTypeRequiredDescription
package_idintegerβœ… YesPackage ID from GET /v1/packages
recipient_phonestringβœ… Yes10-digit Ghana number that will receive the data e.g. 0244000000
payment_methodstringβœ… YesMTN MoMo or Paystack
customer_namestringNoCustomer's name for reference
customer_phonestringNoCustomer's own contact number
paystack_referencestringIf PaystackReference from your Paystack payment callback
momo_screenshotstringIf MoMoURL from POST /v1/upload
For Paystack payments, you must include 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.
Request Body β€” Paystack
{
  "package_id":         1,
  "recipient_phone":    "0244000000",
  "payment_method":     "Paystack",
  "customer_name":      "Kofi Mensah",
  "customer_phone":     "0244000000",
  "paystack_reference": "SPD-1234567890"
}
Request Body β€” MTN MoMo
{
  "package_id":      1,
  "recipient_phone": "0244000000",
  "payment_method":  "MTN MoMo",
  "customer_name":   "Kofi Mensah",
  "momo_screenshot": "https://res.cloudinary.com/spendo/.../screenshot.jpg"
}
Response (201 Created)
{
  "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"
  }
}
Save the order_ref from the response β€” use it with GET /v1/track to check status updates.
GET /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.

ParameterTypeRequiredDescription
qstring (query param)βœ… YesOrder ref (e.g. ORD-XXXXXXX), AFA ref (e.g. AFA-XXXXXXX), or phone number
Examples
GET /v1/track?q=ORD-A1B2C3D4E5
GET /v1/track?q=AFA-X1Y2Z3W4V5
GET /v1/track?q=0244000000
Response (200)
{
  "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

POST /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.

FieldTypeRequiredDescription
surnamestringβœ… YesAs printed on Ghana Card
first_namestringβœ… YesAs printed on Ghana Card
date_of_birthstringβœ… YesFormat: YYYY-MM-DD e.g. 1990-05-25
ghana_card_numberstringβœ… YesFormat: GHA-123456789-0
phone_numberstringβœ… Yes10-digit Ghana number e.g. 0244000000
payment_methodstringβœ… YesMTN MoMo or Paystack
paystack_referencestringIf PaystackReference from Paystack payment callback
momo_screenshotstringIf MoMoURL from POST /v1/upload
Request Body
{
  "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"
}
Response (201 Created)
{
  "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"
  }
}
Save the reg_ref β€” use it with GET /v1/track?q=AFA-XXXXXXX to check registration status.

⚠️ Error Codes

HTTP CodeMeaningCommon Cause
200OKRequest succeeded
201CreatedOrder or registration submitted successfully
400Bad RequestMissing required field, wrong format, or invalid value
401UnauthorizedMissing or invalid API key
403ForbiddenAPI key is suspended or expired
404Not FoundPackage ID does not exist or is inactive
500Server ErrorUnexpected error β€” contact support with details
Error Response Format
{
  "success": false,
  "error":   "Description of what went wrong"
}

πŸ’» Code Examples

Python β€” Full Paystack order flow
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
Python β€” Full MoMo order flow
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"])
JavaScript (fetch) β€” Place order and track
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"
Python β€” AFA Registration
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:

πŸ“§ Email Support πŸ“± WhatsApp Support
⚑
Install Spendo App Add to home screen for faster access