API Documentation
Integrate SMS, OTP, payments, airtime and data bundles into your own software. Every request is authenticated with your API key and returns JSON.
Getting started
https://api.bulkclix.com/api/v1All paths in this reference are relative to the base URL. Use HTTPS only.
Send your key in the x-api-key header on every request. Keys are created on the API keys page and can be revoked at any time.
x-api-key: YOUR_API_KEYResponse format
Every endpoint answers with JSON. Single-object endpoints wrap the result in a message and data envelope. List endpoints return a paginated envelope with data, links and meta, and accept page and page_size query parameters.
// single object { "message": "Balance retrieved successfully", "data": { "wallet_balance": 250.75, "sms_balance": 1200, "currency": "GHS" } } // paginated list { "data": [ ... ], "links": { "first": "...", "last": "...", "prev": null, "next": "..." }, "meta": { "current_page": 1, "per_page": 15, "total": 42, "last_page": 3 } }
Errors
Errors use the same envelope with a non-2xx status and data set to null. Validation errors (422) additionally include an errors object listing the failing fields. Always branch on the HTTP status first, then read message for a human-readable reason.
{ "message": "Insufficient wallet balance", "data": null } { "message": "The amount field is required.", "errors": { "amount": ["The amount field is required."] } }
Your reference (idempotency)
Every request that moves money takes a reference you generate (transaction_id or client_reference, 12 to 36 characters). It must be unique per account: a repeated reference is rejected with 422, so a retried request can never charge you twice. Store it before you call us and use it later to check status or find the row in history.
Webhooks (callback_url)
Where an endpoint accepts a callback_url, we POST a JSON payload to it when the transaction reaches a final state (success or failed). The request carries an X-Signature header containing an HMAC-SHA256 of the raw JSON body. Respond with any 2xx status to acknowledge. Delivery is a single attempt, so treat the status endpoints as the source of truth if your endpoint was unreachable. Mobile money and bank transfers, collections and airtime all send webhooks; data bundles do not, so poll their status endpoint instead.
{ "amount": "50.00", "charges": "0.50", // collections and transfers "net_amount": 50, // collections only: what was credited to you after the fee "fee_bearer": "customer", // collections only: who absorbed the fee (account setting) "status": "success", // or "failed" "transaction_id": "ORDER-2026-000123", // the reference you sent "ext_transaction_id": "839201746512", "phone_number": "233241234567" // collections and airtime only }
Rate limits
Sender ID requests and momo-funded airtime purchases are limited to 60 requests per hour per account. Status checks are limited to 60 requests per minute. When you exceed a limit you receive 429 with a Retry-After header. Prefer webhooks over tight polling loops.
HTTP status codes
| Code | Meaning |
|---|---|
| 200 | Request succeeded. The body carries message and data. |
| 201 | Returned by the airtime networks list. Treat exactly like 200. |
| 400 | The request could not be fulfilled, most often an insufficient wallet balance or a failed provider call that was refunded. |
| 401 | Missing or unknown x-api-key header. |
| 402 | Insufficient SMS credit. data holds your balance and the cost of the request. |
| 403 | Your account is blocked, not enabled for this product, or the caller IP is not whitelisted. |
| 404 | The referenced record (transaction, group, contact, sender) was not found. |
| 422 | Validation failed. The body has message plus an errors object keyed by field. |
| 429 | Rate limit exceeded. Retry after the number of seconds in the Retry-After header. |
| 500 | Unexpected error on our side. Retry with the same reference; duplicates are rejected. |
API reference
Account
Read the balances that fund your API activity.
1 endpoint
SMS
Send messages from an approved sender ID and read delivery results. Messages are billed in SMS credits: one credit per 160 characters per recipient.
5 endpoints
OTP
Generate and verify one-time passcodes without storing them yourself. Codes are billed as SMS credits.
2 endpoints
Contacts
Manage contact groups and the contacts in them, then target a whole group from Send SMS with group_id_list.
9 endpoints
Payments · Collections
Charge a customer's mobile money wallet. The customer approves a prompt on their phone; you learn the outcome through your callback URL or by checking status. Requires collections to be enabled on your account.
3 endpoints
Payments · Transfers
Pay out from your wallet to mobile money wallets and bank accounts. Transfer endpoints only accept requests from the IP addresses whitelisted on your account. Your wallet is debited the amount plus the transfer fee and the recipient always receives the full amount; responses and webhooks report the charges so you can reconcile.
5 endpoints
KYC
Verify who owns a mobile money number before you pay them or accept a payment.
1 endpoint
Airtime
Top up any Ghanaian number from your wallet. Your account's airtime commission is deducted from the amount debited.
5 endpoints
Data bundles
Send data from your wallet: list the services, fetch the packages the network offers a number, then buy one.
4 endpoints