Reference
Account, balance, usage and Sender IDs
GET /v1/me identifies the key and its workspace, GET /v1/account/balance returns the wallet in kobo and naira, GET /v1/account/usage counts messages and spend for the key's mode, and GET /v1/sms/sender-ids lists Sender IDs with their route and status. None of them change anything.
Endpoints
| Endpoint | Scope | Returns |
|---|---|---|
| GET /v1 | None | An index of every public route, the webhook events and the scopes |
| GET /v1/health | None | Whether the API can reach its database |
| GET /v1/me | account:read | The workspace, the key and its mode |
| GET /v1/account/balance | account:read | The wallet balance |
| GET /v1/account/usage | account:read | Lifetime message count and spend for the key's mode |
| GET /v1/api-keys | account:read | The workspace's keys, without secrets (see authentication) |
| GET /v1/sms/sender-ids | senders:read | The workspace's Sender IDs; filter with ?sms_route= |
| GET /v1/sms/sender-ids/{sender_id} | senders:read | One Sender ID |
GET /v1/me
{
"success": true,
"data": {
"workspace": {
"id": "ws_3e5f7a9b1c2d4e6f",
"name": "Acme Ltd",
"status": "active",
"kyc_status": "approved",
"production_access": true,
"channels": { "sms": "active" }
},
"api_key": {
"id": "key_8a7b6c5d4e3f2a1b",
"name": "Checkout service",
"mode": "production",
"prefix": "sk_live_",
"status": "active",
"scopes": ["messages:send", "messages:read"],
"last_used_at": "2026-09-24T10:15:02.114Z"
},
"mode": "production"
},
"request_id": "req_5e6f7a8b9c0d1e2f"
}Call it at startup to fail fast on a wrong key. A live send needs status active, kyc_status approved (or waived) and production_access true; otherwise it returns 403 kyc_required or workspace_suspended. A sandbox send needs none of them.
GET /v1/account/balance
{
"success": true,
"data": {
"wallet": {
"id": "wal_1f2e3d4c5b6a7980",
"frozen": false,
"balance": {
"amount_kobo": 2500000,
"amount": 25000,
"currency": "NGN",
"formatted": "NGN 25,000.00"
}
}
},
"request_id": "req_6f7a8b9c0d1e2f3a"
}- Amounts are integers in kobo (100 kobo = ₦1).
amountis the same value in naira, for display only; do arithmetic onamount_kobo. - The balance is what is available to spend. A live send reserves its full estimated cost when it is accepted, so the balance drops at once and rises again when the unused part is released.
- A frozen wallet refuses live sends with 403 wallet_frozen.
walletis null for a workspace that has no wallet yet. - The wallet is funded in the Console by card or bank transfer. There is no API endpoint to fund it.
GET /v1/account/usage
{
"success": true,
"data": {
"total_messages": 184233,
"by_channel": { "sms": 184233 },
"wallet_spend_kobo": 128963100
},
"request_id": "req_7a8b9c0d1e2f3a4b"
}A live key reports live traffic and a sandbox key reports sandbox traffic, so sandbox tests never inflate your production figures. For usage over a date range, use the reports in the Console.
Sender IDs
A Sender ID is the name a message appears from. Each is requested in the Console under Sender IDs, reviewed, and approved for one route: promotional or transactional. A live send must name a Sender ID approved for the route it sends on. The API lists them; it cannot request one.
{
"success": true,
"data": [
{
"sender_id": "sid_2c4e6a8b0d1f3e5a",
"sender": "AcmeAlerts",
"use_case": "Login codes and payment alerts",
"sms_route": "transactional",
"status": "approved",
"networks": ["MTN", "Airtel", "Glo", "9mobile"],
"created_at": "2026-08-02T11:20:00.000Z",
"updated_at": "2026-08-05T09:41:12.000Z"
}
],
"request_id": "req_8b9c0d1e2f3a4b5c"
}| status | Can send live? |
|---|---|
| approved, active | Yes, on its sms_route |
| pending_review | No: sender_id_not_approved |
| rejected, suspended | No: sender_id_not_approved |
sms_routeispromotionalortransactional. Sending on the other route returns 403 sender_id_route_mismatch.unclassifiedmarks an older Sender ID not yet assigned a route. It cannot send live (403 sender_id_unclassified) until Sendozi classifies it.senderis what you put in a send request'ssenderfield, spelt exactly as shown.- This list is not paginated.
GET /v1/health
Needs no key. Returns 200 with status: "ok" and the database latency when the API is healthy, and 503 with database_unavailable when it is not. Use it for uptime monitoring, not before every send.
Frequently asked questions
- Can I check my Sendozi balance through the API?
- Yes. GET /v1/account/balance returns the wallet balance in kobo and in naira, and whether the wallet is frozen. It needs a key with the account:read scope.
- Can I register a Sender ID through the API?
- No. Sender IDs are requested and approved in the Console. The API lists them, with their route and status, so your code can check one before sending.
- Why does usage from my sandbox key show different numbers?
- Usage is reported for the key's own mode. A sandbox key counts sandbox messages; a live key counts live ones.
Related reading
Getting started
Authentication, API keys and scopes
How to authenticate Sendozi API requests, sandbox and production keys, the scopes that limit what a key can do, and how to rotate or revoke a key without surprises.
Sending
Sending SMS
POST /v1/sms/send and /v1/sms/bulk: every request field, the response, how recipients are cleaned, limits, the delivery window, and exactly how a send is priced and charged.
Reliability
Errors and the response envelope
The Sendozi response envelope, every error code the API returns with its HTTP status and fix, which errors are safe to retry, Retry-After, and how request_id traces a request.