REST API · v1

KIMISMS Bulk SMS API

Send SMS messages to any Kenyan mobile network programmatically. Two endpoints. No SDK required. Works from any language.

2
Endpoints
60/min
Rate limit
JWT
Auth type
JSON
Format

Overview

The KIMISMS REST API lets you send SMS messages from any application. All requests and responses use JSON over HTTPS.

1
Get credentials — sign up and go to Developer → Credentials for your client_id and client_secret.
2
Get a token — call POST /api/generate-token to receive a 30-day Bearer token.
3
Send messages — call POST /api/send-sms with the token in every request.

Base URL

https://app.kimisms.com
All API calls must use HTTPS. Plain HTTP is rejected.

Authentication

The API uses OAuth 2 client credentials. Every protected request needs a Bearer token in the Authorization header.

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Tokens expire after 30 days. The expires_in field is in seconds.

Error Codes

CodeMeaningCommon cause
200OKRequest succeeded
400Bad RequestValidation error — see field messages in body
401UnauthorizedMissing, expired or invalid Bearer token
429Too Many RequestsRate limit exceeded — back off and retry
500Server ErrorUnexpected failure — contact support

Generate Token

Exchange your client_id and client_secret for a JWT Bearer token required by all other endpoints.

POST /api/generate-token No auth required

Request body

FieldTypeRequiredDescription
grant_typestringrequiredMust be client_credentials
client_idstringrequiredYour API client ID from Developer → Credentials
client_secretstringrequiredYour API client secret from Developer → Credentials

Code examples

cURL Python JavaScript PHP
curl -X POST https://app.kimisms.com/api/generate-token \ -H "Content-Type: application/json" \ -d '{ "grant_type": "client_credentials", "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET" }'
import requests response = requests.post( "https://app.kimisms.com/api/generate-token", json={ "grant_type": "client_credentials", "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET", } ) token = response.json()["access_token"]
const res = await fetch("https://app.kimisms.com/api/generate-token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ grant_type: "client_credentials", client_id: "YOUR_CLIENT_ID", client_secret: "YOUR_CLIENT_SECRET", }), }); const { access_token } = await res.json();
<?php $ch = curl_init("https://app.kimisms.com/api/generate-token"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode([ "grant_type" => "client_credentials", "client_id" => "YOUR_CLIENT_ID", "client_secret" => "YOUR_CLIENT_SECRET", ]), ]); $data = json_decode(curl_exec($ch), true); $token = $data["access_token"];

Responses

{ "token_type": "Bearer", "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expires_in": 2592000 // 30 days in seconds }
{ "credentials": ["Invalid credentials"] }

Send SMS

Send a single SMS to any mobile number. Requires a valid Bearer token and an approved Sender ID on your account.

POST /api/send-sms Bearer token required

Request headers

HeaderRequiredValue
AuthorizationrequiredBearer <access_token>
Content-Typerequiredapplication/json
Idempotency-KeyoptionalUnique UUID — prevents duplicate sends on retry

Request body

FieldTypeRequiredDescription
sender_namestringrequiredApproved Sender ID on your account, e.g. KIMISMS
contactstringrequiredRecipient phone without +, e.g. 254712345678 (9–13 digits)
messagestringrequiredSMS body. 160 chars = 1 unit; longer messages split and billed per part.
callback_urlstringoptionalHTTPS URL that receives a delivery-status webhook

Code examples

cURL Python JavaScript PHP
curl -X POST https://app.kimisms.com/api/send-sms \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \ -d '{ "sender_name": "KIMISMS", "contact": "254712345678", "message": "Hello! Your OTP is 123456. Valid for 5 minutes.", "callback_url": "https://yourapp.com/sms/callback" }'
import requests, uuid headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", "Idempotency-Key": str(uuid.uuid4()), } response = requests.post( "https://app.kimisms.com/api/send-sms", json={ "sender_name": "KIMISMS", "contact": "254712345678", "message": "Hello! Your OTP is 123456.", "callback_url": "https://yourapp.com/callback", }, headers=headers, ) print(response.json())
import { v4 as uuidv4 } from "uuid"; const res = await fetch("https://app.kimisms.com/api/send-sms", { method: "POST", headers: { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", "Idempotency-Key": uuidv4(), }, body: JSON.stringify({ sender_name: "KIMISMS", contact: "254712345678", message: "Hello! Your OTP is 123456.", callback_url: "https://yourapp.com/callback", }), }); const data = await res.json();
<?php $ch = curl_init("https://app.kimisms.com/api/send-sms"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$accessToken}", "Content-Type: application/json", "Idempotency-Key: {$idempotencyKey}", ], CURLOPT_POSTFIELDS => json_encode([ "sender_name" => "KIMISMS", "contact" => "254712345678", "message" => "Hello! Your OTP is 123456.", "callback_url" => "https://yourapp.com/callback", ]), ]); $data = json_decode(curl_exec($ch), true);

Responses

{ "message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "contact": "254712345678", "status": "Pending", "description": "Initiated", "provider": "Swift" }
{ "sender_name": ["Sender ID not found"] }
{ "detail": "Authentication credentials were not provided." }

Idempotency

Pass a unique Idempotency-Key header to safely retry without sending duplicate messages. Keys are cached 24 hours.

Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890

Delivery Callbacks

Provide a callback_url and KIMISMS will POST a delivery status update to it once the message is delivered or fails. Your endpoint must respond with HTTP 200 within 5 seconds.

Callback payload — POST → your callback_url
{ "message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "contact": "254712345678", "status": "Delivered", "description": "Message delivered successfully" }

Ready to integrate?

Create a free account, generate your API keys, and send your first message in under 5 minutes.